配列とポインタ #3

なんか、ネタを小出しにしている感があるなぁ。まともな Web 環境にもどったしとりあえず、昨日のネタの簡単な解説をば。(C FAQ 1 見とけという話も)
型情報もまとめて出力すると、結果は下記のようになる。

       a: value = 0xbffff62c, size =  200, type = int [5][10]
   a + 0: value = 0xbffff62c, size =    4, type = int (*) [10]
      &a: value = 0xbffff62c, size =    4, type = int (*) [5][10]
      *a: value = 0xbffff62c, size =   40, type = int [10]
    a[0]: value = 0xbffff62c, size =   40, type = int [10]
   &a[0]: value = 0xbffff62c, size =    4, type = int (*) [10]
&a[0][0]: value = 0xbffff62c, size =    4, type = int*

まぁ、全部同じ結果になるわけだけど、ポイントは3つ。

  • 配列は要素数も含めて型を形成し、ポインタとは区別される
  • 配列へのポインタの dereference は「アドレスの指す先を参照」するわけではなく、そのまま「アドレス」を返す
  • 同様に配列の referenece は「配列の先頭のアドレス」を返す

で、int [10] は int * に暗黙に変換されるんだけども、int [5][10] は int ** には変換できないわけだ(ポインタの指す先の型が違うから)。だから

b = a;

としたければ b は

int (*b)[10];

と「要素数10のintの配列へのポインタ」、として宣言しなければならない。ちなみに、当然ながら

(void *) (a + i) - (void *) a == sizeof(int [10]) * i

な感じになる。
ついでに GCC なら

void foo(int);

とあったとき、

foo == &foo

が成り立ったりする。

あと、文字列リテラルにより初期化した場合。

char *s = "abc";
char s[] = "abc";

それぞれ、文字列が格納される領域が異なる。後者は自動変数の領域(一般的にはスタック)に長さ4の配列が確保され、その内容が "abc\0" で初期化されるが、前者は静的領域の "abc\0" という文字列の格納アドレスを指すことになる。ついでに、

char *s = "abc";
s[0] = "d";

としたときの動作は未定義だ。プログラムが停止する処理系もある。