先上代码:
int a[2][3]={1,2,3,4,5,6};//2行3列的int型数组
int **pp=a;//编译出错,不能用二级指针直接指向二维数组
int (*p)[3]=a;//对,p是指向一维数组的指针,可以指向二维数组
int *p1=a[0];//可以,p1也是一维指针,可以指向二维数组
可以将上述的代码自己上机试试。
按理说一维数组对应一维指针,即类似于int a[3]; int *p=a;
二维数组应该也对应于二级指针才对啊,但是其实不然。
要想解释清楚,先想想二级指针的定义,
定义:
- 二级指针是指向一级指针的指针,一级指针是取一次地址,二级指针是取两次地址。这个没有异议吧。 这个可以扩展成:n级指针是指向n-1级指针的指针,n级指针是取n次地址。
要回答题目的答案,我觉得可以有两个解释。
解释a:不需要用二级指针
我们想想,对二维数组应该取多少次地址呢,答案是一次。
为什么呢,有人会问二维数组不是对应着两次吗?
给出原因:
因为二维数组是连续存放的。取了一次地址之后,所有元素的地址就全部知道了,只需要指针的移动就可以遍历所有的元素了,不需要再取一次地址了。
像上面代码中的二维数组a,你只要知道了a,就可以知道a[0]的地址,(因为a=&a[0]),通过指针的移动也就是知道a[i][j]
的地址了。
举个栗子,也就是上面的指向一维数组的指针p,
int a[2][3]={1,2,3,4,5,6};
int (*p)[3]=a;
//p指向了a,就是指向了a[0]为数组名的一维数组,数组元素
//为1、2、3,p的移动,实现了a[0]、a[1]、a[2]这三个一维数组之间移动。
for(int i=0;i<2;i++)
for(int j=0;j<3;j++)
{
printf("%d ",p[i][j]);//可以看到,一级指针p可以实现对二维数组的遍历
}
栗子2:二维数组实际上可以看出两个一维数组合并成一个一维数组,
所以像一开始的指针p1,int *p1=a[0];
指针p1=a[0]就是p1=&a[0][0],因为这六个元素都是连续存放的,所以p1的移动也就是实现了对这个长度为6的“一维数组”,即对二维数组a[2][3]的遍历。
解释b:不可以
解释a说明了不需要用二级指针来指向二维数组,那是不是说明需要的时候是可以这么用的? 不是。
根本就不可以这么用。
先说说上面的代码二级指针pp的出错的问题:
int **pp=a;//编译出错,不能用二级指针直接指向二维数组
之所以会编译出错,是因为
int **pp=a;//
//等价于
int **pp=&a[0];//因为a=&a[0],所以变量pp里面放着的内容就是a[0]的地址
//又进一步等价于
int **pp=&a[0][0];//因为&a[0][0]的值和&a[0]的值一样,都是数字1的地址
//所以最终推得 pp装的就是a[0][0]的地址。
所以pp=&a[0][0],即*pp=1;
那么你想想还可以对*pp
再取一次地址吗,现在*pp
里面放的是数字1,再加一个*
号的话,**pp访问的就是物理地址为1的内容了。这是不允许的。所以会出错。
这里所用的知识也还是解释a里面的东西。
还有一点,二级指针是指向一级指针的,那么二级指针pp每次移动的大小就是sizeof(int *)也就是4个字节,所以pp+1不是像二维数组a+1那样移动到下一行首地址,而是移动四个字节。关于指针移动的差别也进一步说明了不可以用二级指针指向二维数组。
那有人会提问了,int **pp=a;
不行,那int **pp=&a;
总可以了吧。
答案还是不行。
原因也还是上文讲的移动的大小不一致导致的。
下面说说什么时候可以用
像
char *str[2]={"hello","world"};
char **pp;
pp=str;//这种情况是可以的。
本质原因是str[2]为一维数组,但是它的两个元素都是char*型的,都是地址。所以,*pp=str[0]
,str[0]不是像上面的那样是个定值1,这里的str[0]是一个地址,是字符串“hello”的首地址,所以还要再取一次地址才能访问到字符串“hello”的每个字符。这就是需要两次取地址了。这时候用二级指针就没有问题了。二级指针不就是取两次地址嘛。
况且这里指针的移动也没有问题,char*本来就是四个字节的。