あなたが見つけた振る舞いは、実際にはC言語の大きないぼです。配列パラメーターを取る関数を宣言すると、コンパイラーはそれを無視し、パラメーターをポインターに変更します。したがって、これらの宣言はすべて最初の宣言のように動作します。
void func(int *a)
void func(int a[])
void func(int a
typedef int array_plz[5];
void func(array_plz a)
aは、4つのケースすべてでintへのポインタになります。配列をfuncに渡すと、すぐに減衰して最初の要素へのポインタになります。(64ビットシステムでは、64ビットポインターは32ビットintの2倍であるため、sizeof比率は2を返します。)
このルールの唯一の目的は、関数の引数として集計値を渡すことをサポートしていなかった従来のコンパイラとの下位互換性を維持することです。
これは、配列を関数に渡すことが不可能であることを意味しません。配列を構造体に埋め込むことにより、このいぼを回避できます(これは基本的にC ++ 11のstd :: arrayの目的です):
struct array_rly {
int a[5];
};
void func(struct array_rly a)
{
printf("%zd\n", sizeof(a.a)/sizeof(a.a[0])); /* prints 5 */
}
または、配列へのポインタを渡すことによって:
void func(const int (*a)[5])
{
printf("%zd\n", sizeof(*a)/sizeof((*a)[0])); /* prints 5 */
}
配列サイズがコンパイル時の定数でない場合は、C99可変長配列で配列へのポインター手法を使用できます。
void func(int n, const int (*a)[n])
{
printf("%zd\n", sizeof(*a)/sizeof((*a)[0])); /* prints n */
}