Multidimensional arrays follow the same rules as single-dimensional arrays when passing them to a function. However the combination of decay-to-pointer, operator precedence, and the two different ways to declare a multidimensional array (array of arrays vs array of pointers) may make the declaration of such functions nonintuitive. The following example shows the correct ways to pass multidimensional arrays.
                      
                      #include <assert.h>
                      
                      #include <stdlib.h>
                      
                      /* When passing a multidimensional array (i.e. an array of arrays) to a   function, it decays into a pointer to the first element as usual.  But only   the top level decays, so what is passed is a pointer to an array of some fixed   size (4 in this case). */
                      
                      void f(int x[][4])
                      
                      {    
                      
                      assert(sizeof(*x) == sizeof(int) * 4);
                      
                      }
                      
                      /* This prototype is equivalent to f(int x[][4]).   The parentheses around *x are required because [index] has a higher   precedence than *expr, thus int *x[4] would normally be equivalent to int *(x[4]), i.e. an array of 4 pointers to int.  But if it's declared as a   function parameter, it decays into a pointer and becomes int **x,   which is not compatable with x[2][4]. */
                      
                      void g(int (*x)[4])
                      
                      {    
                      
                      assert(sizeof(*x) == sizeof(int) * 4);
                      
                      }
                      
                      /* An array of pointers may be passed to this, since it'll decay into a pointer   to pointer, but an array of arrays may not. */
                      
                      void h(int **x)
                      
                      {    
                      
                      assert(sizeof(*x) == sizeof(int*));
                      
                      }
                      
                      int main(void)
                      
                      {    
                      
                      int foo[2][4];    
                      
                      f(foo);    
                      
                      g(foo);
                      
                          /* Here we're dynamically creating an array of pointers.  Note that the       size of each dimension is not part of the datatype, and so the type       system just treats it as a pointer to pointer, not a pointer to array       or array of arrays. */    
                      
                      int **bar = malloc(sizeof(*bar) * 2);    
                      
                      assert(bar);    
                      
                      for (size_t i = 0; i < 2; i++)
                      
                      {        
                      
                      bar[i] = malloc(sizeof(*bar[i]) * 4);        
                      
                      assert(bar[i]);    
                      
                      }
                      
                      h(bar);       
                      
                      for (size_t i = 0; i < 2; i++)
                      
                      {        
                      
                      free(bar[i]);    
                      
                      }    
                      
                      free(bar);
                      
                      }