なぜあなたは「配列へのポインタ」を学ぶ必要があるのか

かつて(20年くらい前)一緒に仕事をしていた人が、こう言ったことがあります。

はっきり言って、Cプログラマ過半数は、「配列へのポインタ」を理解していないと思う。

まあ「過半数」かどうかはわかりませんが、実際、それなりに長くCを使っている人でも、「配列へのポインタ」を理解していない人はいるように思います。以下、一例。

きくT(2/10 ビッグアップル) on Twitter: "んー、2次元配列を1次元にキャストして関数に渡すことはできるんだけど、1次元配列として受け取った関数側で2次元配列にキャストする方法はあるのか?"
f:id:kmaebashi:20171217185156p:plain

もちろん菊池誠先生は物理学者であり、Cは専門ではないでしょうから、このことをもって菊池先生のことをとやかく言う意図はありません。

それにしても、リプライ中にある以下の記述は、やはりCの初心者の方にとっては「わけがわからない」ものであるかと思います。

Int (*b)[5] = (int(*)[5])a;

int (*b)[5]」なんて、こんなマニアックな書き方使わないよ!

と思う人もいるかもしれません。でも、現に菊池先生が困ったように、これを使う機会は、確実にあるものです。

「配列へのポインタ」を使う典型的なケースは、「多次元配列を関数の引数として渡す時」です。たとえばオセロの盤面をintの2次元配列(int board[8][8];)で表現するとして、これを引数として受け取る関数のプロトタイプは、たとえば以下のようになります。

void func(int (*board)[8]);

いや、そんなマニアックな書き方しなくても、

void func(int board[8][8]);

とか

void func(int board[][8]);

とか書けばよいのでは? と思う人もいるかもしれません。しかし、こうして受け取った引数を別の変数にコピーしようとした場合や、malloc()で動的に多次元配列を確保したい場合は、「配列へのポインタ」を宣言することを避けることはできません。
たとえばオセロの盤面をmalloc()で確保するなら、以下のように書くことになります。

int (*board)[8] = malloc(sizeof(int) * 8 * 8);

で、わざわざこんなの書いたということはもちろん宣伝なのですが、「配列へのポインタ」はもちろん、Cの配列とポインタについて徹底解説した「C言語 ポインタ完全制覇」改訂版発売中ですよ!

ちなみに旧版の画像はこちら。この画像を見せると「あっ、見たことある!」という方も結構いらっしゃるので。
f:id:kmaebashi:20171217195236j:plain