配列を指すポインタ
さて三度、お題を再掲する。
以下の違いを説明せよ。 1)int a[2][3]; 2)int a[3][2]; 3)int *a[3]; 4)int (*a)[3]; 5)int **a;
1)〜3)まではすでに述べた。今回は4) int (*a)[3];を取り上げる。
このときのaの型はint[3]を指すポインタとなる。1)〜3)は配列だったが、4)〜5)はポインタである。
重要なので囲んで強調する。
int *a[3]; →aはintを指すポインタの配列[3] int (*p)[3]; →pはintの配列[3]を指すポインタ
「こんな宣言使ったこと無い!」と思うかもしれないが、実はこのpと同じ型をそれと意識せずに使っている。
1)で使った、配列aを式の中で単にaと書いた場合の型が、実は4)と同じなのだ。
宣言 int a[2][3];があった場合 式の中でaと書いた場合の型は &a[0] → int (*)[3] 式の中でa[0]と書いた場合の型は &((a[0])[0])→ int * 式の中でa[0][0]と書いた場合の型は int
4)を使ったサンプルコード
#include <stdio.h> #include <stdlib.h> #define ARRAY_NUM_OF(array) ((sizeof(array)) / (sizeof(array[0]))) int main(void) { int (*p)[3]; int i, j; p = malloc(sizeof(int) * 2 * 3); printf("int (*p)[3] (pointer to array[3] of int)\n"); printf("sizeof(p) = %u, sizeof(p[0]) = %u\n", sizeof(p), sizeof(p[0])); for (i = 0; i < 2; i++) { printf(" p[%d] is @ %p\n", i, &p[i]); } printf("====================== \n"); for (i = 0; i < 2; i++) { for (j = 0; j < ARRAY_NUM_OF(p[i]); j++) { printf("p[%d][%d] is @ %p\n", i, j, &p[i][j]); } } free(p); return 0; }
実行結果
$ ./main int (*p)[3] (pointer to array[3] of int) sizeof(p) = 4, sizeof(p[0]) = 12 p[0] is @ 0x6d1f18 p[1] is @ 0x6d1f24 ====================== p[0][0] is @ 0x6d1f18 p[0][1] is @ 0x6d1f1c p[0][2] is @ 0x6d1f20 p[1][0] is @ 0x6d1f24 p[1][1] is @ 0x6d1f28 p[1][2] is @ 0x6d1f2c
サンプルコードのポイント
- ポインタなので、有効なバッファを用意してそこを指す必要がある。
- mallocでsizeof(int)*2*3=24バイトの領域を用意して、pで指す。
- int a[2][3];と同じように使用できる。
- http://d.hatena.ne.jp/Einherjar/20100513/p2はスタック、今回はヒープと確保した領域は異なるが、メモリ上の配置は全く同じである。
- 配列のように使えても、pはあくまでもポインタである。サンプルコードに配列の要素数を求めるマクロがある。このマクロには配列しか渡すことができない。よってpは渡せない。p[i]は配列なので要素数を求めることができる。