【C/C++】 2次元配列の引数渡し
今日、ふと悩んだ2次元配列を関数引数で渡す方法。
やりたいのはつまり...
static void sub_func( ☆ )
{
/* ここで2次元配列A[5][3]の各要素をA[x][y]のように読み書きする */
}
void func( void )
{
int A[5][3];
sub_func( A );
}
という事。
問題となるのは「☆」の部分をどのようにすれば良いのか?
いくつか方法を思いつきますね。
1. static void sub_func( int* a )
これはあくまで2次元配列の先頭アドレスをポインタ変数として受け取る形。この形式で渡すと2次元配列を格納したメモリにアクセスする事はできるけど、2次元配列である情報が渡らない。つまりA[x][y]のような形式でアクセスできない。
よく見かけるコートは、この方法でアドレスを渡して、その後中の要素番号を計算しつつアクセスする方法。でもそれじゃコンパイラの恩恵は得られないし、第一エレガントじゃない。
2. static void sub_func( int** a )
これは間違い。コンパイルできない。Aはあくまで[5][3]要素を持った2次元配列の名前であってポインタポインタで指定できるかどうか知ったこっちゃない。というかポインタポインタではない。
3. static void sub_func( int a[] )
当然これも間違い。引数は1次元配列を期待している。
4. static void sub_func( int* a[] )
一見よさげに見えるけど(実は最初こう記述してしまった)これも誤り。これだとint型のポインタ変数の配列が渡される事を期待する。2次元配列はポインタ配列では管理されていない。
5. static void sub_func( int a[5][3] )
これが正解。要素数が固定であれば、直感的にも分かりやすい。注意点としてC言語ではスカラ型データであれば値渡し(値がコピーされる)になるけど、配列の場合は無条件で先頭アドレスの参照渡しになる事。ちなみに最初の次元数は省略できる。つまり
static void sub_func( int a[][3] )
という記述も許される。2次元配列の場合2次元目の数が分かれば1次元目のインクリメントが何バイト単位かが計算できるから。
6. static void sub_func( int (*a)[] )
実はこれも正解。4との違いは括弧の位置。これが正解である事を認識するのにちょっと手間取った。
6番目が正解な理由は1次元配列を考えてみると簡単。
static void sub_func( int a[] )
static void sub_func( int *a )
この2つは呼び出し側としては等価になる。C言語の規約では配列を指定した場合には無条件で参照渡しになるから。ちなみに正確に言うと受け側での意味は異なる。前者は配列を受け取ったが、後者は配列が格納されている先頭アドレスをポインタ変数で受け取っている。これは「a = 数値」といった代入文が前者では記述できず、後者で記述できる事から分かる。後者の場合、スタック上に確保されたaというポインタ変数に呼び出し側配列が記録されているメモリの先頭アドレスが記録されるから。通常は受け側で同じ使い方をするなら同じコードが出力されるけど、コンパイラによっては前者の方が効率が良いかもね。
ちょっと脱線した。
さて、上記の2行を見れば分かるとおり、C言語の呼び出し側にとって「a[]」と「*a」は等価になる。つまり先の2次元配列「int a[][3]」は「int (*a)[3]」と等価であると言える。
では上記3がなぜダメなのか。もうお分かりでしょう。「int *a[3]」と「int (*a)[3]」は異なる。前者は「int型のポインタ変数が3つからなる配列」であるのに対して後者は「int型3つからなる配列の配列の先頭アドレスを格納したポインタ」(配列が2重になっているのはタイプミスではないですよ)という事。
やっぱりC言語のポインタは奥が深い...
« (帰国報告)個人輸入品一覧 | トップページ | Panasonic「ビストロ(NE-R3400)」レシピ総選挙 »
「C/C++」カテゴリの記事
- 【C/C++】 2次元配列の引数渡し(2011.08.03)
- 【C++】 VisualStudio 2010 でのsnprintf関数(2010.11.16)
- 【C/C++】 streamクラスのeofメンバ(2009.12.12)
- 【C/C++】 マクロと関数呼び出し(2009.08.07)
- 【C/C++】 演算子のオーバーロード(2009.04.27)