编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • C语言入门精通

  • Cpp入门到精通

    • README
    • 入门教程

      • README
      • Cpp简史
      • 基础语法
      • 数据类型
      • 运算符
      • 复合类型
      • 流程语句
        • 6.1 程序流程结构
          • 6.1.1 综合案例与思考
        • 6.2 选择结构
          • 6.2.1 if语句
          • 6.2.2 单行格式if语句
          • 6.2.3 if-else语句
          • 6.2.4 多条件的if语句
          • 6.2.5 嵌套if语句
          • 6.2.6 三目运算符
          • 6.2.7 switch语句
          • 6.2.8 选择结构底层原理
          • 6.2.9 选择结构训练题
          • 6.2.10 综合案例与思考
        • 6.3 循环结构
          • 6.3.1 while循环语句
          • 6.3.2 do...while循环语句
          • 6.3.3 for循环语句
          • 6.3.4 嵌套循环
          • 6.3.5 循环结构底层原理
          • 6.3.6 循环结构训练题
          • 6.3.7 综合案例与思考
        • 6.4 跳转语句
          • 6.4.1 break语句
          • 6.4.2 continue语句
          • 6.4.3 goto语句
          • 6.4.4 跳转语句底层原理
          • 6.4.5 跳转语句训练题
          • 6.4.6 综合案例与思考
        • 6.5 综合案例:猜数字游戏
        • 6.6 思考题
        • 6.7 卷一改造增补:现代 C++ 流程控制三件套
          • 6.7.1 if / switch 带初始化语句(C++17)
          • 6.7.2 if constexpr(C++17):编译期分支
          • 6.7.3 结构化绑定(Structured Bindings, C++17)
          • 6.7.4 推荐阅读
        • 6.8 新手陷阱 Top 5
      • 函数
      • 指针引用
      • 类和对象
      • 继承多态
      • 内存模型
      • 动态内存
      • IO和文件
      • 异常处理
      • 线程和锁
      • STL模版
      • 预处理器
      • 特性图谱
    • 综合案例

    • 专栏博客

    • 开发技巧

  • Java入门精通

  • Go入门到精通

  • JavaScript入门

  • CodeX
  • Cpp入门到精通
  • 入门教程
杨充
2026-05-07
目录

流程语句

# 第 6 章 C++ 流程语句

# 目录介绍

  • 6.1 程序流程结构
    • 6.1.1 综合案例与思考
  • 6.2 选择结构
    • 6.2.1 if语句
    • 6.2.2 单行格式if语句
    • 6.2.3 if-else语句
    • 6.2.4 多条件的if语句
    • 6.2.5 嵌套if语句
    • 6.2.6 三目运算符
    • 6.2.7 switch语句
    • 6.2.8 选择结构底层原理
    • 6.2.9 选择结构训练题
    • 6.2.10 综合案例与思考
  • 6.3 循环结构
    • 6.3.1 while循环语句
    • 6.3.2 do...while循环语句
    • 6.3.3 for循环语句
    • 6.3.4 嵌套循环
    • 6.3.5 循环结构底层原理
    • 6.3.6 循环结构训练题
    • 6.3.7 综合案例与思考
  • 6.4 跳转语句
    • 6.4.1 break语句
    • 6.4.2 continue语句
    • 6.4.3 goto语句
    • 6.4.4 跳转语句底层原理
    • 6.4.5 跳转语句训练题
    • 6.4.6 综合案例与思考
  • 6.5 综合案例:猜数字游戏
  • 6.6 思考题

# 6.1 程序流程结构

C/C++支持最基本的三种程序运行结构:==顺序结构、选择结构、循环结构==

  • 顺序结构:程序按顺序执行,不发生跳转
  • 选择结构:依据条件是否满足,有选择的执行相应功能
  • 循环结构:依据条件是否满足,循环多次执行某段代码

# 6.1.1 综合案例与思考

综合案例:三种流程结构综合演示

#include <iostream>
using namespace std;

int main() {
    // 1. 顺序结构:按顺序执行
    cout << "=== 顺序结构 ===" << endl;
    int a = 10;
    int b = 20;
    int sum = a + b;
    cout << "a + b = " << sum << endl;  // 按顺序:声明->计算->输出

    // 2. 选择结构:根据条件分支
    cout << "\n=== 选择结构 ===" << endl;
    if (sum > 25) {
        cout << "sum大于25" << endl;
    } else {
        cout << "sum不大于25" << endl;
    }

    // 3. 循环结构:重复执行
    cout << "\n=== 循环结构 ===" << endl;
    int total = 0;
    for (int i = 1; i <= 10; ++i) {
        total += i;
    }
    cout << "1到10的和: " << total << endl;

    // 综合:用三种结构实现猜数字(简化版)
    cout << "\n=== 综合应用 ===" << endl;
    int secret = 7;    // 顺序:设定目标
    int guess = 1;
    while (guess <= 10) {       // 循环:逐个尝试
        if (guess == secret) {  // 选择:判断是否猜中
            cout << "猜中了!数字是 " << guess << endl;
            break;
        }
        guess++;
    }

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

案例知识融合:这个案例先分别演示三种基本流程结构(顺序执行→条件分支→循环迭代),然后用一个"猜数字"的综合示例展示三种结构如何协作——顺序初始化数据、循环逐个尝试、选择判断结果。

思考题:

  1. 理论上只用"顺序"和"选择"就能实现循环(通过goto或递归),那为什么还需要专门的循环结构?
  2. 任何程序都可以用这三种基本结构实现(结构化定理),你能解释为什么吗?
  3. 函数调用也会改变执行流程,它属于三种基本结构中的哪一种?还是一种独立的流程控制方式?

# 6.2 选择结构

# 6.2.1 if语句

作用:执行满足条件的语句

if语句的三种形式

  • 单行格式if语句
  • 多行格式if语句
  • 多条件的if语句

# 6.2.2 单行格式if语句

单行格式if语句:if(条件){ 条件满足执行的语句 }

示例:

int main() {
    //选择结构-单行if语句
    //输入一个分数,如果分数大于600分,视为考上一本大学,并在屏幕上打印
    int score = 0;
    cout << "请输入一个分数:" << endl;
    cin >> score;
    cout << "您输入的分数为: " << score << endl;
    //if语句
    //注意事项,在if判断语句后面,不要加分号
    if (score > 600) {
        cout << "我考上了一本大学!!!" << endl;
    }
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

注意:if条件表达式后不要加分号

# 6.2.3 if-else语句

多行格式if语句:if(条件){ 条件满足执行的语句 }else{ 条件不满足执行的语句 };

示例:

int main() {
    int score = 0;
    cout << "请输入考试分数:" << endl;
    cin >> score;
    if (score > 600) {
        cout << "我考上了一本大学" << endl;
    } else {
        cout << "我未考上一本大学" << endl;
    }
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11

# 6.2.4 多条件的if语句

多条件的if语句:if(条件1){ 条件1满足执行的语句 }else if(条件2){条件2满足执行的语句}... else{ 都不满足执行的语句}

示例:

int main() {
    int score = 0;
    cout << "请输入考试分数:" << endl;
    cin >> score;
    if (score > 600) {
        cout << "我考上了一本大学" << endl;
    } else if (score > 500) {
        cout << "我考上了二本大学" << endl;
    } else if (score > 400) {
        cout << "我考上了三本大学" << endl;
    } else {
        cout << "我未考上本科" << endl;
    }
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 6.2.5 嵌套if语句

嵌套if语句:在if语句中,可以嵌套使用if语句,达到更精确的条件判断

案例需求:

  • 提示用户输入一个高考考试分数,根据分数做如下判断
  • 分数如果大于600分视为考上一本,大于500分考上二本,大于400考上三本,其余视为未考上本科;
  • 在一本分数中,如果大于700分,考入北大,大于650分,考入清华,大于600考入人大。

示例:

int main() {
    int score = 0;
    cout << "请输入考试分数:" << endl;
    cin >> score;
    if (score > 600) {
        cout << "我考上了一本大学" << endl;
        if (score > 700) {
            cout << "我考上了北大" << endl;
        } else if (score > 650) {
            cout << "我考上了清华" << endl;
        } else {
            cout << "我考上了人大" << endl;
        }
    } else if (score > 500) {
        cout << "我考上了二本大学" << endl;
    } else if (score > 400) {
        cout << "我考上了三本大学" << endl;
    } else {
        cout << "我未考上本科" << endl;
    }
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 6.2.6 三目运算符

作用: 通过三目运算符实现简单的判断

语法:表达式1 ? 表达式2 :表达式3

解释:

如果表达式1的值为真,执行表达式2,并返回表达式2的结果;

如果表达式1的值为假,执行表达式3,并返回表达式3的结果。

示例:

int main() {
    int a = 10;
    int b = 20;
    int c = 0;
    c = a > b ? a : b;
    cout << "c = " << c << endl;
    //C++中三目运算符返回的是变量,可以继续赋值
    (a > b ? a : b) = 100;
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << "c = " << c << endl;
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

总结:和if语句比较,三目运算符优点是短小整洁,缺点是如果用嵌套,结构不清晰

# 6.2.7 switch语句

作用:执行多条件分支语句

语法:

switch(表达式){
	case 结果1:执行语句;break;
	case 结果2:执行语句;break;
	...
	default:执行语句;break;
}
1
2
3
4
5
6

示例:

int main() {
    //请给电影评分
    //10 ~ 9   经典
    // 8 ~ 7   非常好
    // 6 ~ 5   一般
    // 5分以下 烂片
    int score = 0;
    cout << "请给电影打分" << endl;
    cin >> score;
    switch (score) {
        case 10:
        case 9:
            cout << "经典" << endl;
            break;
        case 8:
            cout << "非常好" << endl;
            break;
        case 7:
        case 6:
            cout << "一般" << endl;
            break;
        default:
            cout << "烂片" << endl;
            break;
    }
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

注意1:switch语句中表达式类型只能是整型或者字符型

注意2:case里如果没有break,那么程序会一直向下执行

总结:与if语句比,对于多条件判断时,switch的结构清晰,执行效率高,缺点是switch不可以判断区间

# 6.2.8 选择结构底层原理

if-else 的汇编翻译:编译器将 if-else 翻译成条件跳转指令。CPU 先用 CMP 指令做比较(本质是减法),然后根据标志寄存器的值决定是否跳转。

; if (x > 10) 的汇编伪码
    mov eax, [x]       ; 加载x的值
    cmp eax, 10         ; 比较x和10(执行 x - 10)
    jle ELSE_BRANCH     ; 如果 x <= 10,跳转到else分支
    ; ... if分支的代码 ...
    jmp END_IF          ; 跳过else分支
ELSE_BRANCH:
    ; ... else分支的代码 ...
END_IF:
1
2
3
4
5
6
7
8
9

疑惑:if-else 和 switch 在底层有什么区别?switch 真的更快吗?

答疑:对于少量分支(2-3个),if-else 和 switch 性能几乎没有区别,编译器可能生成相同的代码。但当 case 数量较多且值连续时,编译器会将 switch 优化为跳转表(Jump Table)。

论证:跳转表的原理是用 case 值作为数组索引,直接查表跳转,时间复杂度是 O(1),而 if-else 链是逐个比较,最坏 O(n)。

; switch 跳转表伪码(case 0, 1, 2, 3)
    mov eax, [choice]       ; 加载switch变量
    cmp eax, 3              ; 检查是否超出范围
    ja  DEFAULT             ; 超出则跳转到default
    jmp [JUMP_TABLE + eax*8] ; 直接查表跳转!O(1)

JUMP_TABLE:
    dq CASE_0, CASE_1, CASE_2, CASE_3  ; 跳转地址表
1
2
3
4
5
6
7
8

结果展示:当 case 值稀疏(如 1, 100, 10000)时,编译器不会生成跳转表(太浪费内存),而是用二分查找或退化为 if-else 链。所以 switch 的性能优势取决于 case 值的分布。你可以用 g++ -S 生成汇编代码来验证。

三目运算符的底层优化:

三目运算符 a ? b : c 在底层和 if-else 是等价的,编译器会生成相同的条件跳转。但现代 CPU 有一个特殊指令——条件移动(CMOVcc),可以在不跳转的情况下完成选择:

; int result = (x > 0) ? x : -x;
    mov  eax, [x]
    mov  ebx, eax
    neg  ebx            ; ebx = -x
    cmp  eax, 0
    cmovle eax, ebx     ; 如果 x <= 0,则 eax = -x(无跳转!)
1
2
3
4
5
6

条件移动避免了分支预测失败的惩罚(现代CPU中,分支预测失败可能浪费10-20个时钟周期),因此在某些性能关键路径中,三目运算符可能比 if-else 更快。

# 6.2.9 选择结构训练题

训练1:编写一个程序,使用 if-else 实现一个分数等级转换器。输入分数(0-100),输出等级(A/B/C/D/F),并统计各等级的分数段:

#include <iostream>
using namespace std;

int main() {
    int scores[] = {95, 87, 72, 63, 45, 88, 91, 55, 76, 82};
    int countA = 0, countB = 0, countC = 0, countD = 0, countF = 0;
    
    for (int score : scores) {
        char grade;
        if (score >= 90) { grade = 'A'; countA++; }
        else if (score >= 80) { grade = 'B'; countB++; }
        else if (score >= 70) { grade = 'C'; countC++; }
        else if (score >= 60) { grade = 'D'; countD++; }
        else { grade = 'F'; countF++; }
        cout << score << " -> " << grade << endl;
    }
    
    cout << "\n统计: A=" << countA << " B=" << countB 
         << " C=" << countC << " D=" << countD << " F=" << countF << endl;
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

然后思考:如果把 if-else 链改成 switch(switch(score / 10)),代码会更简洁吗?性能会更好吗?

训练2:编写一个简易命令行计算器,使用 switch 处理运算符:

#include <iostream>
using namespace std;

int main() {
    double num1 = 10, num2 = 3;
    char ops[] = {'+', '-', '*', '/', '%'};
    
    for (char op : ops) {
        cout << num1 << " " << op << " " << num2 << " = ";
        switch (op) {
            case '+': cout << num1 + num2; break;
            case '-': cout << num1 - num2; break;
            case '*': cout << num1 * num2; break;
            case '/':
                if (num2 != 0) cout << num1 / num2;
                else cout << "除零错误";
                break;
            case '%':
                cout << static_cast<int>(num1) % static_cast<int>(num2);
                break;
            default: cout << "不支持的运算符";
        }
        cout << endl;
    }
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

思考:如果不写 break,会发生 fall-through。C++17 引入了 [[fallthrough]] 属性,它的作用是什么?

训练3:比较 if-else 和 switch 的编译结果。编写以下两段代码,用 g++ -S -O2 生成汇编,观察差异:

// 版本A:if-else
int classify_if(int x) {
    if (x == 0) return 10;
    else if (x == 1) return 20;
    else if (x == 2) return 30;
    else if (x == 3) return 40;
    else return 50;
}

// 版本B:switch
int classify_switch(int x) {
    switch (x) {
        case 0: return 10;
        case 1: return 20;
        case 2: return 30;
        case 3: return 40;
        default: return 50;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

观察:编译器在 -O2 优化下,是否对两者生成了相同的代码?switch 版本是否使用了跳转表?

# 6.2.10 综合案例与思考

综合案例:用选择结构实现简易菜单系统

#include <iostream>
#include <string>
using namespace std;

int main() {
    int choice;
    cout << "======= 学生管理系统 =======" << endl;
    cout << "1. 查看成绩" << endl;
    cout << "2. 修改成绩" << endl;
    cout << "3. 成绩统计" << endl;
    cout << "4. 退出系统" << endl;
    cout << "请选择(1-4): ";
    cin >> choice;

    // if-else多条件判断
    if (choice == 1) {
        cout << "--- 成绩列表 ---" << endl;
        int scores[] = {85, 92, 78, 96};
        string names[] = {"张三", "李四", "王五", "赵六"};
        for (int i = 0; i < 4; ++i) {
            string level;
            // 嵌套if:成绩等级判断
            if (scores[i] >= 90) level = "优秀";
            else if (scores[i] >= 80) level = "良好";
            else if (scores[i] >= 60) level = "及格";
            else level = "不及格";
            cout << names[i] << ": " << scores[i] << " (" << level << ")" << endl;
        }
    } else if (choice == 2) {
        cout << "请输入学生编号和新成绩: ";
        // 三目运算符:快速判断
        int id, newScore;
        cin >> id >> newScore;
        string result = (newScore >= 0 && newScore <= 100) ? "修改成功" : "分数无效";
        cout << result << endl;
    } else if (choice == 3) {
        // switch语句:统计方式选择
        cout << "统计方式: 1.平均分 2.最高分 3.最低分" << endl;
        int mode;
        cin >> mode;
        int scores[] = {85, 92, 78, 96};
        switch (mode) {
            case 1: {
                int sum = 0;
                for (int s : scores) sum += s;
                cout << "平均分: " << sum / 4.0 << endl;
                break;
            }
            case 2:
                cout << "最高分: 96" << endl;
                break;
            case 3:
                cout << "最低分: 78" << endl;
                break;
            default:
                cout << "无效选择" << endl;
        }
    } else {
        cout << "退出系统,再见!" << endl;
    }

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

案例知识融合:这个案例通过一个菜单系统综合使用了所有选择结构——if-else多条件分支(菜单选择)、嵌套if(成绩等级判断)、三目运算符(快速校验)、switch语句(统计方式选择)。展示了不同选择结构各自最适合的使用场景。

思考题:

  1. if-else链和switch语句都能实现多分支选择,它们在性能上有差异吗?编译器对switch做了什么优化?
  2. C++17引入了if语句的初始化器(如if (int x = getValue(); x > 0)),这种语法有什么好处?
  3. 在实际项目中,当分支非常多时(如几十个case),除了switch还有什么更好的设计方式?(提示:考虑函数指针表或策略模式)

# 6.3 循环结构

# 6.3.1 while循环语句

作用:满足循环条件,执行循环语句

语法:while(循环条件){ 循环语句 }

解释:==只要循环条件的结果为真,就执行循环语句==

示例:

int main() {
    // 局部变量声明
    int a = 10;
    // while 循环执行
    while (a < 20) {
        cout << "a 的值:" << a << endl;
        a++;
    }
    return 0;
}
1
2
3
4
5
6
7
8
9
10

注意:在执行循环语句时候,程序必须提供跳出循环的出口,否则出现死循环

# 6.3.2 do...while循环语句

作用:满足循环条件,执行循环语句

语法:do{ 循环语句 } while(循环条件);

注意:与while的区别在于==do...while会先执行一次循环语句==,再判断循环条件

示例:

int main() {
    // 局部变量声明
    int a = 10;
    // do 循环执行
    do {
        cout << "a 的值:" << a << endl;
        a = a + 1;
    } while (a < 20);
    return 0;
}
1
2
3
4
5
6
7
8
9
10

总结:与while循环区别在于,do...while先执行一次循环语句,再判断循环条件

# 6.3.3 for循环语句

作用: 满足循环条件,执行循环语句

语法:for(起始表达式;条件表达式;末尾循环体) { 循环语句; }

示例:

int main() {
    // for 循环执行
    for (int a = 10; a < 20; a = a + 1) {
        cout << "a 的值:" << a << endl;
    }
	return 0;
}
1
2
3
4
5
6
7

注意:for循环中的表达式,要用分号进行分隔

总结:while , do...while, for都是开发中常用的循环语句,for循环结构比较清晰,比较常用

# 6.3.4 嵌套循环

作用: 在循环体中再嵌套一层循环,解决一些实际问题

示例:

int main() {
	//外层循环执行1次,内层循环执行1轮
	for (int i = 0; i < 10; i++){
		for (int j = 0; j < 10; j++){
			cout << "*" << " ";
		}
		cout << endl;
	}
	return 0;
}
1
2
3
4
5
6
7
8
9
10

# 6.3.5 循环结构底层原理

循环的汇编本质:所有循环在底层都是 条件跳转 + 无条件跳转 的组合。CPU 没有"循环"的概念,只有"跳转"。

while 循环的汇编翻译:

; while (i < n) { body; i++; }
LOOP_START:
    cmp [i], [n]         ; 比较 i 和 n
    jge LOOP_END         ; 如果 i >= n,跳出循环
    ; ... 循环体代码 ...
    inc [i]              ; i++
    jmp LOOP_START       ; 无条件跳回循环开始
LOOP_END:
1
2
3
4
5
6
7
8

for 循环和 while 循环在底层完全等价:for (init; cond; step) { body; } 等价于 init; while (cond) { body; step; }。编译器生成相同的机器码。

do-while 循环的特殊优化:do-while 的汇编比 while 少一条跳转指令,因为它把条件判断放在循环末尾:

; do { body; i++; } while (i < n);
LOOP_START:
    ; ... 循环体代码 ...
    inc [i]              ; i++
    cmp [i], [n]         ; 比较
    jl LOOP_START        ; 如果 i < n,跳回循环开始
; 注意:没有开头的无条件跳转!
1
2
3
4
5
6
7

疑惑:编译器的循环优化有多强?

答疑:现代编译器(GCC/Clang/MSVC)在 -O2 以上会进行多种循环优化,其效果可能超出你的想象。

论证:常见的循环优化技术包括:

  1. 循环展开(Loop Unrolling):将循环体复制多次,减少循环开销
// 原始代码
for (int i = 0; i < 100; i++) {
    arr[i] = arr[i] * 2;
}

// 编译器展开后(伪代码)
for (int i = 0; i < 100; i += 4) {
    arr[i]   = arr[i]   * 2;
    arr[i+1] = arr[i+1] * 2;
    arr[i+2] = arr[i+2] * 2;
    arr[i+3] = arr[i+3] * 2;
}
1
2
3
4
5
6
7
8
9
10
11
12
  1. 循环不变量外提(Loop-Invariant Code Motion):将循环中不变的计算移到循环外

  2. 强度削减(Strength Reduction):将循环中的乘法替换为加法

// 原始代码
for (int i = 0; i < n; i++) {
    arr[i * 4] = 0;  // 每次乘法
}

// 优化后
int offset = 0;
for (int i = 0; i < n; i++) {
    arr[offset] = 0;  // 加法替代乘法
    offset += 4;
}
1
2
3
4
5
6
7
8
9
10
11
  1. 自动向量化(Auto-Vectorization):利用 SIMD 指令一次处理多个数据

结果展示:你可以用 g++ -O2 -fopt-info-vec-optimized 查看编译器的向量化报告,了解哪些循环被自动优化了。在某些场景中,编译器优化后的代码甚至比手写汇编更快,因为编译器能全局分析代码的数据依赖关系。

# 6.3.6 循环结构训练题

训练1:使用三种循环分别计算斐波那契数列的前20项,对比代码风格:

#include <iostream>
using namespace std;

int main() {
    // while版本
    cout << "=== while ===" << endl;
    int a = 0, b = 1, count = 0;
    while (count < 20) {
        cout << a << " ";
        int temp = a + b;
        a = b;
        b = temp;
        count++;
    }
    cout << endl;
    
    // for版本
    cout << "=== for ===" << endl;
    a = 0; b = 1;
    for (int i = 0; i < 20; i++) {
        cout << a << " ";
        int temp = a + b;
        a = b;
        b = temp;
    }
    cout << endl;
    
    // do-while版本
    cout << "=== do-while ===" << endl;
    a = 0; b = 1; count = 0;
    do {
        cout << a << " ";
        int temp = a + b;
        a = b;
        b = temp;
        count++;
    } while (count < 20);
    cout << endl;
    
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

思考:三种写法产生完全相同的输出,但哪种最适合这个场景?为什么?

训练2:用嵌套循环打印杨辉三角的前10行:

#include <iostream>
#include <iomanip>
using namespace std;

int main() {
    const int rows = 10;
    int triangle[rows][rows] = {0};
    
    for (int i = 0; i < rows; i++) {
        triangle[i][0] = 1;  // 每行第一个元素为1
        triangle[i][i] = 1;  // 每行最后一个元素为1
        for (int j = 1; j < i; j++) {
            triangle[i][j] = triangle[i-1][j-1] + triangle[i-1][j];
        }
    }
    
    // 格式化输出
    for (int i = 0; i < rows; i++) {
        cout << string((rows - i) * 2, ' ');  // 前导空格
        for (int j = 0; j <= i; j++) {
            cout << setw(4) << triangle[i][j];
        }
        cout << endl;
    }
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

思考:这段代码的时间复杂度是多少?如果只需要第n行的值,能否不用二维数组?

训练3:编写一个判断素数的程序,并用循环优化找出1-1000内的所有素数:

#include <iostream>
#include <cmath>
using namespace std;

bool isPrime(int n) {
    if (n < 2) return false;
    if (n < 4) return true;
    if (n % 2 == 0 || n % 3 == 0) return false;
    // 优化:只检查 6k±1 形式的因子
    for (int i = 5; i <= sqrt(n); i += 6) {
        if (n % i == 0 || n % (i + 2) == 0)
            return false;
    }
    return true;
}

int main() {
    int count = 0;
    for (int i = 2; i <= 1000; i++) {
        if (isPrime(i)) {
            cout << i << " ";
            count++;
        }
    }
    cout << "\n共 " << count << " 个素数" << endl;
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

思考:isPrime 中为什么只需要检查到 sqrt(n)?6k±1 优化的原理是什么?

# 6.3.7 综合案例与思考

综合案例:用循环解决实际问题集

#include <iostream>
#include <cmath>
using namespace std;

int main() {
    // 1. while循环:输入验证(至少执行到输入合法)
    cout << "=== while: 输入验证 ===" << endl;
    int input;
    cout << "请输入1-100的数字: ";
    cin >> input;
    while (input < 1 || input > 100) {
        cout << "输入无效,请重新输入: ";
        cin >> input;
    }
    cout << "你输入了: " << input << endl;

    // 2. do-while循环:至少执行一次的菜单
    cout << "\n=== do-while: 菜单循环 ===" << endl;
    int choice;
    do {
        cout << "1.计算 2.查询 0.退出 >> ";
        cin >> choice;
        if (choice == 1) cout << "执行计算..." << endl;
        else if (choice == 2) cout << "执行查询..." << endl;
    } while (choice != 0);
    cout << "已退出" << endl;

    // 3. for循环:经典数学问题
    cout << "\n=== for: 求阶乘 ===" << endl;
    int n = 10;
    long long factorial = 1;
    for (int i = 1; i <= n; ++i) {
        factorial *= i;
    }
    cout << n << "! = " << factorial << endl;

    // 4. 范围for循环(C++11)
    cout << "\n=== 范围for ===" << endl;
    int arr[] = {3, 1, 4, 1, 5, 9};
    int maxVal = arr[0];
    for (int val : arr) {
        if (val > maxVal) maxVal = val;
    }
    cout << "最大值: " << maxVal << endl;

    // 5. 嵌套循环:九九乘法表
    cout << "\n=== 嵌套循环: 乘法表 ===" << endl;
    for (int i = 1; i <= 9; ++i) {
        for (int j = 1; j <= i; ++j) {
            cout << j << "×" << i << "=" << i * j << "\t";
        }
        cout << endl;
    }

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

案例知识融合:这个案例展示了每种循环的最佳使用场景——while用于"先判断后执行"的输入验证、do-while用于"至少执行一次"的菜单循环、for用于已知次数的数学计算、范围for用于遍历容器/数组、嵌套循环用于二维问题(九九乘法表)。

思考题:

  1. while(true)无限循环+内部break退出 vs do-while(condition) 循环,你更倾向于用哪种方式?各有什么优缺点?
  2. C++11的范围for循环(for (auto x : container))和传统for循环相比有什么优势?什么场景下不能使用范围for?
  3. 循环中应该把不变的计算放在循环外(循环不变量外提),编译器的-O2优化能自动做到这点吗?

# 6.4 跳转语句

# 6.4.1 break语句

作用: 用于跳出==选择结构==或者==循环结构==

break使用的时机:

  • 出现在switch条件语句中,作用是终止case并跳出switch
  • 出现在循环语句中,作用是跳出当前的循环语句
  • 出现在嵌套循环中,跳出最近的内层循环语句

示例1:在switch 语句中使用break

int main() {
    cout << "请选择您挑战副本的难度:" << endl;
    cout << "1、普通" << endl;
    cout << "2、中等" << endl;
    cout << "3、困难" << endl;
    int num = 0;
    cin >> num;
    switch (num) {
        case 1:
            cout << "您选择的是普通难度" << endl;
            break;
        case 2:
            cout << "您选择的是中等难度" << endl;
            break;
        case 3:
            cout << "您选择的是困难难度" << endl;
            break;
    }
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

示例2:在循环语句中用break

int main() {
    for (int i = 0; i < 10; i++) {
        if (i == 5) {
            break; //跳出循环语句
        }
        cout << i << endl;
    }
    return 0;
}
1
2
3
4
5
6
7
8
9

示例3:在嵌套循环语句中使用break,退出内层循环

int main() {
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 10; j++) {
            if (j == 5) {
                break;
            }
            cout << "*" << " ";
        }
        cout << endl;
    }
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12

# 6.4.2 continue语句

作用: 在==循环语句==中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环

示例:

int main() {
    for (int i = 0; i < 100; i++) {
        if (i % 2 == 0) {
            continue;
        }
        cout << i << endl;
    }
    return 0;
}
1
2
3
4
5
6
7
8
9

注意:continue并没有使整个循环终止,而break会跳出循环

# 6.4.3 goto语句

作用: 可以无条件跳转语句

语法: goto 标记;

解释: 如果标记的名称存在,执行到goto语句时,会跳转到标记的位置

示例:

int main() {
    cout << "1" << endl;
    goto FLAG;
    cout << "2" << endl;
    cout << "3" << endl;
    cout << "4" << endl;
    FLAG:
    cout << "5" << endl;
    return 0;
}
//1
//5
1
2
3
4
5
6
7
8
9
10
11
12

注意:在程序中不建议使用goto语句,以免造成程序流程混乱

# 6.4.4 跳转语句底层原理

break 和 continue 的汇编本质:break 翻译为一条无条件跳转指令 jmp,跳转目标是循环结束后的第一条指令;continue 也是 jmp,跳转目标是循环的条件判断处(for循环则跳到步进表达式)。

; for (int i = 0; i < n; i++) {
;     if (condition) continue;
;     if (condition2) break;
;     body;
; }

LOOP_START:
    cmp [i], [n]
    jge LOOP_END            ; 循环条件不满足则退出
    ; if (condition) continue
    test [condition], 1
    jnz STEP                ; continue → 跳到步进
    ; if (condition2) break
    test [condition2], 1
    jnz LOOP_END            ; break → 跳到循环后
    ; body
STEP:
    inc [i]                 ; 步进
    jmp LOOP_START
LOOP_END:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

goto 的汇编实现:goto 是最"诚实"的语句——它直接对应一条 jmp 指令,没有任何附加逻辑。这就是为什么 goto 既强大又危险:它可以跳转到函数内的任何位置,完全绕过正常的流程控制。

疑惑:goto 真的那么可怕吗?为什么 Linux 内核大量使用 goto?

答疑:goto 在结构化编程中被批评,是因为滥用 goto 会导致"意大利面条代码"——跳来跳去无法追踪执行流。但 goto 本身只是一个工具。

论证:Linux 内核中 goto 的典型用法是错误处理和资源清理:

int doSomething() {
    int* buf1 = new int[100];
    if (!buf1) goto fail1;
    
    int* buf2 = new int[200];
    if (!buf2) goto fail2;
    
    int* buf3 = new int[300];
    if (!buf3) goto fail3;
    
    // ... 正常逻辑 ...
    
    delete[] buf3;
    delete[] buf2;
    delete[] buf1;
    return 0;
    
fail3: delete[] buf2;
fail2: delete[] buf1;
fail1: return -1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

结果展示:这种"向后 goto"模式清晰表达了资源释放的逆序关系,避免了深层嵌套的 if-else。当然在现代 C++ 中,我们有 RAII(资源获取即初始化)和智能指针来替代这种模式,但在 C 语言和内核编程中,goto 的错误处理模式仍然是最佳实践。

C++中更好的替代方案:使用 RAII 和析构函数自动释放资源,彻底消除手动清理的需求:

#include <memory>
#include <vector>

int doSomethingModern() {
    auto buf1 = std::make_unique<int[]>(100);
    auto buf2 = std::make_unique<int[]>(200);
    auto buf3 = std::make_unique<int[]>(300);
    // 如果任何分配失败,前面的unique_ptr会自动释放
    // 函数结束时,所有资源自动释放
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11

# 6.4.5 跳转语句训练题

训练1:实现一个文本搜索程序,在字符串数组中搜索目标字符串,找到后用 break 提前退出:

#include <iostream>
#include <string>
using namespace std;

int main() {
    string words[] = {"apple", "banana", "cherry", "date", "elderberry",
                      "fig", "grape", "honeydew", "kiwi", "lemon"};
    int size = sizeof(words) / sizeof(words[0]);
    string target = "fig";
    
    int foundIndex = -1;
    int comparisons = 0;
    for (int i = 0; i < size; i++) {
        comparisons++;
        if (words[i] == target) {
            foundIndex = i;
            break;  // 找到后立即退出
        }
    }
    
    if (foundIndex >= 0)
        cout << "找到 \"" << target << "\" 在位置 " << foundIndex 
             << ",比较了 " << comparisons << " 次" << endl;
    else
        cout << "未找到,比较了 " << comparisons << " 次" << endl;
    
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

思考:如果不用 break,而是用标志变量控制循环退出,代码会怎样?哪种方式更清晰?

训练2:使用 continue 实现一个数据清洗程序,跳过无效数据:

#include <iostream>
#include <vector>
using namespace std;

int main() {
    // 原始数据中混有无效值(负数和超大值)
    vector<int> rawData = {23, -1, 45, 67, -5, 89, 999, 12, -3, 56, 78, 1000, 34};
    
    vector<int> cleanData;
    int skipped = 0;
    
    for (int val : rawData) {
        // 跳过无效数据
        if (val < 0 || val > 100) {
            skipped++;
            continue;
        }
        cleanData.push_back(val);
    }
    
    cout << "原始数据: " << rawData.size() << " 个" << endl;
    cout << "有效数据: " << cleanData.size() << " 个" << endl;
    cout << "跳过: " << skipped << " 个" << endl;
    
    double sum = 0;
    for (int val : cleanData) sum += val;
    cout << "有效数据平均值: " << sum / cleanData.size() << endl;
    
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

思考:continue 适合在什么场景使用?如果过滤条件很复杂(多个条件),用 continue 还是 if-else 包裹更好?

训练3:不使用 goto,实现一个跳出多层嵌套循环的搜索:

#include <iostream>
using namespace std;

int main() {
    // 在二维数组中搜索目标值
    int matrix[5][5] = {
        {1,  2,  3,  4,  5},
        {6,  7,  8,  9,  10},
        {11, 12, 13, 14, 15},
        {16, 17, 18, 19, 20},
        {21, 22, 23, 24, 25}
    };
    int target = 18;
    
    // 方法1:标志变量
    bool found = false;
    for (int i = 0; i < 5 && !found; i++) {
        for (int j = 0; j < 5 && !found; j++) {
            if (matrix[i][j] == target) {
                cout << "方法1: 找到 " << target << " 在 [" << i << "][" << j << "]" << endl;
                found = true;
            }
        }
    }
    
    // 方法2:封装成函数,用return跳出
    // (此处省略函数定义,自行实现)
    
    // 方法3:C++11 lambda + return
    [&]() {
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 5; j++) {
                if (matrix[i][j] == target) {
                    cout << "方法3: 找到 " << target << " 在 [" << i << "][" << j << "]" << endl;
                    return;  // 从lambda返回,等效于跳出所有循环
                }
            }
        }
    }();
    
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

思考:三种跳出多层循环的方式(标志变量、封装函数、lambda+return)各有什么优缺点?你最喜欢哪种?

# 6.4.6 综合案例与思考

综合案例:跳转语句在实际场景中的应用

#include <iostream>
#include <vector>
using namespace std;

int main() {
    // 1. break:在查找到目标后立即退出
    cout << "=== break: 查找元素 ===" << endl;
    vector<int> data = {15, 23, 7, 42, 8, 56, 31};
    int target = 42;
    bool found = false;
    for (int i = 0; i < data.size(); ++i) {
        if (data[i] == target) {
            cout << "找到 " << target << " 在位置 " << i << endl;
            found = true;
            break;  // 找到后不需要继续遍历
        }
    }
    if (!found) cout << "未找到" << endl;

    // 2. continue:跳过不满足条件的元素
    cout << "\n=== continue: 过滤处理 ===" << endl;
    cout << "偶数: ";
    for (int i = 1; i <= 20; ++i) {
        if (i % 2 != 0) continue;  // 跳过奇数
        cout << i << " ";
    }
    cout << endl;

    // 3. break跳出嵌套循环(只跳一层)
    cout << "\n=== break在嵌套循环中 ===" << endl;
    for (int i = 0; i < 5; ++i) {
        for (int j = 0; j < 5; ++j) {
            if (j == 3) break;  // 只跳出内层循环
            cout << "(" << i << "," << j << ") ";
        }
        cout << endl;
    }

    // 4. 用标志变量跳出多层循环(替代goto)
    cout << "\n=== 跳出多层循环 ===" << endl;
    bool shouldBreak = false;
    for (int i = 0; i < 10 && !shouldBreak; ++i) {
        for (int j = 0; j < 10 && !shouldBreak; ++j) {
            if (i * 10 + j == 25) {
                cout << "在i=" << i << ",j=" << j << "处退出" << endl;
                shouldBreak = true;
            }
        }
    }

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

案例知识融合:这个案例展示了跳转语句的实际应用——break在查找到目标后提前退出循环避免不必要的遍历、continue在过滤场景中跳过不满足条件的元素、break在嵌套循环中只跳出一层的特点,以及用标志变量替代goto跳出多层循环的优雅方式。

思考题:

  1. break只能跳出最内层循环,如果需要跳出多层嵌套循环,除了goto和标志变量,还有什么方法?(提示:可以把循环封装成函数)
  2. goto在C++中被认为是"不好的实践",但Linux内核代码中大量使用了goto做错误处理。你如何看待这个矛盾?
  3. continue和break看似简单,但在复杂循环中使用不当可能导致逻辑混乱。有什么最佳实践可以避免这种问题?

# 6.5 综合案例:猜数字游戏

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

int main() {
    srand(static_cast<unsigned>(time(nullptr)));
    int secret = rand() % 100 + 1;  // 1-100的随机数
    int guess, attempts = 0;
    const int maxAttempts = 7;
    
    cout << "=== 猜数字游戏 ===" << endl;
    cout << "我想了一个1-100之间的数字,你有" << maxAttempts << "次机会猜。" << endl;
    
    do {
        cout << "\n第 " << (attempts + 1) << " 次猜测: ";
        cin >> guess;
        attempts++;
        
        if (guess < 1 || guess > 100) {
            cout << "请输入1-100之间的数字!" << endl;
            continue;  // 无效输入不计入次数? 这里依然计入了
        }
        
        if (guess == secret) {
            cout << "恭喜你猜对了!用了 " << attempts << " 次。" << endl;
            // 评价
            switch (attempts) {
                case 1: cout << "评价:天才!一次就中!" << endl; break;
                case 2: case 3: cout << "评价:优秀!" << endl; break;
                case 4: case 5: cout << "评价:不错!" << endl; break;
                default: cout << "评价:再接再厉!" << endl;
            }
            break;
        } else if (guess < secret) {
            cout << "太小了!";
        } else {
            cout << "太大了!";
        }
        cout << "还剩 " << (maxAttempts - attempts) << " 次机会。" << endl;
        
    } while (attempts < maxAttempts);
    
    if (guess != secret) {
        cout << "\n很遗憾,次数用尽!答案是 " << secret << endl;
    }
    
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

案例知识融合:这个猜数字游戏综合运用了本章所有知识点——do-while循环确保至少执行一次、if-else if-else多条件判断大小关系、switch评价猜测次数、break在猜中后跳出循环、continue跳过无效输入、const定义最大尝试次数。是一个完整的流程控制综合实战。

# 6.6 思考题

  1. 编译器优化与流程控制:现代编译器在 -O2 优化级别下,会对 if-else 做分支预测提示优化。GCC 提供了 __builtin_expect 宏,Linux 内核封装为 likely()/unlikely() 宏。你了解分支预测对性能的影响吗?在什么场景下值得手动添加分支预测提示?

  2. 结构化编程的边界:Dijkstra 1968 年发表了著名的"Go To Statement Considered Harmful"论文,推动了结构化编程。但 C++ 至今保留了 goto。结合你对 RAII、异常处理和 goto 的理解,你认为在现代 C++ 中还有使用 goto 的合理场景吗?

  3. 循环 vs 递归:任何循环都可以改写为递归,反之亦然。在 C++ 中,循环通常比递归性能更好(因为函数调用有栈帧开销)。但函数式编程语言偏好递归。你认为在 C++ 中什么时候应该用递归而不是循环?(提示:考虑树遍历、分治算法等场景)

  4. C++20 的协程(Coroutine):C++20 引入了协程,它提供了一种新的流程控制方式——可以"暂停"和"恢复"函数执行。这打破了传统的"函数要么完整执行要么不执行"的模型。你了解协程能解决什么问题吗?它和传统的循环/条件分支有什么根本区别?


# 6.7 卷一改造增补:现代 C++ 流程控制三件套

本节为卷一新增。这三个特性看似小,却几乎出现在现代 C++ 项目的每一个函数里,是新手向「现代风格」过渡的分水岭。

# 6.7.1 if / switch 带初始化语句(C++17)

目的:把变量的作用域严格限制在条件块内,杜绝「悬挂变量」污染外层作用域。

// C++17 之前
auto it = m.find(key);
if (it != m.end()) { use(it->second); }
// it 在这里依然可见,容易被误用

// C++17:把变量塞进 if 的「初始化部分」
if (auto it = m.find(key); it != m.end()) {
    use(it->second);
}
// 出了 } 之后 it 就不存在了
1
2
3
4
5
6
7
8
9
10

switch 同样支持:

switch (auto code = parse(); code) {
    case 0: /* ... */ break;
    case -1: /* ... */ break;
}
1
2
3
4

# 6.7.2 if constexpr(C++17):编译期分支

目的:在模板里根据类型属性走不同分支,不匹配的分支根本不会被编译——彻底替代传统的 SFINAE 与 tag dispatch 黑魔法。

template <typename T>
auto serialize(const T& v) {
    if constexpr (std::is_arithmetic_v<T>) {
        return std::to_string(v);              // 数值类型走这里
    } else if constexpr (std::is_same_v<T, std::string>) {
        return v;                               // string 直接返回
    } else {
        return v.to_json();                     // 自定义类型调用成员函数
    }
}
1
2
3
4
5
6
7
8
9
10

普通 if 在模板里会要求所有分支都能编译,而 if constexpr 只编译被选中的分支——这就是它能调用 v.to_json() 而不出错的关键。

# 6.7.3 结构化绑定(Structured Bindings, C++17)

目的:一次性把聚合类型(pair/tuple/struct/数组)拆开成多个命名变量。

// 遍历 map:C++17 之前
for (auto it = m.begin(); it != m.end(); ++it) {
    cout << it->first << "=" << it->second;
}

// C++17:直接拆开
for (const auto& [key, value] : m) {
    cout << key << "=" << value;
}

// 接收多返回值
struct Result { int code; std::string msg; };
Result query();
auto [code, msg] = query();

// 配合 if-init 简直是绝配
if (auto [it, ok] = m.insert({k, v}); ok) {
    cout << "插入成功";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

注意三处坑:

  1. 默认是值拷贝,要修改原值用 auto& [k, v],要避免拷贝用 const auto& [k, v]。
  2. 绑定的名字不是引用,但底层会保持引用语义(编译器优化得很好)。
  3. C++17 还不能给绑定的名字加 [[maybe_unused]];C++26 起允许 auto& [_, v] = ...,C++17/20 中可用 [[maybe_unused]] auto&。

# 6.7.4 推荐阅读

  • 卷一 18.特性图谱 §C++17 部分
  • 卷一第 7 章 07.函数.md —— 多返回值与 tuple
  • 卷三《模板与泛型卷》—— if constexpr 在模板编程中的深度应用

# 6.8 新手陷阱 Top 5

# 陷阱 说明
1 if (a = b) 写错赋值 应为 if (a == b),开 -Wparentheses 警告
2 switch 漏 break 串档 故意 fallthrough 用 [[fallthrough]]; 标注(C++17)
3 浮点数循环步长 for (float f=0; f<1; f+=0.1) 永远到不了 1.0
4 范围 for 修改容器 循环中 push_back 导致迭代器失效
5 goto 跨过对象构造 编译报错;即使能编译也会破坏 RAII
上次更新: 2026/06/10, 11:13:41
复合类型
函数

← 复合类型 函数→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式