The behavior you found is actually a big wart in the C language. Whenever you declare a function that takes an array parameter, the compiler ignores you and changes the parameter to a pointer. So these declarations all behave like the first one:
void func(int *a)
void func(int a[])
void func(int a
typedef int array_plz[5];
void func(array_plz a)
a will be a pointer to int in all four cases. If you pass an array to func, it will immediately decay into a pointer to its first element. (On a 64-bit system, a 64-bit pointer is twice as large as a 32-bit int, so your sizeof ratio returns 2.)
The only purpose of this rule is to maintain backwards compatibility with historical compilers that did not support passing aggregate values as function arguments.
This does not mean that it’s impossible to pass an array to a function. You can get around this wart by embedding the array into a struct (this is basically the purpose of C++11’s 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 */
}
or by passing a pointer to the array:
void func(const int (*a)[5])
{
printf("%zd\n", sizeof(*a)/sizeof((*a)[0])); /* prints 5 */
}
In case the array size isn’t a compile-time constant, you can use the pointer-to-array technique with C99 variable-length arrays:
void func(int n, const int (*a)[n])
{
printf("%zd\n", sizeof(*a)/sizeof((*a)[0])); /* prints n */
}