みんな大好き,ポインタ/配列/参照 ~多次元配列まとめ~

汎整数型(論理型(bool),文字型(charなど),整数型(intやlongなど))と浮動小数点型を合わせて算術型といい,算術型とvoid型を合わせて基本型といいます。基本型からは「ポインタ型」「配列型」「参照型」の3種類の組込み型が得られますが,この記事ではその中でも特に(単なる基本型へのポインタ型,配列型,参照型を除いた)2重以上のポインタ型,配列型,参照型の扱い方について説明します。constについては説明しません。templateについては説明しません。変数の型だけを見るので,変数名は全てハンガリアン記法の接頭辞のみとします。

まずCにあるポインタ型と配列型から先に説明します。

int i;

iはint型です。関数hoge(int)を宣言しておくと,hoge(i)でhoge(int)が呼び出されます。

intに*,[1],[2]を付けるとそれぞれ次の型が得られます。

int *pi, a1i[1], a2i[2];

piはint*型,a1iはint[1]型,a2iはint[2]型です。関数呼び出しではこれらは全て同じものとして扱われます。つまり関数hoge(int*)を宣言しておくと,hoge(pi),hoge(a1i),hoge(a2i)の全てにおいてhoge(int*)が呼び出されるということです。

int*に*,[1],[2]を付けるとそれぞれ次の型が得られます。

int **ppi, *a1pi[1], *a2pi[2];

ppiはint**型,a1piはint*[1]型,a2piはint*[2]型です。関数呼び出しではこれらは全て同じものとして扱われます。つまり関数hoge(int**)を宣言しておくと,hoge(ppi),hoge(a1pi),hoge(a2pi)の全てにおいてhoge(int**)が呼び出されるということです。

int[1]に*,[1],[2]を付けるとそれぞれ次の型が得られます。

int (*pa1i)[1], a1a1i[1][1], a2a1i[2][1];

pa1iはint(*)[1]型,a1a1iはint[1][1]型,a2a1iはint[2][1]型です。関数呼び出しではこれらは全て同じものとして扱われます。つまり関数hoge(int(*)[1])を宣言しておくと,hoge(pa1i),hoge(a1a1i),hoge(a2a1i)の全てにおいてhoge(int(*)[1])が呼び出されるということです。hoge(int**)に渡したいときはキャストします。

int[2]に*,[1],[2]を付けるとそれぞれ次の型が得られます。

int (*pa2i)[2], a1a2i[1][2], a2a2i[2][2];

pa2iはint(*)[2]型,a1a2iはint[1][2]型,a2a2iはint[2][2]型です。関数呼び出しではこれらは全て同じものとして扱われます。つまり関数hoge(int(*)[2])を宣言しておくと,hoge(pa2i),hoge(a1a2i),hoge(a2a2i)の全てにおいてhoge(int(*)[2])が呼び出されるということです。

変数Aの型がTであるとき,&AはT*型です。即ちint(**[2])[3]型変数a3ppa2iのアドレス&a3ppa2iはint(**(*)[2])[3]型です。

変数Bの型がT*であるとき,*BはT型です。即ちint(*(**)[4])[2]型変数a2pa4ppiの参照*a2pa4ppiはint(*(*)[4])[2]型です。

変数を配列型にキャストすることはできません。reinterpret_castとかそういう問題ではなく,言語仕様上絶対に不可能です。

int a0i[0]はint[0]型です。

sizeof iが4,sizeof piが8であった場合,sizeof a1iは4×1=4,sizeof a2iは4×2=8,sizeof ppiは8,sizeof a1piは8×1=8,sizeof a2piは8×2=16,sizeof pa1iは8,sizeof a1a1iは4×1×1=4,sizeof a2a1iは4×1×2=8,sizeof pa2iは8,sizeof a1a2iは4×2×1=8,sizeof a2a2iは4×2×2=16です。配列+整数のsizeof,即ちsizeof(a2i+1)やsizeof (a2a1i+0)などは全てsizeof pi(=8)に等しくなります。sizeof *a2a1iは*a2a1iがint[1]型なのでsizeof a1i(=4)に等しくなります。sizeof *(a2a1i+0)も同じくsizeof a1iに等しくなります。a2a1i[0]は*(a2a1i+0)の糖衣構文なので,sizeof a2a1i[0]も同じくsizeof a1iに等しくなります。

C++にはポインタ,配列に加え,参照というものがあります。

関数hoge_cpp(int&)をhoge_cpp(i)で,関数hoge_cpp(int&&)をhoge_cpp(i+0)で,関数hoge_cpp(int*&)をhoge_cpp(pi)で,関数hoge_cpp(int*&&)をhoge_cpp(pi+0)やhoge_cpp(a1i)やhoge_cpp(a1i+0)で,関数hoge_cpp(int(&)[1])をhoge_cpp(a1i)で,関数hoge_cpp(int(&)[2])をhoge_cpp(a2i)で呼び出せます。hoge_cpp(a1i)でhoge_cpp(int(&)[2])を呼び出すことはできないので,テンプレートを使って配列の大きさごとに関数を作れば(使うたびに関数が増えますが)関数内で配列の大きさを知ることができます。

とりあえずこの記事はここまでです。部分的に情報密度が高くなってしまいましたが,その一部でもあなたの役に立てば幸いです。