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++ 这种表达式,先问自己两个问题:
- 我到底是想移动指针,还是修改值?
- 如果三个月后我自己回来看这段代码,能一眼看懂吗?
如果答案是否定的,那就拆开写,或者加括号。
知识体系图
下面这张图总结了 ++ 和 * 组合时的优先级关系:
说白了,记住一句话就行:后缀 ++ 是老大,单目运算符排第二,二元运算符靠边站。拿不准就加括号,别跟编译器玩猜谜游戏。
公众号:蓝海资料掘金营,微信deep3321