配列とポインタ #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";
としたときの動作は未定義だ。プログラムが停止する処理系もある。