C运算符优先级
定义
C的表分15级,同一行的运算符优先级相同。判定规则与Python一致:优先级高的先结合,同一优先级按结合方向分组。但两级之间的分界不同,C把按位运算放在比较之下,这是最容易误判的一处。
| 优先级 | 运算符 | 结合性 | 描述与示例 |
|---|---|---|---|
| 1 | () [] -> . ++ --(后缀) | 左到右 | 函数调用、数组下标、成员访问、后缀自增自减 |
| 2 | ! ~ ++ --(前缀) + -(一元) * & (type) sizeof | 右到左 | 单目运算符。*p++ 等价于 *(p++):先取值,再让指针自增 |
| 3 | * / % | 左到右 | 乘、除、取模。5 / 2得2,整数除法截断 |
| 4 | + - | 左到右 | 加、减 |
| 5 | << >> | 左到右 | 移位 |
| 6 | < <= > >= | 左到右 | 关系运算 |
| 7 | == != | 左到右 | 等于、不等于。if (a = b) 是赋值表达式,不是比较 |
| 8 | & | 左到右 | 按位与 |
| 9 | ^ | 左到右 | 按位异或 |
| 10 | | | 左到右 | 按位或 |
| 11 | && | 左到右 | 逻辑与,短路。优先级低于按位与 & |
| 12 | || | 左到右 | 逻辑或,短路 |
| 13 | ?: | 右到左 | 三目运算符 |
| 14 | = += -= *= /= %= <<= >>= &= ^= |= | 右到左 | 赋值 |
| 15 | , | 左到右 | 逗号运算符,值取最后一个表达式 |
性质
结合方向
C中只有单目运算符、三目运算符和赋值运算符右结合,其余左结合:
a = b = c等价于a = (b = c),赋值从右往左串起来;a ? b : c ? d : e等价于a ? b : (c ? d : e);i = 2, i + 3先执行i = 2,整个表达式的值是i + 3。
经典陷阱
*p++ 与 (*p)++ 的效果
#include <stdio.h>
int main(void) { int a[3] = {1, 2, 3}; int *p = a;
int x = *p++; /* x = 1,p移到a[1] */ int y = (*p)++; /* y = 2,a[1] 变成3,p不动 */ printf("%d %d %d %d\n", x, y, a[0], a[1]); /* 1 2 1 3 */ return 0;}第一行移动的是指针,第二行修改的是数据。读这类表达式时,先按优先级补出括号,再判断动的是地址还是值。
a & b == c的分组
#include <stdio.h>
int main(void) { int a = 6, b = 3, c = 2;
printf("%d\n", a & b == c); /* 6 & (3 == 2) = 6 & 0 = 0 */ printf("%d\n", (a & b) == c); /* (6 & 3) == 2,即2 == 2,得1 */ return 0;}两个表达式只差括号,结果一个是0、一个是1。位运算与比较同时出现时,按本意加括号。
探索
自增改变指针还是指向的值?
比较 *p++ 与 (*p)++ 对指针和数组元素的影响。
正在准备演示…
练习
int a = 6, b = 3, c = 2;时,表达式a & b == c的值是 。参考解答
比较先算,
3 == 2为0,再算6 & 0,结果为0。本意若是(a & b) == c,要自己加括号。int a[3] = {1, 2, 3}; int *p = a; int x = *p++;之后,x的值是 ,*p的值是 。参考解答
*p++等价于*(p++):先取a[0]的值给x,再把p移到a[1],所以*p是2。int a = 5; if (a = 0) { /* 分支 */ }中条件的结果是:参考解答
赋值表达式把0赋给
a,整个表达式的值就是0,因此条件为假。C允许赋值出现在条件中,编译器通常只警告;比较应写a == 0。int i = 0;之后,表达式i = 2, i + 3的值是 。参考解答
逗号运算符的优先级最低,先执行
i = 2,再求i + 3,整个表达式的值取最后一个,即5。表达式
0 ? 1 : 2 ? 3 : 4的值是 。参考解答
三目运算符右结合,等价于
0 ? 1 : (2 ? 3 : 4)。条件0为假,取后一支;内层条件2为真,取3。在32位
int的平台上,-7 / 2的值是 。参考解答
C的整数除法向零截断,商的整数部分是 。同样的式子写成Python的
-7 // 2,结果是 ,因为Python向下取整。