C
C++

配列"への"ポインタ・参照

More than 3 years have passed since last update.

ある特定の要素数の(静的)一次元配列のみを引数に取る関数を作りたいとする。

今回は例として、int型で10個の要素を持つ配列のみを引数にとるvoid型関数fooを作ってみる。

(多次元配列では下の論理が必ずしも通用しないらしいので今回は論じないことにする)


とりあえず素朴に

最初はこう考えてみる。

void foo(int a[10]){

a[0] = 0; //アクセス例
// 処理
}

使うならこんな感じ。

int b[10];

foo(b);

でもこれはうまくいかない。次のようなコードも問題なくコンパイルを通る。

int c[25]; //要素数過多

foo(c); //コンパイルは素通り


敗因

配列名をコード内に書くと、配列の最初の要素へのポインタになる。

つまり配列int a[]について、

a == &a[0]

aの型はint*型に過ぎず、要素数についての情報は持っていない。


配列"への"ポインタ

ちょっと変えてみる。

void foo(int (*a)[10]){

*a[0] = 0; //アクセス例
// 処理
}

引数はint *a[10]ではなくint (*a)[10]であることに注意。

使うならこんな感じ。

int b[10];

foo(&b);

引数にアドレス演算子&をつけることに注意。

これなら次のような問題のある引数は弾ける。

int c[25]; //要素数過多

foo(&c); //コンパイルエラー


勝因

配列名に&を付けると、"配列そのもの"へのポインタになる。

これは型情報として要素数を持っている。

たとえば、int a[10]へのポインタはint (*)[10]型である。

要素数が一致しないと型は一致せず、コンパイルエラーを引き起こしてくれる。


参照でも書ける

C++使いの方なら参照で書きたい、ということもあるだろう。

ポインタで書いたものを少し変えるだけでよい。

void foo(int (&a)[10]){

a[0] = 0;
// 処理
}

使うならこんな感じ。

int b[10];

foo(b);

参照の利点である「変数宣言以外に特別な記述が要らない」ことも生きている。

もちろん要素数の違う配列は弾いてくれる。


テンプレートとのコンビ

要素数が型情報に含まれるということは、テンプレートの処理で要素数が取り出せる、ということだ。

たとえば次のような静的配列の要素数を返す関数。

(constexprはC++11以降でないと通らないので注意)

template <typename T,size_t size>

constexpr size_t array_size(T (&)[size]){
return size;
}

(constexprが使えるなら)これはCでよく見る次のマクロの完全な代替になる。

#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))

(std::arrayを使えば、とは言わないでほしい)