Pointers
ポインタも変数の一種であり、Cプログラミング言語において非常に重要な役割を果たします。ポインタは、以下のような様々な目的で使用されます。
- 文字列
- 動的メモリ割り当て
- 関数の引数を参照渡しする
- 複雑なデータ構造を構築する
- 関数へのポインタ
- 特殊なデータ構造(ツリー、トライなど)を構築する
その他多数。
ポインタとは何ですか?
ポインタは本質的には、実際の値自体を保持するのではなく、値を指す メモリ アドレス を保持する単純な整数変数です。
コンピュータのメモリはデータを格納するための連続的な場所であり、ポインタはそのメモリの特定の部分を指し示します。プログラムでは、ポインタを広範囲のメモリ領域を指すように使用することができます。その範囲は、メモリ上の特定の一点からどれだけの量のデータを読み込むかによって決まります。
ポインタとしての文字列
文字列については既に説明しましたが、ここではもう少し深く掘り下げて、C における文字列が実際に何であるかを理解します (C++ と混在する場合、他の文字列と区別するために C 文字列と呼ばれます)。
以下を見てください:
char * name = "John";
この行は3つの処理を行います。
nameというローカル(スタック)変数をメモリに割り当てます。メモリアドレスを格納する箱となるもので、1文字へのポインタとなります。- 文字列 "John" をプログラムメモリのどこかに出現させます(もちろん、コンパイルおよび実行後)。
name引数を初期化し、J文字のアドレスを格納します。メモリ内の文字列の残りの部分がそれに続きます。
name 変数に配列としてアクセスしようとすると、それはうまくいって、文字 J のASCIIコード値を返します。これは、name 変数が実際には文字列の先頭を正確に指しているためです。
メモリは連続的であることがわかっているため、メモリ内で次の文字に進むと、文字列の末尾 (ASCIIコード値が 0 の文字、\0 と表記) に達するまで、文字列内の次の文字を受け取ると想定できます。
デリファレンス(逆参照)
デリファレンスとは、メモリアドレスを参照する代わりに、ポインタが指している場所に格納されている値を参照する動作です。実はもう、配列でデリファレンスを使用していたのですが、あのときはまだ、そのことを知らなかったのです。例えば、添字演算子 [0] は配列の最初の要素にアクセスします。配列は実際にはポインタなので、配列の最初の要素にアクセスすることは、すなわちポインタをデリファレンスすることと、同じ動作になります。ポインタをデリファレンスするには、アスタリスク演算子 * を使用します。
スタック内の別の変数を指す配列を作成する場合は、次のコードを記述します。
/* ローカル変数 a を定義する */
int a = 1;
/* ポインタ変数を定義し、&演算子を使用して、aを指す */
int * pointer_to_a = &a;
printf("The value a is %d\n", a);
printf("The value of a is also %d\n", *pointer_to_a);
先ほど作成した変数 a のアドレスを得るために & 演算子を使用していることに注意してください。
次に、アスタリスク演算子を使って、アドレスから逆に、そこに格納されている値を参照しました。値を読むだけでなく、逆参照(デリファレンス)した変数の内容を変更することもできます。
int a = 1;
int * pointer_to_a = &a;
/* 変数 a を変更してみましょう */
a += 1;
/* 変数をもう一度変更しました! */
*pointer_to_a += 1;
/* 3 と表示されます */
printf("The value of a is now %d\n", a);
Exercise
pointer_to_n というローカル変数 n へのポインタを作成し、それを使用して n の値を 1 増やします。