C/C++指针数组中(*p)[2]与*(p[2])相等原因及延伸问题
C/C++ 指针数组下标运算相关疑问
在如下C/C++代码中:
int main() { int a[3] = {11, 22, 33}; int *p[3]; p[0] = &a[0]; p[1] = &a[1]; p[2] = &a[2]; printf("(*p)[2] = %d\n",(*p)[2]); printf("*(p[2]) = %d\n",*(p[2])); return 0; }
运行结果显示(*p)[2] = *(p[2]) = a[2]。
已知前提:
int *p[3];定义了包含3个int型指针的数组- 代码中后续三行赋值逻辑分别将
a[0]、a[1]、a[2]的地址存入p[0]、p[1]、p[2]中 *(p[2])的含义是取出p[2]存储地址指向的变量,因此*(p[2]) = a[2]符合预期
待解答疑问:
- 应如何理解
(*p)[2]的运算逻辑,为什么它的结果也等于a[2]? - 若将p定义为指向int类型
std::vector的指针数组,(*p)[2] = *(p[2])的相等关系是否仍然成立?
问题解答
1. (*p)[2]的运算逻辑
这个结果相等本质是代码内存布局导致的巧合,不是语法上的等价关系,拆解运算步骤就能明白:
- 第一步:数组名在表达式中会自动退化为指向数组首元素的指针。
p是int*类型的数组,因此单独出现的p会退化为指向p[0]的指针(类型为int**),对其解引用*p得到的就是数组第一个元素p[0],也就是之前存入的&a[0](类型为int*,指向a数组的首元素)。 - 第二步:C/C++中下标运算的本质是指针偏移后解引用,规则为
x[y]完全等价于*(x + y)。因此(*p)[2]等价于*(*p + 2),也就是从a[0]的地址开始,向后偏移2个int类型的长度,得到a[2]的地址,解引用后就拿到了a[2]的值。
之所以和*(p[2])结果一致,是因为代码恰好把连续存储的a数组的元素地址,按顺序存在了p数组的连续空间里:p[0]存a[0]地址,偏移2个int刚好落在a[2]的位置,和p[2]里存的a[2]地址指向同一块内存,值才会相等。如果p数组里存的指针不是指向连续内存的同数组元素,这个相等关系立刻就不成立。
2. 指向std::vector的指针数组场景下相等关系不成立
这个场景下两个表达式不仅值不相等,连类型、语义都完全不同,甚至大概率触发程序崩溃:
- 首先看语义差异:如果p定义为
std::vector<int>* p[3],即元素为vector<int>指针的数组:(*p)[2]等价于p[0][2],语义是取p[0]指向的那个vector对象中下标为2的元素,返回值是int类型,前提是p[0]指向的vector长度至少为3,否则属于越界访问,触发未定义行为。*(p[2])的语义是解引用p[2]存储的指针,拿到p[2]指向的整个vector<int>对象,返回值是vector<int>类型,和前者的int类型根本不匹配,连赋值都做不到,更谈不上相等。
- 哪怕你是想写
(*p[2])[2](取p[2]指向vector的下标2元素),和(*p)[2]也没有必然相等关系:前者访问的是第三个指针指向的vector的第3个元素,后者访问的是第一个指针指向的vector的第3个元素,除非手动把p[0]、p[1]、p[2]都指向同一个长度足够的vector对象,否则值不可能相等。
内容的提问来源于stack exchange,提问作者Vic
相关产品推荐
相关产品推荐

