int と配列とポインタ


まずは、int (整数)の場合について、 int a, int a_array[10], int *a_p などの差を眺めていきましょう。サンプルコードはこんな感じ(ソース)。
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 も ポインタも配列も局所領域に確保されていることにも注意しましょう。

まずは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