2008年10月28日火曜日

C言語の話#1 仮引数での配列添字演算子はシンタックスシュガー

久しぶりの更新で,C言語の話.最近までちゃんと知らなかったので,備忘録的にエントリにしてみた.

配列を引数にとる関数の定義は,次のようなかんじになると思う.
void f(int a[])
{
/* 処理 */
}

また,次のようにかいても問題ない.
void f(int *a)
{
/* 処理 */
}

というか,前者は後者のシンタックスシュガーでしかない.
int a[8];
a = NULL;

は,コンパイルエラーになるが,
void f(int a[]) {
a = NULL;
}

は,コンパイルエラーにならないことから理解できる.

となると,次のような2次元配列の場合が気になる.
void f(int a[][8])
{
/* 処理 */
}

これは,次の記述と同じ意味になる.
void f(int (*a)[8])
{
/* 処理 */
}

予想と違ったという人も結構いると思う.int *a[8]ではなくint (*a)[8]なのだ.ちなみに,前者は,int型へのポインタの配列で,後者は,int型の配列へのポインタだ.

さて,なんでこんなことになってんだろう.これについては,次のプログラムを見てほしい.
int a[8];
f(a);

何ら変哲のない,配列を関数に渡すプログラムだ.しかし,このとき関数に渡されるのは,本当に配列だろうか?実は,アドレスでしかない.配列として確保された領域の先頭アドレスだ.(知っていると思うけど,aと&a[0]が同じ.)つまり,配列を渡しているというわけではないのだ.

そう考えると,次のような2次元配列のときの説明も容易だ.
int a[4][8];
f(a);

渡しているのは,2次元配列の先頭アドレスである.(aとa[0]と&a[0][0]が同じ.)しかし,2次元配列であるので,列の要素数を知らなければ,2次元配列としてアクセスができないのだ.(列の要素数がわからないと,次の行がどの番地から始まるのか計算できない.)もちろん,2次元以上の多次元配列にも同様のことがいえる.

ということで,こんな風にまとめたわけだけど,間違ってそうで怖いなwC言語に超詳しい人ってざらにいるしw間違ってたら教えてください(*- -)(*_ _)ペコリ

参考文献:
B.W.カーニハン,D.M.リッチー.プログラミング言語C 第2版, 共立出版, 1989.
平林雅英.新ANSI C言語辞典,技術評論社,1997.

0 件のコメント: