配列のポインター(C言語)

「C言語の配列には<長さ>がないからなー」
つい先日までそう思っていました。

* * *

int array[16];

のように宣言した「配列」の array。この size は

sizeof array

という式で取れて、 64 と取り出せます。(語長が 4 バイトに翻訳される環境において)

要素のサイズは

sizeof array[0]

で取り出せるので、

sizeof array / sizeof array[0]

で配列の長さは計算できます。面倒なので

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

とマクロで定義することもあるくらい有名。

* * *

けれど配列を「関数」に渡すと、とたんに面倒なことになる。

void f(int array[16]) {
  printf("%lu\n", sizeof(array));
}

この関数を呼び出すと

sizeof (int *)

の値を表示します。(64bit 環境だと 8)

void f(int array[16]);

は、実際のところ

void f(int *array);

と同じ。関数に配列を渡すつもりでも、この宣言で関数が実際に受け取るのはその配列の先頭要素へのポインター。

これを持って、僕は「配列」には長さ情報がない、などと思い込んでいたのです。

* * *

前置きが長かった。ところで配列からも「ポインター」を取り出せます。

int array[16];
int (*pArray)[16] = &array;

このとき pArray は int サイズの要素を16持つ配列を参照するポインター。そして * を付すことで参照を外して配列に戻せる。

sizeof *pArray / sizeof (*pArray)[0]

は 16 になります。

だから

void g(int (*array)[16]) {
  printf("%lu\n", sizeof *array / sizeof **array);
}
...
int array[16];
g(&array); // prints "16"

のようにすると、ちゃんと配列を、その「サイズ」ごとまるっと関数に渡せるようになり、だからその長さも求められるようになる。

* * *

配列のポインターを利用する、もうひとつ便利な方法。

int (*array)[16] = malloc(sizeof *array);

配列ポインターの参照をはずして sizeof を取ると、全体のサイズがわかるので必要なメモリーを単純かつ明快にきりだせる。

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