Except when it is the operand of the sizeof, _Alignof, or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T" and the value of the expression will be the address of the first element of the array.
When you pass an array expression as a function argument, what the function actually receives is a pointer value:
void foo( T *a ) // equivalent to T a[] and T a[N]
{
  ...
}
int main( void )
{
  T arr[N];
  ...
  foo( arr );
  ...
}
As a "convenience" C allows you to use array notation for a function parameter declaration, but be aware it will be "adjusted" to be a pointer declaration because the parameter is a pointer value, not an array - T a[N] and T a[] will be interpreted as T *a.  Note that this is only true in a function parameter declaration, not a regular variable declaration.  
Now, just for giggles, replace T with the array type double [M].  So instead of being an N-element array of T, arr is now an N-element array of M-element arrays of double:
int main( void )
{
  double arr[N][M];
  ...
  foo( arr );
  ...
}
In the call to foo, the expression arr "decays" from type "N-element array of M-element arrays of double" to "pointer to M-element array of double", or double (*)[M].  So our declaration of foo looks like this:
void foo( double (*a)[M] ) // equivalent to double a[][M] and double a[N][M]
{
  ...
}
And this is why the two seemingly different declarations are equivalent - in the context of a function parameter declaration, double a[][M] is interpreted as double (*a)[M].  Again, this is only true for function parameter declarations, not regular variable declarations.