二维数组传参 基本形式 二维数组在栈上分配,各行地址空间连续 
定义的时候,拥有两种形式 
第一种是指明行数和列数
1 int  array [3 ][3 ] = {{1 ,2 ,3 },{4 ,5 ,6 },{7 ,8 ,9 }};
第二种是不指明第一维,而指明第二维
1 int  array [][3 ] = {{1 ,2 ,3 },{4 ,5 ,6 },{7 ,8 ,9 }};
而在子函数声明的时候,有三种方式 
一种是指明形参的行数和列数
1 2 3 4 5 6 7 8 9 10 int  func (int  array [][3 ], int  m, int  n)   {    int  i,j;     for (i=0 ;i<m;i++) {         for (j=0 ;j<n;j++)                          printf ("\t%d" , array [i][j]);         printf ("\n" );     }     return  0 ; } 
第二种是不指明形参的第一维,而指明第二维
1 2 3 4 5 6 7 8 9 10 int  func (int  array [][3 ], int  m, int  n)   {    int  i,j;     for (i=0 ;i<m;i++) {         for (j=0 ;j<n;j++)                          printf ("\t%d" , array [i][j]);         printf ("\n" );     }     return  0 ; } 
第三种是形参使用指针的形式,
1 2 3 4 5 6 7 8 9 10 int  func (int  (*array )[3 ], int  m, int  n)   {    int  i,j;     for (i=0 ;i<m;i++) {         for (j=0 ;j<n;j++)                          printf ("\t%d" , array [i][j]);         printf ("\n" );     }     return  0 ; } 
而定义的两种形式与调用的三种形式是可以互相混合使用,因此基本形式一共有2x3=6中方法
运行结果均为:
这种分配是在栈上进行的,能够保证所有元素的地址空间连续。这样,array[i][j] 和 *(*(array +i) +j)是一样的,程序是知道array+i的i实际上偏移了i*N个单位, 这也导致了在二维数组array[3][3]中,使用下标array[2][1]和array[1][4]是访问的同一个元素,尽管后者的下标对于一个3*3矩阵来说是非法的,但这并不影响访问。
这种形式,无论是数组定义还是函数都不够泛用,两个维度在编译前就定好了,唯一可以做的就是把维度M、N声明为宏或者枚举类型,但这仍不能避免每次修改后都要重新编译。
注意: 在主函数中调用子函数的方法与在子函数中访问数组的方法
在主函数中调用子函数 
在子函数中使用形参数组有两种方式 :array[i][j] 和 *(*(array +i) +j)
数组传参形式 二维数组在栈上分配,各行地址空间连续,函数参数使用指针形式 
当把这种二维数组的指针直接作为参数传递时,数组名退化为指针,函数并不知道数组的列数,N对它来说是不可见的,即使使用*(*(array +i) +j),第一层解引用失败。这时,编译器会报warning,运行生成的文件会发生segment fault。那么,为了指导这个函数如何解引用,也就是人为地解引用,需要把这个二维数组的首元素地址 传给函数,于是就变成了下面的形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include  <stdio.h>  #include  <stdlib.h>  #include  <assert.h>  int  func (int  *array , int  m, int  n)   {    int  i,j;     for (i=0 ;i<m;i++) {         for (j=0 ;j<n;j++)             printf ("\t%d" , *(array  +i*n +j));         printf ("\n" );     }     return  0 ; } int  main (int  argc,char ** argv)   {    int  m=3 ,n=3 ,i;     int  array [][3 ] = {{1 ,2 ,3 },{4 ,5 ,6 },{7 ,8 ,9 }};     func(*array ,m,n);     return  0 ; } 
上面代码如果将主函数调用子函数语句改为func(array,m,n);不可行,则会报错error: cannot convert 'int (*)[3]' to 'int*' for argument '1' to 'int func(int*, int, int)'
当然也可以写成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int  fun (int  *array ,int  m,int  n)   {    int  i,j;     for (i=0 ;i<m;i++)         for (j=0 ;j<n;j++)             printf ("%d " , *((int *)array  + n*i + j));     return  0 ; } int  main ()   {    int  array [3 ][3 ] = {         {1 ,2 ,3 },         {4 ,5 ,6 },         {7 ,8 ,9 }     };     fun((int  *)array ,3 ,3 );     return  0 ; } 
但是意图没有上一种清晰,并不推荐。
你可能会问,为什么下面的不行?原因其实和上面提到的一样,第一次解引用时,函数并不知道数组的列数,从而导致失败。准确的说,是因为数组实际类型是int [3][3],在作为右值时可以被转化为int (*)[3],它们都和int **不同,自然不可用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int  func (int  **array , int  m, int  n)   {    ...     printf ("\t%d" , *(*array  +i*n +j));     ... } int  main ()   {  int  array [3 ][3 ] = {         {1 ,2 ,3 },         {4 ,5 ,6 },         {7 ,8 ,9 }     };     ...      func(array ,3 ,3 );   ...  } 
动态数组形式 第2种虽然函数参数的限定降低了,但仍需要在栈上预先分配一定大小的二维数组,程序整体并不是完全的泛用。为了进一步提高泛用性,把二维数组空间的分配也动态化,使用malloc()在堆上分配空间:
1 2 3 4 int  **array ;array  = (int  **)malloc (m *sizeof (int  *));for (i=0 ;i<m;i++)    array [i] = (int  *)malloc (n *sizeof (int )); 
画出该动态数组的内存分布如下图所示,其中array与array[i]因为使用到了malloc,因此需要被free.
这时,在分配空间的作用域里,对0<i<m,0<j<n,array[i][j]的访问完全没有问题。那么,对应地,函数写作
1 2 3 4 5 int  func (int  **array ,int  m,int  n)   {    ...     printf ("%d " , *(*(array +i)+j));     ... } 
值得注意的是,虽然malloc()每次分配的空间在地址上是连续的,但是多次malloc()分配的空间之间并不一定是连续的 ,这与在栈上分配的二维矩阵有着根本的不同,对于二维数组array[3][3],不能再用array[1][4]来访问array[2][1]了,前者地址越界。而且如果malloc申请的空间太多了 堆上可能就容纳不下了 到头来还是得考虑用栈实现分配。
折中形式 用堆上分配的一维数组表示二维数组,函数参数使用指针形式
用一维数组来实现二维数组,是一种折中方案,但是很好理解,也不易出错。这样分配的数组空间是连续的。使用时需要把两维下标转化为一维下标。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include  <stdio.h>  #include  <stdlib.h>  #include  <assert.h>  int  func (int  *array , int  m, int  n)   {    int  i,j;     for (i=0 ;i<m;i++) {         for (j=0 ;j<n;j++)             printf ("\t%d" ,*(array +i*n+j));         printf ("\n" );     }     return  0 ; } int  main (int  argc,char ** argv)   {    int  m,n,i;     int  *array ;     assert(argc == 3 );     m = atoi(argv[1 ]);     n = atoi(argv[2 ]);     array  = (int *)malloc (m*n*sizeof (int ));     for (i=0 ;i<m*n;i++)         array [i] = i;     func(array ,m,n);     return  0 ; } 
较新的编译器 用栈上分配的直到执行时才确定大小的二维数组
C90不支持这种形式,C99支持,因此一些较新的编译器可以对下面的代码进行执行。注意print()的参数顺序不能改变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void  print (int  x, int  y, int  a[x][y])  {    printf ("\n" );     int  i, j;     for (i = 0 ; i < x; i++){         for (j = 0 ; j < y; j++)             printf ("%d     " , a[i][j]);         printf ("\n" );     } } void  init_2d (int  *a, int  x, int  y)  {    int  i, j;     for (i = 0 ; i < x; i++){         for (j = 0 ; j < y; j++){             a[i*y + j] = i + j;         }         printf ("\n" );     } } int  main ()  {    int  m , n ;     scanf ("%d %d" ,&m,&n);     int  a[m][n];       init_2d(a, m, n);     print(m, n, a); } 
另外,这种分配方式仍然是在栈上,相关讨论可见于http://bbs.csdn.net/topics/90350681。 
小结 其实所谓的二维数组,在K&R上只是指预先分配好大小的形如int a[M][M]这样的数组,它存在于栈上;而实际使用的在堆空间利用malloc动态分配空间的并不是这种,只是用的人多了,把后者叫成二维数组了(我不认为把后者也称为二维数组是标准的说法)。再加上我们经常用它来处理矩阵,”标准的”二维数组、”动态的”二维数组、矩阵这三个概念就混在了一起。矩阵是可以用这两种二维数组表示的,而对于这两种不同的二维数组,函数传参的方式也不完全相同,不能随意混用。
C99对于多维数组的描述: If E is an n -dimensional array ( n ≥ 2) with dimensions i × j × … × k , then E (used as other than an lvalue) is converted to a pointer to an ( n − 1)-dimensional array with dimensions j × … × k . If the unary * operator is applied to this pointer explicitly, or implicitly as a result of subscripting, the result is the pointed-to ( n − 1)-dimensional array which itself is converted into a pointer if used as other than an lvalue. It follows from this that arrays are stored in row-major order (last subscript varies fastest).
栈上分配的二维数组数组名int array[3][3]的真实类型是int [ ][ ],在作为右值时才被转化为(int *array)[N] (感谢 garbageMan指出),和int **是不同的。把前者进行强制转换为后者,在函数中对元素操作也会导致段错误。而这样就能解释为什么int array[3][3]既可以用f(int array[3][3]) 来传参,也可以用f(int (*array)[3]) 来传参了下面用图来说明二者区别:
固定矩阵的大小,可以省略二维矩阵的第一维
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include  <stdio.h>  #include  <stdlib.h>  #include  <assert.h>  int  func (int  *array , int  m, int  n)   {    int  i,j;     for (i=0 ;i<m;i++) {         for (j=0 ;j<n;j++)             printf ("\t%d" , *(array  +i*n +j));         printf ("\n" );     }     return  0 ; } int  main (int  argc,char ** argv)   {    int  m=3 ,n=3 ,i;     int  array [][3 ] = {{1 ,2 ,3 },{4 ,5 ,6 },{7 ,8 ,9 }};               func(*array ,m,n);     return  0 ; } 
运行结果为:
动态二维数组的声明与赋初值 当从键盘读取一维与二维的长度时,可以动态二维数组分配内存,但是a[][]不行,会报错。
 C++中有三种方法来动态申请多维数组
C中的malloc/free C++中的new/delete STL容器中的vector malloc/free 动态开辟一维数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void  dynamicCreate1Array ()  {    int  m;     int  i;     int  *p;          printf ("请输入开辟的数组长度:" );     scanf ("%d" ,&m);     p = (int *)malloc (sizeof (int )*m);       printf ("请输入数据:" );     for (i = 0 ; i < m ; i++)         scanf ("%d" ,&p[i]);       printf ("输出数据:\n" );     for (i = 0 ; i < m; i++)         printf ("%d " ,p[i]);     free (p); } 
动态开辟二维数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include  <stdio.h> //包含输入输出函数  #include  <stdlib.h>//包含动态内存分配函数。  int  main ()  {    int  **p;      int  m, n;     int  i,j;     scanf ("%d%d" , &m, &n);      if (m <= 0  || n <= 0 ) return  -1 ;      p = (int  **) malloc (sizeof (int  *) *m);     for (i = 0 ; i < m; i ++)         p[i] = (int  *)malloc (sizeof (int ) * n);      for (i = 0 ; i < m; i ++)         for (j = 0 ; j < n; j ++)             scanf ("%d" ,&p[i][j]);     printf ("输入的数组为%d行%d列:\n" , m, n);     for (i = 0 ; i < m; i ++)     {         for (j = 0 ; j < n; j ++)             printf ("%d " , p[i][j]);         printf ("\n" );     }          for (i = 0 ; i < m; i ++)         free (p[i]);     free (p);     return  0 ; } 
记住要使用free
new/delete 动态开辟一维数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void  DynamicCreate1Array ()  {    int  len;          cout <<"请输入长度:" ;     cin >>len;          int  *p = new  int [len];          cout <<"请输入数据:" ;     for (int  i = 0 ; i < len; i++)         cin >>p[i];          cout <<"输出数据:" <<endl ;     for (i = 0 ; i < len; i++)         cout <<setw(4 )<<p[i];          delete [] p; } 
动态开辟二维数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 void  DynamicCreate2Array ()  {    int  m,n;     cout <<"请输入行和列:" ;     cin >>m>>n;            int  **p = new  int *[m];      for (int  i = 0 ; i < m; i++)         p[i] = new  int [n];        cout <<"请输入数据:" ;     for (i = 0  ; i < m ; i++)         for (int  j = 0 ; j < n; j++)             cin >>p[i][j];       cout <<"输出数据:" <<endl ;     for (i = 0 ; i < m; i++)     {         for (int  j = 0 ; j < n; j++)             cout <<setw(3 )<<p[i][j];         cout <<endl ;     }            for (i = 0 ; i < m; i++)         delete [] p[i];     delete [] p;   } 
STL中的vector 动态开辟二维数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 void  VectorCreate ()  {    int  m,n;     cout <<"请输入行和列:" ;     cin >>m>>n;               vector <vector <int > > p(m,vector <int >(n));          cout <<"请输入数据:" ;     for (int  i = 0  ; i < m ; i++)         for (int  j = 0 ; j < n; j++)             cin >>p[i][j];              cout <<"输出数据:" <<endl ;     for (i = 0 ; i < m; i++)     {         for (int  j = 0 ; j < n; j++)             cout <<setw(3 )<<p[i][j];         cout <<endl ;     }              } 
参考 C++中动态申请二维数组并释放方法