二维数组名作形参

之前帮同学调一个程序的时候遇到的,把二维数据改为全局变量,不通过参数传递就没问题了,否则程序崩溃。

细究一下,二维数据名用于形参时需要注意哪些方面。

测试程序如下:

#include<stdio.h>
#include<stdlib.h>

void print1(int **a, int m, int n);
void print2(int (*a)[2], int m, int n);

int main()
{
  int a[2][2]={1,2,3,4};
  print1((int**)a,2,2);
  print2(a,2,2);
  system("pause");
  return 0;
}

void print1(int **a, int m, int n)
{
  for(int i=0;i<m;++i)
    for(int j=0;j<n;++j)
    {    
      printf("%d\n",*(a+i*n+j));
      //printf("%d\n",a[i][j]); 
    }
}

void print2(int (*a)[2], int m, int n)
{
  for(int i=0;i<m;++i)
    for(int j=0;j<n;++j)
    {
      printf("%d\n",a[i][j]);        
    }     
}
程序可以正常运行,而且没问题。有问题的是print1中注释掉的语句。可能很多人会觉得二维数组的数组名不就是一个二维指针吗,为什么用a[i][j]的方式访问不行?注释掉的语句在执行时,程序崩溃。

原因是这样的,二维数组的数组名其实是一个int (*)[N]的指针,和 int** 指针还是不一样的。直观来看,对前者一次解引用的话,得到的是数组;对后者一次解引用,得到的是指针。其实在调用的时候就可以发现,如果你不进行强制类型转换,而直接进行这样的调用 print1(a,2,2); 会提示如下错误:cannot convert `int (*)[2]' to `int**' for argument `1' to `void print1(int**, int, int)' 。编译器已经告诉你,类型不匹配了;这个时候你应该想到的是用第二种方式来定义函数。而有的人可能觉得既然类型不匹配,就给直接强制类型转换了,结果编译通过了,但程序崩溃了,更找不出问题。

产生这个问题的根本原因是,对数组名和指针的细微差别没有认识到。

数组名,在访问元素时,是直接取址的过程。比如a[i],它是直接在a的地址上进行一个 i (乘以a[i]的类型大小)的偏移。(每个符号的地址在编译时可知

指针,访问元素时,是一个间接寻址的过程。比如对int *a; 在a[i]取元素时,首先它会在变量a的地址上取数,取得的值作为地址再去寻址,然后再进行一个 i (乘以类型大小)的偏移。

这样的话,对上面测试代码而言,在main函数中,你声明的a是数组名,即int (*)[2]类型,传递到print2中,在print2函数中用a[i][j]访问时,是先对形参a进行一个间接寻址,然后直接寻址。而传递到print1中,首先你需要强制转换成int ** 才可以,在print1中用a[i][j]访问时,是对形参a进行间接寻址后,再间接寻址,这样就发生了错误。而在print1中用*(a+i*n+j)的方式则是可以的。

这个和下面这类错误很类似。就是在一个文件中定义了int a[100]; 然后你想在另一文件中使用它,你用了外部声明,但是你声明成了这样: extern int * a; 这样,在访问a数组的元素时,本来应该是按照数组名的直接寻址方式,在这个文件中却会用指针的间接寻址,就会出现不可预知的错误。

也有参考这里

关于指针和数组名在什么情况下不同,以后有时间再总结下。

阅读更多
个人分类: C/C++
上一篇后端JSP文件里Java语言如何操作Oracle Spatial提供的JGeometry对象2
下一篇new/delete 和 malloc/free 的区别
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭