4. 运算符与表达式:C语言的运算基石
C语言之所以被称为“中级语言”,很大程度上源于其丰富的运算符体系。运算符是告诉编译器执行特定数学或逻辑操作的符号,而表达式则是由运算符和操作数组成的、能够计算出单一值的式子。在计算机二级考试中,运算符的优先级、结合性以及不同运算符的混合使用是必考且极易失分的点。本章将系统拆解七大类运算符,并通过对比表格与实战案例,帮助你建立清晰的运算逻辑框架。
4.1 算术运算符与赋值运算符:最基础的运算单元
核心原理:算术运算符包括加(+)、减(-)、乘(*)、除(/)和取模(%)。其中,/ 和 % 的行为取决于操作数类型:当两个整数相除时,结果仍为整数(小数部分被截断);取模运算要求两个操作数均为整数,结果为余数。赋值运算符分为简单赋值(=)和复合赋值(+=、-=、*=、/=、%=),复合赋值等价于先进行算术运算再赋值,但表达式只计算一次左值。
7/2 结果为 3,而 7.0/2 结果为 3.5。
案例拆解:考虑以下代码片段:
int a = 10, b = 3;
float c = 10.0f;
int d = a / b; // d = 3,整数除法
float e = c / b; // e = 3.3333,浮点除法
int f = a % b; // f = 1,10除以3余1
a += 5; // a = 15,等价于 a = a + 5
b *= 2; // b = 6,等价于 b = b * 2
这里的关键在于理解类型转换规则:当整型与浮点型混合运算时,整型会自动提升为浮点型。复合赋值运算符的右侧表达式会先被完整计算,再进行运算和赋值,例如 a *= b + 2 等价于 a = a * (b + 2),而非 a = a * b + 2。
| 运算符 | 示例 | 结果 | 注意事项 |
|---|---|---|---|
| / | 7/2 | 3 | 整数除法截断小数 |
| / | 7.0/2 | 3.5 | 浮点除法保留小数 |
| % | 7%2 | 1 | 仅适用于整数 |
| += | a+=3 | a=a+3 | 右结合,先算右侧 |
-7%3 结果为 -1,而 7%-3 结果为 1。C语言标准规定,a%b 的结果符号与 a 相同。
4.2 关系运算符与逻辑运算符:条件判断的核心
核心原理:关系运算符(>、<、>=、<=、==、!=)用于比较两个值,结果为 1(真)或 0(假)。逻辑运算符包括逻辑与(&&)、逻辑或(||)和逻辑非(!),操作数被当作“真”(非零)或“假”(零)处理。C语言中,任何非零值都视为真,零视为假。
判断方法:逻辑与(&&)具有短路特性:若左侧操作数为假,则右侧表达式不再计算,直接返回假。逻辑或(||)同样具有短路特性:若左侧为真,则右侧不再计算。这一特性常用于避免除零错误或空指针访问。
int year = 2024;
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
printf("闰年\n");
} else {
printf("平年\n");
}
这里利用短路特性:若 year%4 != 0,则第一个括号内直接为假,不会继续判断 year%100。
常见误区:混淆赋值运算符(=)与相等运算符(==)。在条件判断中误写为 if (a = 5) 会导致赋值操作,且条件恒为真。建议将常量写在左侧:if (5 == a),这样即使误写为 5 = a 也会产生编译错误。
| 运算符 | 含义 | 示例 | 结果 |
|---|---|---|---|
| && | 逻辑与 | 1 && 0 | 0 |
| || | 逻辑或 | 1 || 0 | 1 |
| ! | 逻辑非 | !0 | 1 |
| == | 等于 | 3 == 3 | 1 |
| != | 不等于 | 3 != 4 | 1 |
4.3 位运算符:二进制层面的精细操作
核心原理:位运算符直接对整数的二进制位进行操作,包括按位与(&)、按位或(|)、按位异或(^)、按位取反(~)、左移(<<)和右移(>>)。位运算通常用于嵌入式开发、权限管理、数据压缩等场景,其执行效率远高于算术运算。
操作流程:位运算的规则如下:
- 按位与(&):两个位都为1时结果为1,否则为0。常用于清零特定位。
- 按位或(|):两个位至少一个为1时结果为1。常用于置位特定位。
- 按位异或(^):两个位不同时结果为1,相同时为0。常用于翻转特定位。
- 左移(<<):所有位向左移动,右侧补0,相当于乘以2的n次方。
- 右移(>>):所有位向右移动,左侧补符号位(算术右移)或补0(逻辑右移),相当于除以2的n次方。
unsigned char perm = 0; // 初始无权限
perm |= (1 << 0); // 赋予读权限,perm = 00000001
perm |= (1 << 2); // 赋予执行权限,perm = 00000101
// 检查是否有写权限
if (perm & (1 << 1)) {
printf("有写权限\n");
} else {
printf("无写权限\n");
}
// 翻转执行权限
perm ^= (1 << 2); // perm = 00000001
这种方式的优势在于:多个权限可以存储在一个字节中,且判断和修改操作都非常高效。
4.4 条件运算符、逗号运算符与优先级结合性
核心原理:条件运算符(? :)是C语言中唯一的三目运算符,格式为 表达式1 ? 表达式2 : 表达式3。若表达式1为真,则整个表达式的值为表达式2的值,否则为表达式3的值。逗号运算符(,)用于将多个表达式串联为一个表达式,整个表达式的值为最后一个表达式的值。
判断方法:运算符优先级决定了表达式中运算的先后顺序,结合性决定了同优先级运算符的执行方向。C语言的优先级规则可总结为:单目 > 算术 > 关系 > 逻辑 > 赋值 > 逗号。结合性方面,绝大多数运算符为左结合(从左到右),但赋值、条件、单目运算符为右结合(从右到左)。
| 优先级 | 运算符 | 结合性 |
|---|---|---|
| 最高 | () [] -> . | 左 |
| 单目 | ! ~ ++ -- + - * & (类型) | 右 |
| 算术 | * / % | 左 |
| 算术 | + - | 左 |
| 移位 | << >> | 左 |
| 关系 | < <= > >= | 左 |
| 相等 | == != | 左 |
| 按位与 | & | 左 |
| 按位异或 | ^ | 左 |
| 按位或 | | | 左 |
| 逻辑与 | && | 左 |
| 逻辑或 | || | 左 |
| 条件 | ? : | 右 |
| 赋值 | = += -= *= /= %= <<= >>= &= |= ^= | 右 |
| 逗号 | , | 左 |
int max = (a > b) ? a : b;
逗号运算符常用于for循环中初始化多个变量:
for (int i = 0, j = 10; i < j; i++, j--) {
printf("%d %d\n", i, j);
}
这里逗号运算符将多个表达式串联,整个表达式的值为最后一个表达式的值。
a < b && c > d 实际为 (a < b) && (c > d),因为关系运算符优先级高于逻辑与。而 a = b > c 实际为 a = (b > c),因为赋值优先级最低。建议在复杂表达式中使用括号明确运算顺序,既提高可读性又避免错误。
4.5 综合案例:运算符优先级实战演练
案例背景:某嵌入式系统需要根据传感器数据计算控制信号。给定整型变量 x=5, y=2, z=1,计算以下表达式的值:
int result = x > y + z && !(y < z) ? x * y : x / y;
分析过程:按照优先级逐步拆解:
- 先计算算术运算:
y + z = 3,x * y = 10,x / y = 2。 - 再计算关系运算:
x > 3即5 > 3为真(1);y < z即2 < 1为假(0)。 - 逻辑非:
!(0) = 1。 - 逻辑与:
1 && 1 = 1。 - 条件运算符:条件为真,取
x * y = 10。 - 赋值:
result = 10。
结果验证:最终 result 的值为 10。这个案例展示了多类运算符混合时的运算顺序:先算术、再关系、再逻辑、再条件、最后赋值。
-Wall 选项)可以帮助发现潜在的优先级问题。考试中遇到复杂表达式,建议先手动添加括号再逐步计算,避免因优先级判断失误而丢分。
总结复盘:本章覆盖了C语言七大类运算符的核心规则。重点掌握:整数除法与浮点除法的区别、复合赋值的展开方式、短路求值特性、位运算的典型应用场景、条件运算符的替代if-else能力,以及优先级和结合性的判断方法。在二级考试中,运算符相关题目通常以“求表达式值”或“判断输出结果”的形式出现,建议通过大量练习形成条件反射式的分析能力。