I vettori e le matrici sono passate sempre tramite puntatore per motivi strani (questioni di compatibilità addirittura con il B, mi pare).
Lo standard infatti dice in proposito (§6.7.5.3 ¶7):
A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’, where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation.
ovvero: una dichiarazione del tipo:
codice:
void funzione(int arr[10]) // arr è un array di 10 int
di fatto viene letta come
codice:
void funzione(int * arr) // arr è un puntatore ad int
e quando viene richiamata l'array decade in un puntatore al suo primo elemento.
Analogamente
codice:
void funzione(int arr[10][10]) // arr è un array di 10 array di 10 int
diventa
codice:
void funzione(int (* arr)[10]) // arr è un puntatore ad un array di 10 int
dove quella sintassi con le parentesi identifica un puntatore ad un array di 10 elementi (quando passi un array multidimensionale di fatto viene passato un puntatore alla sua prima riga).
In ogni caso, se vuoi passare gli array per valore li devi incapsulare in una struct.