int main(int argc, char** argv) { int a; int a_array[10]; int *a_p; a = 999; a++; // a_arrray++; a_p = a_array; *a_p = 12; printf("before:: a_array:%x, a_array[0]: (%d, address %x), a_array[1]: (%d, address %x), a_p: %x, &a_p: %x, *a_p: %d\n", a_array, a_array[0], &(a_array[0]), a_array[1], &(a_array[1]), a_p, &a_p, *a_p); a_p++; *a_p = 13; printf("after::: a_array:%x, a_array[0]: (%d, address %x), a_array[1]: (%d, address %x), a_p: %x, &a_p: %x, *a_p: %d\n", a_array, a_array[0], &(a_array[0]), a_array[1], &(a_array[1]), a_p, &a_p, *a_p); return 0; }ちなみに、main の関数フレームの中のイメージ図はこんな感じ。 四角で表されているのが、データを格納する領域です。
まずはint a ですが、基本データ型 int である、名前aの変数がつくられるわけです。 で、 int a_array[10]は、 int 用の領域を 10 個分準備し、その先頭のアドレスについて a_arrayという名前を与えます。 a_array自体は定数(変数じゃない!)で、その値をとることも出来ますが、a_array++とかして値を書き込むのは無理です。
さて、一方で、ポインタ int * a_pについて。 これは、整数データのアドレスを格納するデータの事で、そのための領域を確保しています。 まずは、 a_p = a_arrayで配列の先頭にポインタを合わせているわけです。 その後、*a_pでそのポインタの指す先のデータにアクセスしながら、 a_p++ でポインタの場所を隣に移しています。
さて、ポインタの + 演算というのが何をしているかというと、 a_p + 1 というのは、a_p が整数データの並んでいるアドレスを指しているとして、 その後ろどなりのアドレスを指します。 実際には、整数データのサイズ sizeof(int) = 4の場合、アドレス値は 4 増加しているわけです(他の基本データ型の場合)。 16 進数表示させた時のアドレス数値に気をつけてみましょう。ということで、これが実行例です。
kamada@cygwin% gcc -o data_A data_A.c kamada@cygwin% ./data_A.exe before:: a_array:22fe74, a_array[0]: (12, address 22fe74), a_array[1]: (1628014616, address 22fe78), a_p: 22fe74, &a_p: 22fe70, *a_p: 12 after::: a_array:22fe74, a_array[0]: (12, address 22fe74), a_array[1]: (13, address 22fe78), a_p: 22fe78, &a_p: 22fe70, *a_p: 13
というわけで、 a[X] というのは、 *(a + X) というのと同じことです。
さて、 実際にプログラムをいじってprintf を加えてみるなり、各変数を操作するコードを挟んでみたり、 debugger をあげて、各種データの値を見るなりしてみましょう。
最後に。一般の変数に対しても
a_p = & a;というように、そのアドレスをとることも出来ます。 但し、局所変数のアドレスを外に漏らす場合、それなりの注意が必要です。 というのも局所変数は未来永劫存在するものではありませんから。
2002.10.14/ Tomio KAMADA: kamada@cs.kobe-u.ac.jp