4. 运算符与表达式:C语言的运算基石

C语言之所以被称为“中级语言”,很大程度上源于其丰富的运算符体系。运算符是告诉编译器执行特定数学或逻辑操作的符号,而表达式则是由运算符和操作数组成的、能够计算出单一值的式子。在计算机二级考试中,运算符的优先级、结合性以及不同运算符的混合使用是必考且极易失分的点。本章将系统拆解七大类运算符,并通过对比表格与实战案例,帮助你建立清晰的运算逻辑框架。

C语言运算符体系 算术运算符 + - * / % 赋值运算符 = += -= *= /= %= 关系运算符 > < >= <= == != 逻辑运算符 && || ! 位运算符 & | ^ ~ << >> 条件运算符 ? : 逗号运算符 , 优先级核心规则 单目 > 算术 > 关系 > 逻辑 > 赋值 > 逗号 结合性核心规则 左结合:算术/关系/逻辑 右结合:赋值/条件/单目

4.1 算术运算符与赋值运算符:最基础的运算单元

核心原理:算术运算符包括加(+)、减(-)、乘(*)、除(/)和取模(%)。其中,/ 和 % 的行为取决于操作数类型:当两个整数相除时,结果仍为整数(小数部分被截断);取模运算要求两个操作数均为整数,结果为余数。赋值运算符分为简单赋值(=)和复合赋值(+=、-=、*=、/=、%=),复合赋值等价于先进行算术运算再赋值,但表达式只计算一次左值。

判断方法:整数除法与浮点除法的区分是高频考点。若操作数中至少有一个是浮点型(float/double),则执行浮点除法;若均为整型,则执行整数除法。例如 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/23整数除法截断小数
/7.0/23.5浮点除法保留小数
%7%21仅适用于整数
+=a+=3a=a+3右结合,先算右侧
常见误区:取模运算对负数结果的符号取决于被除数的符号。例如 -7%3 结果为 -1,而 7%-3 结果为 1。C语言标准规定,a%b 的结果符号与 a 相同。

4.2 关系运算符与逻辑运算符:条件判断的核心

核心原理:关系运算符(>、<、>=、<=、==、!=)用于比较两个值,结果为 1(真)或 0(假)。逻辑运算符包括逻辑与(&&)、逻辑或(||)和逻辑非(!),操作数被当作“真”(非零)或“假”(零)处理。C语言中,任何非零值都视为真,零视为假。

判断方法:逻辑与(&&)具有短路特性:若左侧操作数为假,则右侧表达式不再计算,直接返回假。逻辑或(||)同样具有短路特性:若左侧为真,则右侧不再计算。这一特性常用于避免除零错误或空指针访问。

实战场景:判断一个年份是否为闰年。闰年条件:能被4整除但不能被100整除,或者能被400整除。
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 && 00
||逻辑或1 || 01
!逻辑非!01
==等于3 == 31
!=不等于3 != 41

4.3 位运算符:二进制层面的精细操作

核心原理:位运算符直接对整数的二进制位进行操作,包括按位与(&)、按位或(|)、按位异或(^)、按位取反(~)、左移(<<)和右移(>>)。位运算通常用于嵌入式开发、权限管理、数据压缩等场景,其执行效率远高于算术运算。

操作流程:位运算的规则如下:

  • 按位与(&):两个位都为1时结果为1,否则为0。常用于清零特定位。
  • 按位或(|):两个位至少一个为1时结果为1。常用于置位特定位。
  • 按位异或(^):两个位不同时结果为1,相同时为0。常用于翻转特定位。
  • 左移(<<):所有位向左移动,右侧补0,相当于乘以2的n次方。
  • 右移(>>):所有位向右移动,左侧补符号位(算术右移)或补0(逻辑右移),相当于除以2的n次方。
案例拆解:使用位运算实现权限管理。假设权限用8位二进制表示,第0位为读权限,第1位为写权限,第2位为执行权限。
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;

分析过程:按照优先级逐步拆解:

  1. 先计算算术运算:y + z = 3,x * y = 10,x / y = 2。
  2. 再计算关系运算:x > 3 即 5 > 3 为真(1);y < z 即 2 < 1 为假(0)。
  3. 逻辑非:!(0) = 1。
  4. 逻辑与:1 && 1 = 1。
  5. 条件运算符:条件为真,取 x * y = 10。
  6. 赋值:result = 10。

结果验证:最终 result 的值为 10。这个案例展示了多类运算符混合时的运算顺序:先算术、再关系、再逻辑、再条件、最后赋值。

经验总结:在编写复杂表达式时,遵循“先括号、再单目、后算术、再关系、然后逻辑、最后赋值”的口诀。同时,利用编译器的警告信息(如 -Wall 选项)可以帮助发现潜在的优先级问题。考试中遇到复杂表达式,建议先手动添加括号再逐步计算,避免因优先级判断失误而丢分。

总结复盘:本章覆盖了C语言七大类运算符的核心规则。重点掌握:整数除法与浮点除法的区别、复合赋值的展开方式、短路求值特性、位运算的典型应用场景、条件运算符的替代if-else能力,以及优先级和结合性的判断方法。在二级考试中,运算符相关题目通常以“求表达式值”或“判断输出结果”的形式出现,建议通过大量练习形成条件反射式的分析能力。