4. 运算符与表达式
运算符是C++语言中用于执行数据操作的符号,而表达式则是由运算符、操作数和标点符号组成的有效序列。在计算机二级C++考试中,运算符与表达式是语法基础中的核心模块,直接关系到后续流程控制、函数调用和类设计的理解深度。本章将系统梳理八大类运算符的规则、优先级与结合性,并通过案例拆解帮助你在实际编程中避免常见陷阱。
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这类表达式,应直接判定为未定义行为,不要试图猜测编译器行为。正确做法是将自增操作独立成语句。
i = i++ + 1 在部分编译器上可能得到意外结果,务必避免。
4.2 关系运算符与逻辑运算符
核心原理
关系运算符(>、<、>=、<=、==、!=)返回布尔值 true 或 false。逻辑运算符(&&、||、!)操作布尔值,支持短路求值:&& 左侧为假时右侧不执行,|| 左侧为真时右侧不执行。短路求值是C++的重要特性,常用于条件保护。
判断方法与操作流程
| 运算符 | 含义 | 示例 | 结果 |
|---|---|---|---|
| > | 大于 | 5 > 3 | true |
| == | 等于 | 5 == 5 | true |
| != | 不等于 | 5 != 3 | true |
| && | 逻辑与 | true && false | false |
| || | 逻辑或 | true || false | true |
| ! | 逻辑非 | !true | false |
实战场景
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。
掌握运算符的核心在于理解优先级表、结合性规则以及每种运算符的求值特性。建议在编写表达式时遵循“简洁优先、括号辅助”的原则,避免写出依赖复杂优先级规则的代码。考试中遇到表达式求值题,先画出优先级树,再逐步计算,可有效降低出错率。