O comportamento que você encontrou é, na verdade, uma grande verruga na linguagem C. Sempre que você declara uma função que recebe um parâmetro de matriz, o compilador o ignora e altera o parâmetro para um ponteiro. Portanto, todas essas declarações se comportam como a primeira:
void func(int *a)
void func(int a[])
void func(int a
typedef int array_plz[5];
void func(array_plz a)
a será um ponteiro para int em todos os quatro casos. Se você passar um array para func, ele imediatamente se tornará um ponteiro para seu primeiro elemento. (Em um sistema de 64 bits, um ponteiro de 64 bits é duas vezes maior que um int de 32 bits, portanto, seu tamanho de proporção retorna 2.)
O único propósito desta regra é manter a compatibilidade retroativa com compiladores históricos que não suportavam a passagem de valores agregados como argumentos de função.
Isso não significa que seja impossível passar um array para uma função. Você pode contornar essa verruga incorporando a matriz em uma estrutura (essa é basicamente a finalidade de std :: array do C ++ 11):
struct array_rly {
int a[5];
};
void func(struct array_rly a)
{
printf("%zd\n", sizeof(a.a)/sizeof(a.a[0])); /* prints 5 */
}
ou passando um ponteiro para a matriz:
void func(const int (*a)[5])
{
printf("%zd\n", sizeof(*a)/sizeof((*a)[0])); /* prints 5 */
}
Caso o tamanho do array não seja uma constante de tempo de compilação, você pode usar a técnica de ponteiro para array com arrays de comprimento variável C99:
void func(int n, const int (*a)[n])
{
printf("%zd\n", sizeof(*a)/sizeof((*a)[0])); /* prints n */
}