☰
*p++ 改变的是指针,还是指针指向的值?
2026/10/30 5:00:11 网站建设 项目流程

*p++ 改变的是指针,还是指针指向的值?

先看一段最小代码:

int values[] = {10, 20, 30}; int *p = values; int result = *p++;

执行后,三个结果分别是:result为 10,p指向values[1],数组仍是{10, 20, 30}。

所以题目的直接答案是:*p++改变指针p,不改变它原来指向的元素。要真正看懂这句话,不能只记“谁的优先级高”,还要把表达式的结果和它产生的副作用分开。

先补括号:它是*(p++)

后缀++比一元解引用*结合得更紧,因此:

*p++

按下面的结构解析:

*(p++)

而不是:

(*p)++

更严谨地说,C11 标准本身用表达式文法规定这种结构,常见的“优先级表”是从文法归纳出来的。这里说“先结合”是在说明括号怎样补,不是在声称某条机器指令一定先执行。

Clang 16 的抽象语法树也给出同样结构:外层节点是前缀*,它的操作数是后缀++。编译器证据用于辅助观察,规则本身仍由 C 文法决定。

一次表达式,留下两个结果

后缀自增p++有两层效果:这个子表达式产生p的原值,同时以副作用把p更新为下一个元素的位置。

假设最初p == &values[0]:

  1. p++

    这个子表达式提供旧指针,也就是&values[0];

  2. 外层*使用旧指针,指代values[0];

  3. 在int result = *p++;的读取上下文中,result得到 10;

  4. 副作用完成后,p变为&values[1]。

这里没有任何一步要求修改values[0]。哪怕丢掉整个表达式的结果,指针更新仍然存在:

(void)*p++;

这句会让p前进一步,但不会让当前元素加一。它偶尔用于边读边推进的代码,不过拆成两句通常更便于调试:

int current = *p; ++p;

三种相似写法,改的对象不同

把三种写法放进相同的初始状态,差别最直观:

int values[] = {10, 20, 30}; int *p = values; int a = *p++; /* a=10, p 指向 values[1],数组不变 */

如果每次都重新初始化数组和指针,完整实验得到:

*p++ result=10, index=1, array={10,20,30} (*p)++ result=10, index=0, array={11,20,30} ++*p result=11, index=0, array={11,20,30}

(*p)++的括号让++作用于所指元素:表达式给出元素旧值 10,再把元素改成 11,指针不动。++*p等价于++(*p):先把元素改成 11,表达式结果也是 11,指针仍然不动。

最后还要守住指针边界。指针递增只能在同一数组的元素以及尾后位置之间进行。若p指向最后一个元素,*p++可以读取该元素并形成尾后指针;尾后指针可以比较,却不能再次解引用。把这一点漏掉,即使括号分析正确,下一次访问仍可能是未定义行为。

遇到类似表达式,可以固定检查三件事:先补出语法括号,再写下子表达式产生的值,最后单独记录它修改了哪个对象。对*p++,答案就是*(p++)、旧指针值、更新p。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询