配列へのポインター

1月14日月曜日、晴れ

いままで真剣に向き合ってこなかったので、配列へのポインターについての理解がたいそう浅かった……。反省。
一次元配列だけしか理解してなかったね。そしてまだニュービーだから多次元配列へのポインターの宣言とか苦しくて仕方ないね。

* * *

基本。

int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};

array 2 of array 3 of int.
matrix は2要素の配列で、各要素は3要素の配列で、その要素は int オブジェクト。
メモリーレイアウト上は int オブジェクトが6つ、 1, 2, 3, 4, 5, 6 の順で並ぶ。

matrix の先頭要素を指すポインターは次のように宣言する。

int (*p)[3] = matrix;

p as pointer to array 3 of int.
3つの int 要素からなる配列へのポインター。

ここで p は {1, 2, 3} を指していて、 p + 1 は {4, 5, 6} を指す。
(ポインター p を1つ進めると sizeof(int) * 3 バイト先のアドレスをさしてくれる!)

じゃあアドレス位置で p と p + 1 に等しい2要素にもつ配列は?

int (*row[2])[3] = {matrix + 0, matrix + 1};

array 2 of pointer to array 3 of int.

では、この row の先頭要素を指すポインターは?

int (**it)[3] = row;

pointer to pointer to array 3 of int.

つないで使ってみる。

int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*row[2])[3] = {matrix + 0, matrix + 1};
int (**it)[3] = row;
while (it != row + 2) {
  int (*pr)[3] = *it;
  printf("%p: %d %d %d\n", *it, (*pr)[0], (*pr)[1], (*pr)[2]);
  it += 1;
}

自動変数のアドレスは実行環境によって変わるから人によって結果は変わるだろうけれど、こんな感じの出力が得られる。

0x7ffee4b24a90: 1 2 3
0x7ffee4b24a9c: 4 5 6

(C には参照型変数がないので **it を格納する変数は定義できない)

* * *

多次元配列オブジェクトそのものへのポインターも宣言できる。

int (*pmat)[2][3] = &matrix;

これ、オブジェクト全体のサイズを覚えているところが素晴らしい。
つまり sizeof(*pmat) は sizeof(int) * 2 * 3 と等しくなる。

なにが嬉しいかって、次のような宣言で多次元配列を全体をカバーするメモリーを切りだせる。

int (*parray2d)[2][3] = malloc(sizeof(*parray2d));

* * *

これも今日見つけたのだけれど、難解なポインターまわりの変数宣言を解読してくれるサイト。

(cdelc というと「C 呼出規約」と読みかえてしまう人もいそうだけど)

この記事が気に入ったらサポートをしてみませんか?