C语言操作符全解析(详解 + 优先级表 + 常见陷阱)
C语言的操作符是表达式求值的基础。理解优先级、结合性和副作用是写出正确、高效代码的关键。C23标准(2024正式发布)对操作符本身改动很小,主要新增了 typeof、typeof_unqual 等关键字,但核心操作符表保持稳定。
1. 操作符优先级与结合性完整表(C23)
优先级从高到低(数字越小优先级越高),同一行的运算符优先级相同。
| 优先级 | 操作符 | 描述 | 结合性 | 备注 |
|---|---|---|---|---|
| 1 | () [] -> . ++ -- (后缀) | 函数调用、数组下标、指针成员、结构体成员、后缀自增自减 | 左→右 | 后缀最高 |
| 2 | ++ -- (前缀) + - ! ~ * & (类型) sizeof _Alignof | 前缀自增自减、一元正负、逻辑非、按位取反、解引用、取地址、强制转换、sizeof、对齐 | 右→左 | 一元运算符 |
| 3 | * / % | 乘、除、取模 | 左→右 | |
| 4 | + - | 加、减 | 左→右 | 二元 |
| 5 | << >> | 左移、右移 | 左→右 | |
| 6 | < <= > >= | 小于、小于等于、大于、大于等于 | 左→右 | 关系 |
| 7 | == != | 等于、不等于 | 左→右 | 相等 |
| 8 | & | 按位与 | 左→右 | |
| 9 | ^ | 按位异或 | 左→右 | |
| 10 | | | 按位或 | 左→右 | |
| 11 | && | 逻辑与 | 左→右 | 短路求值 |
| 12 | || | 逻辑或 | 左→右 | 短路求值 |
| 13 | ?: | 条件(三元)运算符 | 右→左 | |
| 14 | = += -= *= /= %= &= ^= |= <<= >>= | 赋值及复合赋值 | 右→左 | |
| 15 | , | 逗号运算符 | 左→右 | 最低 |
记忆技巧:
- 后缀 > 一元 > 算术 > 移位 > 关系 > 相等 > 位运算 > 逻辑 > 条件 > 赋值 > 逗号
- 不确定时永远加括号,这是防御性编程的最佳实践。
2. 各类操作符详解
2.1 算术操作符
+-*/%/和%对整数是整数除法(向零截断)。%要求操作数为整数。
自增自减(++ –)重点:
- 前缀(
++i):先修改后使用 - 后缀(
i++):先使用后修改
int i = 5;
int a = ++i; // a=6, i=6
int b = i++; // b=6, i=7
陷阱:a = i++ + ++i; → 未定义行为(UB),不要在同一个表达式中多次修改同一变量。
2.2 关系与相等操作符
< <= > >= == !=- 结果为
int类型:真为1,假为0。 - 常见陷阱:
if (a = 5)(赋值)而不是if (a == 5)(比较)——这是经典Bug。
2.3 逻辑操作符(&& || !)
&&和||有短路求值特性:a && b:a为假时不求值ba || b:a为真时不求值b
示例(安全写法):
if (ptr != NULL && ptr->data > 0) // 防止野指针
2.4 位操作符(最重要之一)
&(与)、|(或)、^(异或)、~(取反)<<(左移)、>>(右移)
经典用途:
// 设置第n位
flags |= (1U << n);
// 清除第n位
flags &= ~(1U << n);
// 判断第n位是否为1
if (flags & (1U << n))
// 取反
flags ^= (1U << n);
// 右移负数注意:算术右移(符号位扩展)还是逻辑右移(实现定义,建议用无符号类型)
unsigned int u = 0xFFFFFFFFU;
u >>= 1; // 安全
陷阱:a & b == 0 实际是 a & (b == 0),因为 == 优先级高于 &。正确写法:(a & b) == 0
2.5 赋值操作符
- 简单赋值
= - 复合赋值
+= -= *= /= %= &= |= ^= <<= >>=
结合性为右→左:
a = b = c = 0; // 等价于 a = (b = (c = 0));
2.6 条件运算符(三元)
max = (a > b) ? a : b;
陷阱:三元运算符优先级较低,容易出错时加括号。
2.7 逗号运算符
i = (j=3, j+2); // 先执行j=3,再j+2,最后i=5
常用于 for 循环或宏定义中。
2.8 sizeof 与类型转换
sizeof返回size_t(无符号),操作数可以是类型或表达式。(类型)表达式:强制转换(可能丢失精度或引发未定义行为)。
C23 新增:
typeof(expr)、typeof_unqual(expr):获取类型(类似C++ decltype)。
2.9 指针与成员操作符
&(取地址)、*(解引用).(结构体成员)、->(指针成员)
经典陷阱:
*p++ // 等价于 *(p++),不是 (*p)++
3. 常见陷阱与防御性编程
- 优先级错误:
a & b == c、if (x & 1 == 0) - = vs ==:编译器通常会警告
if (x = 5) - 位运算与逻辑运算混淆:
&vs&&,|vs|| - 自增/自减副作用:同一变量多次出现
- 短路求值依赖:不要把有副作用的表达式放在
&&/||右侧 - 移位超过位宽:
1 << 32在32位int上是未定义行为 - 有符号右移:负数右移可能保留符号位
最佳实践:
- 复杂表达式必须加括号
- 使用
unsigned类型处理位运算 - 开启编译器警告(
-Wall -Wextra) - 代码审查时重点检查优先级相关表达式
总结
C语言操作符设计精巧但优先级复杂,“多用括号,少靠记忆” 是最可靠的策略。熟练掌握位运算和短路求值,能大幅提升代码性能和简洁度。
如果你想深入某个部分(例如位运算实战、sizeof 底层原理、C23 typeof 使用示例、常见面试题解析等),或者需要完整优先级记忆口诀、练习题,请告诉我,我可以继续展开!