条件语句
建议先阅读:06_运算符与表达式
原理
程序计数器与跳转指令
条件语句在 CPU 层面映射为条件跳转指令。编译器将 if-else 翻译为如下模式:
比较操作数 → cmp 指令设置标志寄存器
条件跳转 → je/jne/jg/jl 等条件跳转指令
分支代码 → 各分支的机器码块
跳出 → jmp 跳到合并点
分支预测(branch prediction)是 CPU 的重要优化:CPU 猜测跳转方向,提前加载指令流水线。频繁误预测会冲刷流水线,造成显著性能损失。编写对分支预测友好的代码(如将大概率分支放在 if 中)可提升性能。
switch 的跳转表
当 case 值密集连续时,编译器生成跳转表(jump table)——一个函数指针数组,switch 变量作为索引直接跳转,实现 O(1) 分支。若 case 值稀疏,编译器回退为 if-else 链(O(n) 比较)。
这就是 switch 只能接受整型/枚举的原因——需要连续的索引来构建跳转表。
短路求值与安全模式
&& 和 || 被编译为短路分支结构:
if (ptr != nullptr && *ptr > 0) // 空指针时 *ptr 不会被求值编译器不会假设求值顺序,而 && / || 从语言标准层面保证了这一点。
语法
if-else
if (score >= 90) {
std::cout << "A\n";
} else if (score >= 80) {
std::cout << "B\n";
} else if (score >= 60) {
std::cout << "C\n";
} else {
std::cout << "D\n";
}else if 链从上到下,首次匹配后跳过后续分支。注意:
else if并非独立关键字,而是else后接一个新的if。
switch-case
switch (day) {
case 1: case 2: case 3: case 4: case 5:
std::cout << "工作日\n";
break;
case 6: case 7:
std::cout << "周末\n";
break;
default:
std::cout << "无效\n";
break;
}| 要素 | 说明 |
|---|---|
| 表达式类型 | 整型、char、enum(不能是浮点、字符串) |
| case 值 | 编译时常量 |
| break | 跳出 switch;不写会穿透 |
[[fallthrough]] | C++17 标记有意穿透 |
C++17 if 初始化语句
// 变量作用域限制在 if-else 内
if (auto it = map.find(key); it != map.end()) {
std::cout << it->second << '\n';
} else {
std::cout << "未找到\n";
}常见陷阱
| 陷阱 | 错误示例 | 说明 |
|---|---|---|
| 赋值充当条件 | if (x = 5) | = 是赋值,== 才是比较 |
| 悬挂 else | 无花括号时 else 与最近的 if 配对 | 始终用 {} 包裹 |
| 空语句体 | if (x > 0); | 分号使 if 体为空 |
| 浮点等值比较 | if (a == 0.3) | 用 epsilon 范围判断 |
防御性写法:
if (5 == x)误写为if (5 = x)会编译错误,能及早发现 bug。
实践
安全模式(利用短路求值):
int arr[] = {1, 2, 3, 4, 5};
if (index >= 0 && index < 5 && arr[index] > 0) {
// 安全的边界检查+元素访问
}嵌套条件扁平化:将 if (a) { if (b) { ... } } 重构为 if (a && b) { ... } 减少嵌套层次。
力扣:
力扣: 简单条件判断题 (多条件判断)
力扣: 单条件选择题 (单条件选择)
力扣: 条件逻辑与条件输出题 (条件+输出)
AI 自检提示:询问 AI “if-else 链和 switch 在编译后的机器码层面有何不同,switch 的跳转表在什么条件下生成”。