运算符(Operators)
运算符是对数据进行计算和比较的符号。C 语言提供了丰富的运算符,理解它们的优先级和结合性是写出正确表达式的关键。
1. 算术运算符
int a = 10, b = 3;
int sum = a + b; // 加 → 13
int diff = a - b; // 减 → 7
int prod = a * b; // 乘 → 30
int quot = a / b; // 除 → 3(整数除法,截断小数)
int remains = a % b; // 取模(求余数)→ 1
整数除法 vs 浮点除法
int x = 10 / 3; // x = 3(整数除法)
float y = 10 / 3; // y = 3.0(先做整数除法 10/3=3,再转为 3.0)
// 正确获取小数结果的方式:
float z1 = 10.0f / 3.0f; // 直接用浮点数字面量
float z2 = (float)10 / 3; // 将整数显式转换为浮点类型
float z3 = 10.0f / 3; // 混合类型时自动提升
取模运算的特殊规则
- 操作数必须都是整数,不能对
float或double取模。 - 结果的符号与被除数一致:
int r1 = 10 % 3; // 1
int r2 = -10 % 3; // -1(被除数为负,结果也为负)
int r3 = 10 % -3; // 1(被除数为正,结果为正)
2. 关系运算符
关系运算符用于比较两个值,结果为 1(真)或 0(假)。
| 运算符 | 含义 | 示例(a = 10, b = 3) |
|---|---|---|
== |
等于 | a == b → 0 |
!= |
不等于 | a != b → 1 |
> |
大于 | a > b → 1 |
< |
小于 | a < b → 0 |
>= |
大于等于 | a >= 10 → 1 |
<= |
小于等于 | b <= 2 → 0 |
⚠️ 常见陷阱:
=是赋值,==是比较。if (x = 5)永远为真——因为它把 5 赋给了 x,而 5 是非零值。正确写法是if (x == 5)。
3. 逻辑运算符
逻辑运算符连接多个条件,结果依然是 1(真)或 0(假)。
| 运算符 | 含义 | 示例 |
|---|---|---|
&& |
逻辑与(AND) | a > 0 && a < 100(a 在 0 到 100 之间) |
|| |
逻辑或(OR) | x < 0 || x > 100(x 在范围外) |
! |
逻辑非(NOT) | !flag(flag 为假时为真) |
短路求值(Short-Circuit Evaluation)
// && 短路:左侧为假时,右侧不会执行
if (p != NULL && *p == 42) { // 安全:p 为空时不会解引用
// ...
}
// || 短路:左侧为真时,右侧不会执行
if (index < 0 || index >= size) { // 任一条件满足即判定越界
// ...
}
短路特性既提升效率,也常用于写"守卫条件"——先检查指针非空,再访问其内容。
逻辑运算真值表
| A | B | A && B | A || B | !A |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 1 |
| 0 | 非0 | 0 | 1 | 1 |
| 非0 | 0 | 0 | 1 | 0 |
| 非0 | 非0 | 1 | 1 | 0 |
4. 位运算符
位运算符直接操作二进制位,常用于底层编程、标志位管理和性能优化。
| 运算符 | 含义 | 示例(a=5(0101), b=3(0011)) |
|---|---|---|
& |
按位与 | a & b → 1(0101 & 0011 = 0001) |
\| |
按位或 | a \| b → 7(0101 | 0011 = 0111) |
^ |
按位异或 | a ^ b → 6(0101 ^ 0011 = 0110) |
~ |
按位取反 | ~a → -6(~00000101 = 11111010) |
<< |
左移 | a << 1 → 10(0101 → 1010,乘 2) |
>> |
右移 | a >> 1 → 2(0101 → 0010,除 2) |
典型应用
// 1. 标志位操作(用一个整数的不同位表示多个开关)
#define FLAG_A 0x01 // 0001
#define FLAG_B 0x02 // 0010
#define FLAG_C 0x04 // 0100
int flags = 0;
flags |= FLAG_A; // 打开 FLAG_A
flags &= ~FLAG_B; // 关闭 FLAG_B
if (flags & FLAG_C) { // 检测 FLAG_C 是否打开
// ...
}
// 2. 高效率乘除 2 的幂
int x = 10;
int y = x << 3; // 等价于 x * 8(前提是不溢出)
int z = x >> 2; // 等价于 x / 4
⚠️ 不要混淆
&(按位与)和&&(逻辑与)、|(按位或)和||(逻辑或)。前者操作每一位,后者操作布尔值并有短路特性。
5. 赋值运算符
int x = 10;
x += 5; // x = x + 5 → 15
x -= 3; // x = x - 3 → 12
x *= 2; // x = x * 2 → 24
x /= 4; // x = x / 4 → 6
x %= 4; // x = x % 4 → 2
// 位运算的复合赋值
x <<= 2; // x = x << 2
x &= 0x0F; // x = x & 0x0F
6. 自增与自减
int a = 5;
int b = ++a; // 前置自增:a 先加 1,再赋值给 b → a=6, b=6
int c = a++; // 后置自增:先赋值 a 给 c,a 再加 1 → c=6, a=7
| 写法 | 含义 | 执行顺序 |
|---|---|---|
++x |
前置自增 | 先加后用 |
x++ |
后置自增 | 先用后加 |
--x |
前置自减 | 先减后用 |
x-- |
后置自减 | 先用后减 |
建议:在复杂表达式中避免使用自增/自减(如 arr[i++] = ++j),拆成独立语句更清晰。
7. 条件运算符(三元运算符)
// 语法:条件 ? 为真时的值 : 为假时的值
int max = (a > b) ? a : b;
等价于:
int max;
if (a > b) {
max = a;
} else {
max = b;
}
三元运算符适合简单的二选一赋值。嵌套使用会严重降低可读性,应避免。
8. sizeof 运算符
printf("%zu\n", sizeof(int)); // 类型的大小
printf("%zu\n", sizeof(x)); // 变量的大小(可省略括号)
printf("%zu\n", sizeof(arr)); // 数组的总大小(元素数 × 每个元素大小)
9. 运算符优先级
当多个运算符同时出现时,优先级决定"谁先算"。优先级相同时,结合性决定方向。
| 优先级 | 运算符 | 结合性 |
|---|---|---|
| 1(最高) | () [] -> . ++(后置) --(后置) |
左到右 |
| 2 | ! ~ ++(前置) --(前置) +(正) -(负) *(解引用) &(取地址) sizeof |
右到左 |
| 3 | * / % |
左到右 |
| 4 | + - |
左到右 |
| 5 | << >> |
左到右 |
| 6 | < <= > >= |
左到右 |
| 7 | == != |
左到右 |
| 8 | &(按位与) |
左到右 |
| 9 | ^ |
左到右 |
| 10 | \|(按位或) |
左到右 |
| 11 | && |
左到右 |
| 12 | \|\| |
左到右 |
| 13 | ?: |
右到左 |
| 14 | = += -= 等赋值符 |
右到左 |
| 15(最低) | ,(逗号) |
左到右 |
口诀:不确定优先级时,加括号。
(a & mask) == mask比a & mask == mask更安全、更清晰。
10. 总结
| 要点 | 说明 |
|---|---|
| 整数除法截断 | 需要小数结果时先将操作数转为浮点类型 |
== 不是 = |
比较用 ==,赋值用 = |
&& 和 \|\| 短路 |
左侧结果能决定整体时右侧不执行 |
& 和 \| 操作位 |
用于标志位和底层操作,不要与逻辑运算符混淆 |
| 优先级不确定时加括号 | 括号优先级最高,且让意图一目了然 |
避免复杂表达式中的 ++/-- |
拆分语句可读性更好 |
作者: Qing | 修改日期: 2026-07-18