配列を指すポインタ

さて三度、お題を再掲する。

以下の違いを説明せよ。
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]は配列なので要素数を求めることができる。