配列の配列

C言語で配列とポインタの理解は第一関門だと思う。私が、C言語のスキルを試すときによくやる質問は次のような物だ。

以下の違いを説明せよ。
1)int a[2][3];
2)int a[3][2];
3)int *a[3];
4)int (*a)[3];
5)int **a;

普通にCを書ける人でも、これに満足に答えられる人は余りいない。上から目線の言い方になるが、これがちゃんと説明できる人は自信を持ってイイと思う。

C言語に多次元配列は無い

int a[N][M];
a[i][j] = 2;

こんな書き方ができるのだから、「論理的には多次元配列が扱える」という立場もあるが、それだと上記1)〜5)の違いを理解するのに邪魔になる。C言語で扱える配列はあくまでも1次元であり、配列の配列という形式で多次元配列のように見せている、というのが実際である。

#include <stdio.h>

#define ARRAY_NUM_OF(array)	((sizeof(array)) / (sizeof(array[0])))

int main(void)
{
	int a[2][3];
	int i, j;

	printf("int a[2][3] (array[2] of array[3] of int\n");
	printf("sizeof(a) = %u, sizeof(a[0]) = %u\n", sizeof(a), sizeof(a[0]));
	for (i = 0; i < ARRAY_NUM_OF(a); i++) {
		printf("   a[%d] is @ %p\n", i, &a[i]);
	}
		
	printf("====================== \n");
	
	for (i = 0; i < ARRAY_NUM_OF(a); i++) {
		for (j = 0; j < ARRAY_NUM_OF(a[i]); j++) {
			printf("a[%d][%d] is @ %p\n", i, j, &a[i][j]);
		}
	}
	
	return 0;
}

実行結果

$ ./main
int a[2][3] (array[2] of array[3] of int
sizeof(a) = 24, sizeof(a[0]) = 12
   a[0] is @ 0x22cd40
   a[1] is @ 0x22cd4c
======================
a[0][0] is @ 0x22cd40
a[0][1] is @ 0x22cd44
a[0][2] is @ 0x22cd48
a[1][0] is @ 0x22cd4c
a[1][1] is @ 0x22cd50
a[1][2] is @ 0x22cd54

上記の結果からメモリマップを書いてみると、以下のようにメモリ上に並んでいることがわかる。

決して2×3というようなイメージではなく、一直線上にint[3]が2つ並んでいるのである。

これがわかれば1)と2)の違いは自ずとわかる。2)はint[2]が3つ並んでいる。