41、运算符优先级:++与*等运算符的优先级混淆

说实话,运算符优先级这个问题,我见过太多人栽跟头了。尤其是 ++* 混在一起的时候,那叫一个乱。我自己刚入行那会儿,也在这上面吃过亏。

先看一个经典例子。你猜下面这段代码会输出什么?

int arr[] = {10, 20, 30};
int *p = arr;
int x = *p++;
printf("%d %d\n", x, *p);

很多人第一反应是:*p++ 到底是先取 p 指向的值,还是先让 p 自增?

答案是:先取 p 指向的值,然后 p 再自增。所以 x 是 10,*p 是 20。

为什么会这样?因为 ++ 的优先级高于 *。但 ++ 是后缀形式,它的规则是「先使用,后自增」。所以 *p++ 等价于 *(p++)——先取 *p,再让 p 加 1。

核心规则:

  • 后缀 ++-- 的优先级高于单目 *
  • 前缀 ++-- 的优先级与单目 * 相同,结合方向从右向左

前缀 ++ 的情况

再看前缀版本:

int arr[] = {10, 20, 30};
int *p = arr;
int y = *++p;
printf("%d %d\n", y, *p);

这里 *++p 等价于 *(++p)。先让 p 自增指向下一个元素,再取那个元素的值。所以 y 是 20,*p 也是 20。

嗯,这里要注意:前缀 ++* 优先级相同,从右向左结合。所以先算 ++p,再算 *

我踩过的坑

我在项目中遇到过这样一个场景。当时要遍历一个链表,把每个节点的值累加起来。我写了这么一段代码:

int sum = 0;
while (head != NULL) {
    sum += *head->data++;
    head = head->next;
}

结果跑出来数据全乱套了。你猜问题出在哪?

-> 的优先级高于 *,但 ++ 的优先级又高于 ->。所以 *head->data++ 实际被解析成了 *(head->(data++))。它先取了 data 指针,然后让 data 自增,最后再解引用。这完全不是我要的逻辑。

正确的写法应该是:

sum += (*(head->data))++;

或者更清晰一点:

int val = *(head->data);
sum += val;
(head->data)++;

避坑指南:

我曾经因为这种优先级混淆,在调试一个通信协议解析模块时花了整整一个下午。最后发现就是 *p++(*p)++ 的区别。前者移动指针,后者修改值。一字之差,天壤之别。

常见组合速查表

表达式 实际含义 结果说明
*p++ *(p++) 取当前值,指针后移
*++p *(++p) 指针先移,再取值
(*p)++ 先取 *p,再自增该值 修改指针指向的值
++*p ++(*p) 先自增指针指向的值
*p->data *(p->data) 取结构体成员指针指向的值

我的建议

我个人习惯是:拿不准就加括号。别觉得加括号显得自己水平低。恰恰相反,写出清晰无歧义的代码,才是专业的表现。

你想想看,代码是写给人看的。编译器能读懂任何写法,但你的同事不一定。我见过太多因为少写一对括号,导致代码逻辑完全跑偏的案例。

实用技巧:

如果你在写 *p++ 这种表达式,先问自己两个问题:

  1. 我到底是想移动指针,还是修改值?
  2. 如果三个月后我自己回来看这段代码,能一眼看懂吗?

如果答案是否定的,那就拆开写,或者加括号。

知识体系图

下面这张图总结了 ++* 组合时的优先级关系:

++ 与 * 运算符优先级关系图 后缀 ++ / -- 优先级最高 前缀 ++ / -- * & ! ~ 优先级相同,从右向左结合 * / % + - << >> ... 优先级低于单目运算符

说白了,记住一句话就行:后缀 ++ 是老大,单目运算符排第二,二元运算符靠边站。拿不准就加括号,别跟编译器玩猜谜游戏。


公众号:蓝海资料掘金营,微信deep3321