先看看无聊的C运算符优先级和结合性。看到表1的人可以分为三类:
- C语言编译器的作者
- 计算机入门者
- 各种计算机基础能力考试应对者
比如要你计算: -Num++, +Num++, f( a(), b(), c()) ,int a= c,d,e; 之类的运算顺序。
大多数工程师为了避免记忆这繁琐的规则,使用()括号大法来解决大部分运算符优先级/结合性的问题。而且在大多数情况下,商业团队会通过统一的编程规范来避免程序语言的”坑”。
表1:运算符优先级和结合性
优先级 | 运算符 | 描述 | 结合性 |
---|---|---|---|
1 | ++ --
|
后缀自增与自减 | 从左到右 |
()
|
函数调用 | ||
[]
|
数组下标 | ||
.
|
结构体与联合体成员访问 | ||
->
|
结构体与联合体成员通过指针访问 | ||
(type){list}
|
复合字面量(C99) | ||
2 | ++ --
|
前缀自增与自减[注 1] | 从右到左 |
+ -
|
一元加与减 | ||
! ~
|
逻辑非与逐位非 | ||
(type)
|
转型 | ||
*
|
间接(解引用) | ||
&
|
取址 | ||
sizeof
|
取大小[注 2] | ||
_Alignof
|
对齐要求(C11) | ||
3 | * / %
|
乘法、除法及余数 | 从左到右 |
4 | + -
|
加法及减法 | |
5 | << >>
|
逐位左移及右移 | |
6 | < <=
|
分别为 < 与 ≤ 的关系运算符 | |
> >=
|
分别为 > 与 ≥ 的关系运算符 | ||
7 | == !=
|
分别为 = 与 ≠ 关系 | |
8 | &
|
逐位与 | |
9 | ^
|
逐位异或(排除或) | |
10 | |
|
逐位或(包含或) | |
11 | &&
|
逻辑与 | |
12 | ||
|
逻辑或 | |
13 | ?:
|
三元条件[注 3] | 从右到左 |
14[注 4] | =
|
简单赋值 | |
+= -=
|
以和及差赋值 | ||
*= /= %=
|
以积、商及余数赋值 | ||
<<= >>=
|
以逐位左移及右移赋值 | ||
&= ^= |=
|
以逐位与、异或及或赋值 | ||
15 | ,
|
逗号 | 从左到右 |
去记忆这种表格明显是反直觉的,我们还是得理解编译器帮我们解释符号的意图。
C的符号可以归结为几类:
- 算术运算符。最普通的+ – * / % ~ & | ^ << >>
- 逻辑运算符。! && ||
- 赋值运算符。= &= <<=
- 比较运算符。< == >= <= > !=
- 成员访问。 [] () * -> .
- 其他 … , (type强转), sizeof, _Alignof
先是结合性,原则很简单,也很人类。
就是按照普通人的阅读顺序去记忆: 比如[], 它肯定是从左到右结合。因为 一个表达式 a[1], 先要知道a,才能知道相对寻址的 a[1]。 再比如算术运算a + b, 肯定是先move a到寄存器,在把a和b想加。因此也是从左向右结合。
那么,哪些是从右往左结合呢?
我们写代码 sizeof( SuperStruct ), 肯定是先想到SuperStruct的大小和地址在哪,然后再去取大小。这就是很直接的计算机思维 。
最有代表性的是: 前++,前– VS 后++, 后–
用他们来测试下这种规律的正确性:
a++ , 从左到右。因为先得知道a,才能++。
–a , 从右往左结合。 因为先得知道a,才能–。
看,这是不用记忆的。非常直接的计算机符号逻辑。
优先级的记忆
优先级最高的: 除了后加/后减的优先级超高。 成员访问优先级高完全符合直觉。成员访问优先级高,可以理解为得先有数据才能做各种运算。
优先级次高的: 几乎全部单目运算符优先级是次高的。单目运算符只需要一个操作数,和成员访问有相同的属性。
优先级第三/四高: 接下来是双目运算符, 乘/除/取余(优先级三) 比 加减(优先级四)优先级高,符合直觉。
优先级第五/六高: 算术运算比关系运算优先级高。 加减乘除移位比所有比较符号优先级高。移位这种算术运算 >>, << 比 >,< >=,<= 的符号优先级高。先算出来再比较。
优先级第六/七高: >=, <= 比 ==, !==优先级高。这个没有什么规则。就是C语言编译器的人自己定的规则。 先比较大小,再比较是否相等。
优先级第八/九/⑩高: 位运算自己的比较,依次是 & ^ |
优先级第十一/十二: 逻辑运算 && ||
第十三: 孤零零的一个 三目。
第十四:各种赋值
第十五:孤独的逗号。
所以,总结起来规则是:
- 成员运算优先级最高。成员运算符算是单目的一种,所以剩下的单目运算符次高。(单目优先原则,直觉)
- 算术运算符比关系比较运算符高。(先算[包括移位]出来再比较,直觉)
- 关系比较运算符比位运算符高。(比较完再来做位运算,非直觉)
- 位运算比逻辑运算符高。(有运算结果再来做逻辑与或)
- 逻辑运算比赋值运算符高。(做完算术,比较,位和逻辑运算再来赋值)
- 逗比排在最后。
- 结合规则完全从直觉去判断。
成员单目运算 > 普通单目运算 > 关系运算 > 位运算 > 逻辑运算 > 赋值运算 > 逗号运算。
结合方向完全用直觉去判断。
1,先单目取值。
2,先计算后比较
3,先比较后做位运算
4,计算比较结束后,开始逻辑上的关系计算。
5,最后三目,赋值,逗号。
6,结合规则按照直觉判断。