C言語の多重配列について。我の場合は仕事でも多重配列を使う必要があるようなアルゴリズミックなプログラムは書いてないので(そのかわりけっこう複雑な構造体を好んで使う)、書いとかないと忘れるんだよな。
何が言いたいのかというと、a[10][20]
のような多重配列をつくるときに、どちらからループを回すのが正しいか、というのが迷うのだ。普通に考えれば(a[10])[20]
だから、a[10]
が20個ある配列で、10から回すのが正しいと言えるだろう。でもそうだったっけ?? と思うのだ。逆じゃなかったかな。20個の配列が10個ある、ようなレイアウトにされてしまったような気がする。毎回のようにテストプログラムを書いて調べることになるのでメモしておく。
実際のメモリレイアウトは以下のようになる。
a[0][0] ... ptr a[0][1] ... ptr+1 : a[1][0] ... ptr+1*20 a[1][1] ... ptr+1*20+1 : a[2][0] ... ptr+2*20 a[2][0] ... ptr+2*20+1
以下のように宣言にカッコをつけてもレイアウトに変更はない。
int (a[10])[20];
だから、多重配列は常に後ろの添字、20のループから回すのが正しい。
ここで興味深いのは、a
と&a
と*a
を評価するとみな同じ値になるのである。なんでこうなるのか、理由がわかるかね、ふふふ。
補足すると、このような理論なので、関数の引数にする場合は先頭の添字のサイズは省略できるが後ろのほうは省略できない。引数を使わなければエラーは吐かない。また、配列のサイズを指定したところでアドレス計算だけに使うため、境界チェックなどやろうともしないという、C言語らしさを見せている。コンパイルオプションで配列の添字チェックをつけるくらいのやさしい心遣いがあってもいいと思う。願う。まあ、コンパイラにつけちゃうとpurifyとか作ってる人が困るとか、あるのかな。でもCに境界チェックがないというのは、欠陥ソフトが量産される背景にもなってるんじゃないかと思う。自分でもそういうソフト書いちゃうしね。わかっていながらあえて書くこともある。
int testarray(int arg[10][20]){ return arg[1][2]; }
... OKint testarray(int arg[][20]){ return arg[1][2]; }
... OKint testarray(int arg[10][]){ return arg[1][2]; }
... BADint testarray(int arg[10][20]){ return arg[100][200]; }
... C言語的にはOK