質問: int a[3][3] のポインタの引数は関数に与えるとき、&a[3][3]でいいですか。今まで1次元配列しかつかってこなかったので、で関数で使うときは a[2][2]とかでいいんでしょうか。 たまにのっているa[0]とはなんでしょうか。よく分からないので教えてください。 (使ったことがないので、被害はありません)
回答: Cでは、二次元の配列は 一次元の配列 の配列です。 例えば、b[3][2]という配列をA番地から割り付けたとするなら(intが4byteを占める処理系としたとき) * b[3][2]の配列 (A+0x00)番地~ ただし、sizeof(b)は24 * b[0], b[1], b[2]という3つの[int型要素2個の配列)]が含まれている、 (A+0x00)番地~b[0] (A+0x08)番地~b[1] (A+0x10)番地~b[2] ただし、sizeof(b[0])は8 * b[0][0], b[0][1], b[1][0], b[1][1], b[2][0], b[2][1] のint型が含まれている (A+0x00)番地~b[0][0] (A+0x04)番地~b[0][1] (A+0x08)番地~b[1][0] (A+0x0c)番地~b[1][1] (A+0x10)番地~b[2][0] (A+0x14)番地~b[2][1] という格納がされているわけです。 以下、Cでは配列が単独で記述されたときには配列の先頭要素へのポインタと解釈される(sizeofあるいは&演算子のオペランドになったときは例外)、というのは前提知識として... 配列を関数の引数とするときは、とりあえず仮引数の型にはその配列をそのまま記述すれば成立はします(ただし配列の要素数は記述不要、記述しても無視される)。 あるいは、配列の先頭要素へのポインタ型を記述します。 * 2次元配列int [3][2]を受け取りたいのなら 仮引数(関数の宣言)はfunc(int arg[][2]) あるいは func(int (*arg)[2]) //後者はint2要素の配列へのポインタ型arg これに対し、実引数(関数の呼び出し時)はそのまま配列を渡せばOK func(b) で呼び出します。つまり、bはその先頭要素b[0]へのポインタとして解釈され、int(*)[2]型の値です。 (余計なことを言えば、func(&b[0])でもOK。先述の例外のひとつ、&演算子で&b[0]はb[0]自身のアドレスを求める) * int[2]を受け取りたいのなら、 仮引数はfunc(int arg[]) あるいは func(int *arg) これに対し、実引数はint 2要素の配列を渡すので、 func(b[0])あるいはfunc(b[1]), func(b[2]) で呼び出します。つまり、b[0]とかb[1],b[2]はその先頭要素b[n][0]へのポインタとして解釈され、int*型の値です。 * intへのポインタを受け取りたいのなら 仮引数はfunc(int *arg) これに対し、実引数はintへのポインタを渡すので func(&b[0][0])あるいはfunc(&b[0][1]), ... func(&b[2][0]) で呼び出します。 等と記述することになります。 ここで、前述のように、bの格納されている番地は、b[0]の格納されている番地でもあり、b[0][0]の格納されている番地(いずれもA+0x00)でもあります。Cは(暗黙で変換出来てしまうなら)型の一致を厳密にはチェックせず、受け取った仮引数の型として解釈しますから、 func(int arg[][2])と宣言した関数に対して func(&b[0][0])と呼び出しを行っても(あるいはfunc(b[0])でも) アドレスは同じなので動作してしまう、ということになります。