我们知道,数组指针是一个指向数组的指针,但是对该指针取值却会出现一种似乎难以理解的情况。我们首先给出一段代码:
#include<stdio.h>
int main()
{
char b[5][10]={ "hello","xxx","xxx","china"};
char (*p)[10]=&b[2];
printf("p = %p ",p);
printf("*p = %p ",*p);
}
运行上面的代码,我们会发现是:p = 0061FEAE *p = 0061FEAE
这里若按正常理解,p是一个指针,所以*p应该是p指向的对象(字符)。
但是事实并不是如此 *p打印出来的结果竟然是一个地址值。难道数组指针是一个二级指针?
问题又来了,打印的结果是*p的地址值与p的地址值相同。这又不是二级指针呀? 所以理解中哪里出了问题呢?
搞清楚这个问题之前,我们首先应该清楚在c语言中 什么是左值,右值、隐式转换、和数组名的更深层次含义。
左值、右值、隐式转换的概念
左值:在赋值符号的左边,通常代表可写的变量。
右值:在赋值符号的右边,可以理解为它代表一些表达式,特征就是只有在运行的时候才知道,类似“中间结果”。
隐式转换:在c语言中,在一定的条件下,左值可能会隐式转换成右值。
想到数组名,我们首先想到的是数组的首地址,可是他所代表的含义真的有这么简单吗?答案是否定的。
善变的数组名
首先数组名可以认为是一个对象,对象是一个实体,也可以把它看成一个特殊的变量。它代表着整个数组。
但是呢,数组名作为一个左值,他是无法访问的,因为数组名是整个数组的聚焦,访问这个聚焦是没有意义的,但是通过数组名访问其数组的具体的元素是有意义的。
所以诸如a[0]、a[1]等这样的形式作为左值访问是没有问题的。说完了左值,我们来说数组名的右值。数组名在某些条件下会发生隐式转换,左值退化成右值。
比如 :
int a[10]; int *p = a;//由于规定左右两边的类型必须保持一致,数组名a发生由左值退化成右值的隐式转换,此时数组名a代表数组的首地址。
int *p = a;//这里注意区分sizeof(a) sizeof作为一种操作符,这里面的数组名并不是作为右值所使用的,他仍然代表整个数组。(原因是没有左值退化成右值的强制条件)
回到问题本身
为了得到更多的信息,我们将代码作如下的处理:
#include<stdio.h>
int main()
{
char b[5][10]={ "hello","world","xxx","china"};
char (*p)[10]=&b[2];
printf("%p ",p);
printf("%p ",*p);
printf("%d ",sizeof(p));
printf("%d ",sizeof(*p));
}
运行结果:0061FEAE 0061FEAE 4 10
这里 printf(“%d\n”,sizeof§);//这里打印出来是4,说明数组指针确实是一个指针变量,这没有问题。
printf(“%d\n”,sizeof(p));//这里打印出来是10,说明p不是指针变量,排除p是二级指针的可能。
问题是既然p不是指针变量,那为什么p的值偏偏又是地址值呢?
再回到我们上面讨论的数组名问题。等等?? 莫非p是数组名?我再一看p占10个字节,再次印证了我的猜想。
这里我还想说说c语言中二维数组。不管是几维数组,在内存空间中,数组都是一维连续分配存储的,只是我们等效地可以看成二维(行*列)的形式罢了。
而且对于二维数组,我们可以用类似”套娃”的概念来理解。比如题中的字符数组b[5][10],这个二维数组内嵌套5个一维数组,每个一维数组又以b[i]命名。i为0到4这5个整数
在本题中的二维数组中,b[i]作为右值代表的各行代表首地址,而作为左值则代表嵌套在二维数组中的某行的一维数组。
所以,真相大白!*p在这里实则代表的是“局部数组名”b[i]的左值。
值得注意的是在 printf(“%p “,p); 中,由于printf的格式符为%p需要打印出地址,这给p制造了左值退化成右值的强制条件。所以*p作为右值打印出了数组某行的首地址。
最后我们来讨论最后一个终极大问题就是为什么p作为一个数组指针,通过一次解引用(*p)没有理应得到该指针指向的数组对象的值,反而是一个数组名的左值呢?
一开始我也百思不得其解。我们再次回到我上面对关于”数组名”本质的理解。数组名作为一个左值,他是对整个数组的聚焦,对它整体访问是没有意义的。
然后我们再回到数组指针的本质!首先他是一个指针,再然后它指向一个数组。对!!!它的意义是指向一个数组。。。。说明什么?
对于数组指针的第一次解引用,我们得到仍是b[i]整个局部数组的对象,前面说过什么?整体访问没有任何意义。所以真相又大白了,不是编译器出错了,而是数组名实在太善变了!!!
整体访问是没有意义的,所以p又怎么可以访问到数组的元素呢?但是b[i]的左值有意义啊,它本就表示局部数组的对象,然后P又本来指向这个对象,数组名b[i]的左值不给p给谁??
既然对于整体访问没有任何意义,而*p就是数组名b[i]的左值,那**P总能访问到数组的元素了吧?**p实际上不就是 *b[i] = b[i][0]了嘛!
加上printf(“%c\n”,**p);后,打印出的结果正好是字符’x’ 符合猜想。
以上是我参阅陈正冲《c语言深度解剖》后对该问题的个人理解。
第一次写,没什么经验。如有错误或补充,非常欢迎大佬们指出更正。