C语言指针、数组偏移与结构体运算代码疑问求解
C语言指针相关代码问题解答
待分析完整代码如下:
#include <stdio.h> #include <stdlib.h> struct trace { char *sign; int *values; struct trace *pN; }; int main() { int decimal[] = {4,2,1}; char text[]="Word-2!"; struct trace *pV; pV = (struct trace*) calloc(2, sizeof(struct trace)); pV->pN = pV; pV->values = decimal + decimal[2]; // = 2 (*pV).sign = text + *decimal; // text + 4 对应字符串"-2!" *(pV + 1) = pV[0]; // 即pV[1] = pV[0] = *pV ++pV[1].values; ++*pV[1].values; printf("%d %s\n", *pV->values, pV->sign); printf("%d %s\n",*pV->pN[1].values, pV->pN[1].sign); return 0; }
疑问1:decimal + decimal[2]运算逻辑与数组名语义
- 单独作为表达式使用的数组名
decimal,语义是指向数组首元素的常量指针,类型为int*,存储的地址就是decimal[0]的内存地址。 decimal[2]是下标访问,对应数组第三个元素的值,也就是1。- C语言指针加法规则:
类型* 指针 + 整数n,返回指针向后偏移n个对应类型长度后的新地址,指向原位置后第n个同类型元素。因此decimal + decimal[2]等价于decimal + 1,指向数组下标为1的元素decimal[1],该地址存储的值为2。 - 你的猜测完全正确:C语言中下标访问本质是指针偏移的语法糖,
arr[i]与*(arr + i)完全等价,因此decimal + decimal[2]就是&decimal[1],解引用后拿到的值就是decimal[1] = 2。
疑问2:++pV[1].values;执行逻辑与作用对象
- 先明确C语言运算符优先级:数组下标访问
[]、结构体成员访问.优先级相同,遵循左结合规则,优先级高于前置自增运算符++。该语句的运算顺序为:- 计算
pV[1],拿到calloc分配的第二个trace结构体实例 - 读取该结构体的
values成员,这是一个int*类型的指针变量 - 前置
++直接作用在这个int*指针上,将指针向后偏移1个int类型长度:初始时pV[1].values和pV[0].values值相同,都指向decimal[1],执行完该语句后,pV[1].values修改为指向decimal[2]。
- 计算
- 与
pV[++1].values的核心区别:- 作用对象完全不同:原语句的
++修改的是结构体的values指针成员的指向;pV[++1].values中++的作用目标是下标位置的数值。 pV[++1].values本身是非法C语法:前置++要求操作数是可修改的左值,字面量1是右值,无法被修改,编译阶段就会报错。如果将下标位置替换为可修改变量(如int i=0; pV[++i].values),逻辑是先自增下标变量,再取对应下标的结构体成员,和原语句修改指针指向的逻辑没有关联。
- 作用对象完全不同:原语句的
- 注意:
pV[1] = pV[0]是结构体值拷贝,两个结构体的values是独立的指针变量,仅初始值相同,修改pV[1].values的指向不会影响pV[0].values。
疑问3:++*pV[1].values;执行逻辑
按优先级拆解运算顺序(.优先级最高,其次是解引用*,最后是前置++):
- 先取
pV[1].values:经过上一行代码修改,该指针当前指向decimal[2],也就是存储值为1的int元素地址 - 对指针做解引用操作
*,直接拿到decimal[2]这个int变量本身 - 前置
++作用在这个int变量上,将decimal[2]的值从1自增为2。
最终输出结果成因
逐行梳理到printf语句前的内存状态:
- 初始状态:
decimal数组元素为{4,2,1},text字符串内存布局为'W','o','r','d','-','2','!','\0' - calloc分配2个连续的
trace结构体,首地址赋值给pV,初始内存全为0 pV->pN = pV:第一个结构体pV[0]的pN指针指向自身,也就是pV数组首地址pV->values = decimal + decimal[2]:decimal[2]值为1,因此pV[0].values指向decimal[1],对应存储值为2(*pV).sign = text + *decimal:*decimal是decimal[0]值为4,因此pV[0].sign指向text下标为4的位置,也就是字符串"-2!"的首地址*(pV + 1) = pV[0]:将pV[0]的所有成员值拷贝给pV[1],此时pV[1]的三个成员初始值和pV[0]完全一致:sign指向"-2!",values指向decimal[1],pN指向pV首地址++pV[1].values:将pV[1]的values指针向后偏移1个int长度,指向decimal[2],pV[0].values不受影响,仍然指向decimal[1]++*pV[1].values:将decimal[2]的值从1自增为2,此时decimal数组变为{4,2,2}
两个printf的输出逻辑:
- 第一个printf:
*pV->values取pV[0].values指向的值,也就是decimal[1] = 2;pV->sign是pV[0].sign指向的"-2!",因此第一行输出2 -2! - 第二个printf:
pV->pN等于pV也就是数组首地址,pV->pN[1]就是pV[1];*pV->pN[1].values取pV[1].values指向的值,也就是自增后的decimal[2] = 2;pV->pN[1].sign是pV[1].sign指向的"-2!",因此第二行也输出2 -2!,和预期结果完全一致。
内容的提问来源于stack exchange,提问作者Sylenthio
相关产品推荐
相关产品推荐

