4. 运算符与表达式

运算符是C++语言中用于执行数据操作的符号,而表达式则是由运算符、操作数和标点符号组成的有效序列。在计算机二级C++考试中,运算符与表达式是语法基础中的核心模块,直接关系到后续流程控制、函数调用和类设计的理解深度。本章将系统梳理八大类运算符的规则、优先级与结合性,并通过案例拆解帮助你在实际编程中避免常见陷阱。

C++ 运算符体系 算术 / 赋值 / 逗号 关系 / 逻辑 / 位运算 sizeof / 优先级结合性 算术: + - * / % ++ -- 赋值: = += -= *= /= %= 逗号: , (从左到右依次求值) 注意: 整数除法截断、取模符号 关系: > < >= <= == != 逻辑: && || ! 位: & | ^ ~ << >> 注意: 短路求值、位运算应用 sizeof: 类型/变量字节数 优先级: 从高到低 15 级 结合性: 左结合 / 右结合 注意: 括号改变优先级 表达式求值顺序 优先级高先算,同级看结合性 类型转换 隐式转换 / 强制转换 (类型) 副作用与序列点 i++ 与 ++i 的区别 核心:优先级决定计算顺序,结合性决定同级方向

4.1 算术运算符与赋值运算符

核心原理

算术运算符包括二元运算符(+、-、*、/、%)和一元运算符(++、--)。其中/对整数执行整除运算,结果向零截断;%要求两个操作数均为整数,结果的符号与被除数一致。赋值运算符分为简单赋值(=)和复合赋值(+=、-=、*=、/=、%=),复合赋值等价于先运算再赋值,但只对左值求值一次。

判断方法与操作流程

  • 整数除法陷阱:7/2 结果为 3,而非 3.5;若需浮点结果,应写成 7.0/2 或 (double)7/2。
  • 自增自减前缀与后缀:++i 先加后用,i++ 先用后加。在复杂表达式中,后缀自增会返回原值并产生副作用。
  • 复合赋值展开:a += b 等价于 a = a + b,但 a 只被求值一次,对数组下标或指针表达式尤为重要。

案例拆解

int a = 10, b = 3;
double c = a / b;        // c = 3.0,因为整数除法先执行
double d = (double)a / b; // d ≈ 3.333...
int e = a % b;           // e = 1
int f = -7 % 3;          // f = -1,符号跟随被除数
int i = 5;
int g = i++ + ++i;       // 未定义行为!避免此类写法

在考试中,若遇到i++ + ++i这类表达式,应直接判定为未定义行为,不要试图猜测编译器行为。正确做法是将自增操作独立成语句。

风险提示:C++标准规定,在同一表达式中对同一变量多次修改且无序列点分隔,属于未定义行为。例如 i = i++ + 1 在部分编译器上可能得到意外结果,务必避免。

4.2 关系运算符与逻辑运算符

核心原理

关系运算符(>、<、>=、<=、==、!=)返回布尔值 true 或 false。逻辑运算符(&&、||、!)操作布尔值,支持短路求值:&& 左侧为假时右侧不执行,|| 左侧为真时右侧不执行。短路求值是C++的重要特性,常用于条件保护。

判断方法与操作流程

运算符含义示例结果
>大于5 > 3true
==等于5 == 5true
!=不等于5 != 3true
&&逻辑与true && falsefalse
||逻辑或true || falsetrue
!逻辑非!truefalse

实战场景

int age = 20;
bool isAdult = (age >= 18) && (age < 60);
// 短路求值示例:
int* ptr = nullptr;
if (ptr != nullptr && *ptr > 0) {
    // 安全:ptr 为 null 时不会解引用
}
经验提醒:在判断浮点数相等时,不要直接使用 ==,因为浮点精度可能导致误差。应使用 fabs(a - b) < 1e-6 进行近似比较。

4.3 位运算符与逗号运算符

核心原理

位运算符直接操作整数的二进制位:&(按位与)、|(按位或)、^(按位异或)、~(按位取反)、<<(左移)、>>(右移)。位运算常用于底层开发、状态标志管理和性能优化。逗号运算符(,)从左到右依次求值每个表达式,整个表达式的值是最右侧表达式的值。

操作流程与案例

unsigned char flags = 0b00001101;
// 检查第3位是否为1
bool isSet = (flags & 0b00000100) != 0;
// 设置第2位为1
flags |= 0b00000010;
// 切换第1位
flags ^= 0b00000001;
// 左移2位相当于乘以4
int x = 3;
int y = x << 2;  // y = 12

// 逗号运算符
int a = 1, b = 2;
int c = (a++, b++, a + b);  // c = 5,a=2, b=3

常见误区

  • 混淆逻辑与和位与:&& 返回布尔值,& 返回整数位运算结果。
  • 右移符号位:对有符号整数右移,行为由编译器决定(算术右移或逻辑右移),建议使用无符号类型进行位运算。
  • 逗号表达式优先级最低:在 for 循环中常用,但注意不要与函数参数列表中的逗号混淆。

4.4 sizeof运算符与优先级结合性

核心原理

sizeof 是编译时运算符,返回对象或类型所占字节数,类型为 size_t。它不计算表达式的值,仅推断类型大小。运算符优先级决定表达式的计算顺序,结合性决定同级运算符的执行方向(左结合或右结合)。C++优先级表共15级,从高到低依次为:作用域解析、后缀、一元、乘除、加减、移位、关系、相等、位与、位异或、位或、逻辑与、逻辑或、条件、赋值、逗号。

优先级与结合性速查表

优先级运算符结合性
1(最高)::左
2() [] -> . ++ --左
3! ~ + - ++ -- & * (类型) sizeof右
4* / %左
5+ -左
6<< >>左
7< <= > >=左
8== !=左
9&左
10^左
11|左
12&&左
13||左
14?:右
15(最低)= += -= *= /= %= <<= >>= &= |= ^= ,右

案例拆解与实战

int arr[10];
size_t n = sizeof(arr) / sizeof(arr[0]);  // n = 10

// 优先级陷阱
int a = 5, b = 3, c = 2;
int result = a > b ? a : b > c ? b : c;  // 右结合,等价于 a > b ? a : (b > c ? b : c)

// 结合性示例
int x = 10;
x += 5 * 2;  // 先算 5*2=10,再 x = 10 + 10 = 20
核心结论:优先级高的运算符先结合,同级运算符按结合性方向执行。赋值运算符是右结合,因此 a = b = c 等价于 a = (b = c)。当表达式复杂时,使用括号明确意图,既提高可读性又避免错误。

常见误区与总结

  • 误区1:认为 sizeof 是函数,实际上它是运算符,不需要括号(虽然通常写成 sizeof(int))。
  • 误区2:混淆 = 与 ==,在条件判断中误用赋值导致逻辑错误。
  • 误区3:忽略短路求值,在 && 右侧放置有副作用的表达式,可能导致意外跳过。
  • 误区4:位运算优先级低于相等比较,flags & mask == 0 实际解析为 flags & (mask == 0),应写成 (flags & mask) == 0。

掌握运算符的核心在于理解优先级表、结合性规则以及每种运算符的求值特性。建议在编写表达式时遵循“简洁优先、括号辅助”的原则,避免写出依赖复杂优先级规则的代码。考试中遇到表达式求值题,先画出优先级树,再逐步计算,可有效降低出错率。