The folks above have given the technical explanation.
The reason behind is is that C and C++ treat arrays as blocks of memory. When an array gets passed to a function, all that gets sent is a pointer to the array. This is in stark contrast to Ada, Pascal, and Fortran that send descriptors of the array to the function.
Your declaration must provide enough information to work with the array with just a pointer passed. That means is need all but the last (first specified) array dimension.
Multidimensional arrays of variable size are usually a sign of bad design. If you have a fixed array size (4x4 is common in 3D transforms), 2D array work out well. For something like general purpose matrix operations, 2D arrays do not work well.
For that you need to define a class that:
1) Manages a 1D array
2) Provides the mechanism for translating 2D references into 1D index.
What you would do is something like
class Matrix
{
unsigned int range1 ;
unsigned int range2
double *values ; // Could use a template
double &valueAt (unsigned int x, unsigned int y) { return values [range1 * x + y] ; }
} ;