第四章 流程控制结构

0 阅读20分钟

第四章 流程控制结构

一、项目引入-猜数字游戏

1.1 先看成品

先看写完之后长什么样。答案先固定成 42,随机数留到 3.6 节。

#include <iostream>
using namespace std;

int main() {
    int answer = 42;
    int guess = 0;
    int count = 0;

    while (guess != answer) {
        cout << "猜一个 1 到 100 的数: ";
        cin >> guess;
        count++;

        if (guess > answer) {
            cout << "猜大了" << endl;
        } else if (guess < answer) {
            cout << "猜小了" << endl;
        } else {
            cout << "猜中了,一共猜了 " << count << " 次" << endl;
        }
    }
    return 0;
}

跑起来是这个节奏。

猜一个 1 到 100 的数: 50
猜大了
猜一个 1 到 100 的数: 25
猜小了
猜一个 1 到 100 的数: 37
猜小了
猜一个 1 到 100 的数: 44
猜大了
猜一个 1 到 100 的数: 40
猜小了
猜一个 1 到 100 的数: 42
猜中了,一共猜了 6 次

这个猜法有章法,先砍一半,再砍一半,六次收工。1 到 100 的范围,二分查找最多七次,这是算得出来的,二的七次方是一百二十八,装得下一百个可能。程序该干的事它都干了,读输入、比大小、报结果,猜中了自动停。注意两个细节,循环不知道要跑多少轮,猜中才停;每轮说的话不一样,看猜的数是大是小。这两件事,一个靠循环,一个靠选择。

┌──────────────────────────────────────────────────────────┐
│ 猜数字游戏走这几步                                       │
├──────────────────────────────────────────────────────────┤
│ 先想数,再循环猜,猜中了才结算                           │
│                                                          │
│ ┌────────────────────────────────────────────────────┐   │
│ │  开局      埋随机种子,生成 1 到 100 的一个数      │   │
│ └────────────────────────────────────────────────────┘   │
│                           │                              │
│                           v                              │
│ ┌────────────────────────────────────────────────────┐   │
│ │  进循环    提示玩家输入,读进一个整数              │   │
│ └────────────────────────────────────────────────────┘   │
│                           │                              │
│                           v                              │
│ ┌────────────────────────────────────────────────────┐   │
│ │  比大小    猜大了、猜小了、猜中了,三条路          │   │
│ └────────────────────────────────────────────────────┘   │
│                           │                              │
│                           v                              │
│ ┌────────────────────────────────────────────────────┐   │
│ │  跳出结算  报告猜了几次,程序收工                  │   │
│ └────────────────────────────────────────────────────┘   │
│                                                          │
│   猜大猜小都不退循环,接着提示,直到猜中为止             │
│   次数靠一个计数器,每猜一次加一                         │
└──────────────────────────────────────────────────────────┘

1.2 拆开看,就三样东西

把这段程序按行拆开,用到的知识摊开就这几样。

段落代码里的样子属于哪一块
准备answer guess count 三个变量第二章的老本
反复猜while 加一个条件循环结构
分岔提示if else if 三分支选择结构
猜中收工条件变假,循环自己退循环的出口

前两章的东西这一章全用得上。cin 读数,第二章学的变量累加,第三章学的输出格式化,都是现成的。真正新加的只有两样,选择结构和循环结构。这一章的正文就讲这两块,最后合起来,就是开头那个游戏。

选择结构回答的是该走哪条路。猜大了走大的提示,猜小了走小的提示,猜中了走结算。循环结构回答的是这件事要做几遍。读数、比较、提示,这一套要重复到猜中为止,写几遍都不对,要写成一个会自己转的圈。

1.3 没有流程控制之前

回头看表哥那十来个 if。他把程序写成了清单,猜 50 做什么,猜 51 做什么,一条条列。清单的问题在于长度固定,而游戏的长度不固定。有人五次猜中,有人六十次猜中,清单写不到那么长。

流程控制和清单的差别在这一点上。清单是照着走,走完拉倒;流程控制是会看情况,情况不变就接着转。while 那一行,条件是真的就再转一圈,是假的就出门,转几圈由玩游戏的人决定,不由写程序的人决定。

猜数字是个好教具。它够小,四十行,一下能看完;它也够全,选择、循环、跳转、输入输出全用上了。这一章把它拆开讲,下一章再把它拼回去。

二、选择结构

2.1 条件的积木

if 后面挂的是一个条件,条件由关系运算符和逻辑运算符搭出来。

关系运算符六个,都是老熟人。

运算符念法例子结果
<小于3 < 5真
大于3 > 5假
<=小于等于5 <= 5真
>=大于等于5 >= 6假
==等于5 == 5真
!=不等于5 != 5假

等于号是两个等号。一个等号是赋值,把右边的值塞进左边的变量,第二章说过。写成 if (x = 0) 是先把 x 改成 0,再拿 0 当条件,永远是假,这是新手头号事故,编译器多半还会补一个警告。

逻辑运算符三个,负责把多个条件拼起来。

运算符含义例子
&&并且,两边都真才真年龄够并且有余票
||或者,一边真就真是会员或者是学生
!取反,真变假不是空

判断一个数在 1 到 100 之间,要写两个条件用 && 连。

if (x >= 1 && x <= 100) {
    cout << "在范围内" << endl;
}

数学里 1 <= x <= 100 这么写很顺,C++ 里不行。运算从左往右,先算 1 <= x 得 0 或 1,再拿这个 0 或 1 去和 100 比,结果永远是 1,条件恒真。这个坑第三章踩过一次,混合比较翻车,同一个道理。

优先级上,关系运算符比逻辑运算符高,&& 比 || 高。拿不准就加括号,括号不要钱,还能让读代码的人少费脑子。

2.2 if 的三种形态

一个 if 有三种长法,分支一个比一个多。

┌────────────────────────────────────────────────────────┐
│ if 的三种形态                                          │
├────────────────────────────────────────────────────────┤
│ 一个 if,三种长法,分支越来越多                        │
│                                                        │
│ +--------------+------------------+----------------+   │
│ |形态          |长相              |什么时候用      |   │
│ +--------------+------------------+----------------+   │
│ |单分支        |if 加花括号       |条件成立才做    |   │
│ |双分支        |if 配 else        |二选一          |   │
│ |多分支        |if else if 链     |三个以上候选    |   │
│ +--------------+------------------+----------------+   │
│                                                        │
│   if 后面挂条件,成立进花括号,不成立跳过去            │
│   else 贴着上一个 if,中间别插别的语句                 │
└────────────────────────────────────────────────────────┘

单分支,条件成立才做,不成立就跳过去。

if (score >= 60) {
    cout << "及格了" << endl;
}

双分支,二选一,总有一边要执行。

if (num % 2 == 0) {
    cout << "偶数" << endl;
} else {
    cout << "奇数" << endl;
}

多分支,一串 if else if 挂下来,从第一个条件开始试,哪个中了走哪条。

if (score >= 90) {
    cout << "优" << endl;
} else if (score >= 80) {
    cout << "良" << endl;
} else if (score >= 60) {
    cout << "中" << endl;
} else {
    cout << "差" << endl;
}

这段代码有个顺序讲究。判断从大到小排,先问是不是优,不是再问良。写成从小到大就全乱,60 分先被中接走,90 分永远摸不到优的门。多分支的条件,谁严谁先。

else 配对是另一个事故点。else 找的是离它最近的、还没配对的 if。缩进好看不等于逻辑好看,下面这段看着是两层,其实是一层。

if (x > 0)
    if (y > 0)
        cout << "第一象限" << endl;
else
    cout << "x 不是正数" << endl;

编译器眼里,那个 else 属于内层 if。x 是负数的时候,内层 if 压根没执行,else 也没机会执行,屏幕干干净净。解决办法是花括号一律写上,哪怕只有一句话。这不是风格问题,是保命。

if (x > 0) {
    if (y > 0) {
        cout << "第一象限" << endl;
    }
} else {
    cout << "x 不是正数" << endl;
}

嵌套 if 的真实用例是二维判断。先分性别,再分年龄段,一层套一层,每层管一个维度。层数最多两层,三层以上就该换成 switch 或者查表,人读不动。

2.3 条件运算符

有一个场景很常见,两个值里挑大的。用 if else 写,先声明再摆一个判断,啰嗦。

int max;
if (a > b) {
    max = a;
} else {
    max = b;
}

条件运算符一行搞定,它是唯一的三目运算符,三个位置各就各位。

int max = (a > b) ? a : b;

问号前面是条件,真走问号后面,假走冒号后面。括号建议一直带着,它的优先级低得反常,混进复杂表达式里容易和赋值号搅在一起。这句话也说给自己,Debug 打多了就知道,一行能装下的聪明,常常是三天后看不懂的伏笔。

2.4 switch 与穿透

分支超过三个,if else if 链就开始显长。switch 是为这种场合生的,拿着一个值去对一堆标签。

int level = 2;
switch (level) {
    case 1:
        cout << "初级" << endl;
        break;
    case 2:
        cout << "中级" << endl;
        break;
    case 3:
        cout << "高级" << endl;
        break;
    default:
        cout << "等级不明" << endl;
}

执行的时候先看 switch 后面挂的值,这里是 2。从上往下找标签,找到 case 2,从那一行开始执行,打印中级,碰上 break,跳出 switch。default 是兜底,没有一个 case 对上的时候归它,写不写都行,建议写。

┌──────────────────────────────────────────────────────────┐
│ switch 是怎么跑的                                        │
├──────────────────────────────────────────────────────────┤
│ switch 拿着值去对标签,对上了从那儿开始跑                │
│                                                          │
│ ┌────────────────────────────────────────────────────┐   │
│ │  对标签    值等于哪个 case 的常量,就从哪行起跑    │   │
│ └────────────────────────────────────────────────────┘   │
│                           │                              │
│                           v                              │
│ ┌────────────────────────────────────────────────────┐   │
│ │  往下顺    没有 break 就一条条掉下去,这叫穿透     │   │
│ └────────────────────────────────────────────────────┘   │
│                           │                              │
│                           v                              │
│ ┌────────────────────────────────────────────────────┐   │
│ │  break     当场跳出 switch,后面的 case 不看       │   │
│ └────────────────────────────────────────────────────┘   │
│                                                          │
│   每个分支末尾都该有 break,忘了就是穿透事故             │
│   case 只能对整数,范围题交给 if else if                 │
└──────────────────────────────────────────────────────────┘

break 是这套机制里最容易忘的东西。忘了会怎样,看一眼穿透事故。

int level = 1;
switch (level) {
    case 1:
        cout << "初级" << endl;
    case 2:
        cout << "中级" << endl;
    case 3:
        cout << "高级" << endl;
    default:
        cout << "等级不明" << endl;
}

输入 1,屏幕上四行全出来了。case 1 执行完没有东西拦着,顺着掉进 case 2,再掉进 case 3,最后连 default 一起执行。这个行为叫穿透,标准规定如此,不是编译器抽风。有些场合故意用它,几个 case 共享一段代码,日常多数是忘写。判断标准很简单,写的时候有没有想过要穿透,没想过就是忘了。

switch 的规矩还有几条,都是硬性的。

规矩说明
只能对整数int char 枚举都行,字符串不行
标签是常量case 后面跟编译期定得下的数,不能是变量
标签不重复两个 case 1,编译直接拦
default 随便放放头放尾都行,习惯放最后

分支多且值是整数,用 switch;条件带范围,比如判断优良好中差,用 if else if。switch 对范围没有天赋,case 后面写 >= 90 直接编不过。

三、循环结构

3.1 while 先判后跑

while 的规矩一句话,条件是真的,身体就再跑一遍,每跑完一遍重新问一次。

int i = 1;
while (i <= 5) {
    cout << i << endl;
    i++;
}

屏幕上出来 1 到 5。注意 i++ 那一行,它让条件有变化。去掉它,i 永远是 1,条件永远为真,程序永远跑下去,这就是死循环。

死循环的样子很吓人,屏幕刷到看不见光标, Ctrl+C 按下去才停。第一次见容易慌,慌可以,别直接关窗口,死循环是本章必修课,见一次修一次就长记性。

最常见的死循环病根有两个。一个是条件里写赋值,while (flag = 1) 每次进来先把 flag 设成 1,再拿 1 当条件,恒真。编译器会给警告,大意是建议加括号确认是不是赋值,看见这个警告就当错误处理。另一个是忘了更新,计数器不走了,条件不动了,圈就不停了。

int flag = 1;
while (flag = 1) {   // 赋值,不是比较,恒真
    cout << "停不下来" << endl;
}

while 有个兄弟叫 do while,俩人的差别在先后。

┌──────────────────────────────────────────────────────┐
│ while 和 do while 差在哪                             │
├──────────────────────────────────────────────────────┤
│ 两种循环,判条件和跑身体的先后反过来                 │
│                                                      │
│ +--------------+----------------+----------------+   │
│ |循环          |先干什么        |身体最少跑几次  |   │
│ +--------------+----------------+----------------+   │
│ |while         |先判条件        |可能一次都不跑  |   │
│ |do while      |先跑身体        |至少跑一次      |   │
│ +--------------+----------------+----------------+   │
│                                                      │
│   循环进门先后不同,空转的表现就不同                 │
│   do while 的分号是语法,漏了报错位置发懵            │
└──────────────────────────────────────────────────────┘

3.2 do while 先跑后判

do while 把身体提到前头,条件挪到后头。翻译成人话,先跑一遍,再问还要不要跑。

int choice;
do {
    cout << "1 开始 2 设置 0 退出" << endl;
    cout << "选一个: ";
    cin >> choice;
} while (choice != 0);

菜单用 do while 正合适。菜单至少得显示一次,while 先问后跑,条件凑不巧就一次不显示,do while 没这个问题。输 0 退出,输别的继续显示,逻辑顺。

两条使用规矩。分号不能省,while (choice != 0); 这个分号是语法的一部分,漏了编译报错,报的位置还容易看懵。花括号里的身体和 while 是绑定的,break 一样管用。

三种循环的分工,一句话版本。次数知道用 for,次数看情况用 while,至少跑一次用 do while。

循环判条件时机身体最少几次典型场合
while进圈之前可能零次猜数字、读文件到结尾
do while跑完之后至少一次菜单、重试询问
for每轮之前可能零次计数、遍历、九九乘法表

3.3 for 为计数而生

while 什么圈都能转,计数的圈转起来啰嗦。定起点,判终点,往前迈一步,三件事摊在 while 里是这个样子。

int i = 1;
while (i <= 100) {
    cout << i << endl;
    i++;
}

for 把这三件事收进一行,一格放一样。

for (int i = 1; i <= 100; i++) {
    cout << i << endl;
}
┌────────────────────────────────────────────────────────┐
│ for 的三段各管什么                                     │
├────────────────────────────────────────────────────────┤
│ for 的三个格子各管一段,分号是格子之间的墙             │
│                                                        │
│ +------------+----------------------+--------------+   │
│ |格子        |管什么                |跑几次        |   │
│ +------------+----------------------+--------------+   │
│ |初始化      |定计数器的起点        |只跑一次      |   │
│ |条件        |决定还跑不跑          |每轮都跑      |   │
│ |更新        |计数器往前走一步      |每轮都跑      |   │
│ +------------+----------------------+--------------+   │
│                                                        │
│   三段都能省,分号不能省,省光就是死循环               │
│   计数器习惯用 i,嵌套里层用 j,别重用                 │
└────────────────────────────────────────────────────────┘

执行顺序得盯一眼。初始化只来一次,条件每轮都问,更新每轮都跑,三个格子的节奏不一样。

┌──────────────────────────────────────────────────────────┐
│ 一轮 for 按什么顺序走                                    │
├──────────────────────────────────────────────────────────┤
│ 一轮循环里,条件和更新隔着身体打招呼                     │
│                                                          │
│ ┌────────────────────────────────────────────────────┐   │
│ │  初始化    进屋前做一次,定好起点                  │   │
│ └────────────────────────────────────────────────────┘   │
│                           │                              │
│                           v                              │
│ ┌────────────────────────────────────────────────────┐   │
│ │  判条件    真的往下走,假的出门                    │   │
│ └────────────────────────────────────────────────────┘   │
│                           │                              │
│                           v                              │
│ ┌────────────────────────────────────────────────────┐   │
│ │  跑身体    该做的事写在这儿                        │   │
│ └────────────────────────────────────────────────────┘   │
│                           │                              │
│                           v                              │
│ ┌────────────────────────────────────────────────────┐   │
│ │  更新      计数器加一,再回去判条件                │   │
│ └────────────────────────────────────────────────────┘   │
│                                                          │
│   顺序是先判后跑再更新,更新完回到判断                   │
│   多跑一轮,多半是判断写成了小于等于                     │
└──────────────────────────────────────────────────────────┘

初学者最常栽在边界上。打印 1 到 10,条件写成 i <= 10 对,写成 i < 10 少一个,写成 i <= 11 多一个。拿不准的时候小刀割一下,先跑个 1 到 3,数一数,再放回去跑 1 到 100。多跑一轮少跑一轮的毛病,十个循环 bug 里占三个。

for 的三段都能省,分号不能省。省一段常见于计数器在别处初始化,省两段也有人写,三段全省就是 for (;;),和 while (true) 一个意思,死循环的正式写法。

for (int i = 0, j = 10; i < j; i++, j--) {
    cout << i << " " << j << endl;
}

这一段用了逗号表达式,初始化和更新各干两件事,两头往中间走,碰头为止。算是个花活,知道有就行,日常代码里见到的机会不多。

还有一条常考细节。for 里声明的变量,活动范围就在这个 for 里,出了圈就没了。C++ 一向是这样,C 语言要到 C99 才跟上,所以老代码里常见先声明再用的写法,两种都看得懂就行。

3.4 break 和 continue

圈里想提前撤,用 break。圈里想跳过这一轮,用 continue。两个都是本章的关键词,去的地方不一样。

┌────────────────────────────────────────────────────────┐
│ break 和 continue 的去向                               │
├────────────────────────────────────────────────────────┤
│ 两个跳语句,跳的高度不一样                             │
│                                                        │
│ +--------------+--------------------+--------------+   │
│ |语句          |跳去哪              |循环还跑吗    |   │
│ +--------------+--------------------+--------------+   │
│ |break         |直接出循环          |不跑了        |   │
│ |continue      |回条件判断          |接着跑下一轮  |   │
│ +--------------+--------------------+--------------+   │
│                                                        │
│   break 在 switch 里也认,跳出这一层 switch            │
│   嵌了两层循环,break 只跳最近的那一层                 │
└────────────────────────────────────────────────────────┘

看代码。1 到 10 挨个打,碰上 5 跳过,碰上 8 收工。

for (int i = 1; i <= 10; i++) {
    if (i == 5) {
        continue;
    }
    if (i == 8) {
        break;
    }
    cout << i << endl;
}

屏幕上挨个出来 1 2 3 4 6 7,六个数字。continue 跳的是本轮剩下的语句,跳完回到更新那一格,计数器照走;break 是整个圈不要了,更新都不回去。

break 只跳最近的一层,这个限定词别丢。想从里层一下撤到最外面,break 不帮忙,得靠一个标记变量加条件判断,那是后话,函数和返回值会给出更干净的解法。

3.5 嵌套循环与九九乘法表

圈里套圈,就是嵌套循环。外层走一步,内层跑一整轮,这个节奏是理解嵌套的唯一钥匙。

┌──────────────────────────────────────────┐
│ 嵌套循环谁先跑                           │
├──────────────────────────────────────────┤
│ 外层走一步,内层要跑完一整轮             │
│                                          │
│   外层 i 从 1 到 9,每走一步             │
│       │                                  │
│       v                                  │
│   内层 j 从 1 到 i,整整齐齐跑完         │
│       ├─ i 是 1 的时候,j 只跑 1 轮      │
│       ├─ i 是 9 的时候,j 跑 9 轮        │
│       └─ 总共 45 个等式,一行比一行长    │
│                                          │
│   内层跑完,外层才更新,不是同时往前跑   │
│   看不清的时候,两个计数器都打印出来看   │
└──────────────────────────────────────────┘

九九乘法表是嵌套循环的保留节目。九行,第一行一个等式,第九行九个,一共四十五个。用循环写,外层管行,内层管列。

for (int i = 1; i <= 9; i++) {
    for (int j = 1; j <= i; j++) {
        cout << j << "x" << i << "=" << i * j << "\t";
    }
    cout << endl;
}

内层的条件是 j <= i,不是 j <= 9。就这一个小于等于,决定了第一行只有一个等式,第九行有九个。改成 j <= 9,出来的是方阵,一行不落,三角形摊成正方形,能跑,但不是要的那个。

外层循环体里就两件事,内层循环加一个换行。内层负责把一行填满,外层负责换下一行。套第三层不是不行,是很少有人读得动,两层是人的舒适区上限。

嵌套循环最容易出的 bug 是计数器写重。两层都用 i,内层一轮把外层的 i 搅得天翻地覆,圈数全乱。规矩是外层 i 内层 j,第三层才轮到 k,别省这点字母。

3.6 随机数与完整版

固定答案 42 的游戏玩第二轮就没意思了,答案得会变。变之前先知道,计算机里的随机是假随机,由数学公式一批批算出来,起点相同算出来的一串数完全相同。

rand 函数从 <cstdlib> 来,每次调用给一个数,范围 0 到 RAND_MAX。RAND_MAX 是个常量,标准只要求不小于 32767,Windows 上常见就是 32767,Linux 和 macOS 上常是二十多亿。不播种,每次运行 rand 吐的数一个样,程序昨天今天一个答案。

srand 负责播种,配 time 拿当前时刻当种子,每次跑都不一样。

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

int main() {
    srand((unsigned int)time(nullptr));
    for (int i = 0; i < 5; i++) {
        cout << rand() << endl;
    }
    return 0;
}

跑两遍,数字不一样了。要 1 到 100,取余再平移,rand % 100 + 1,取余得 0 到 99,加 1 得 1 到 100。

C++11 起有了新玩具,<random> 头文件,随机数引擎加分布,两行搞定,分布更均匀,写法也更直白。

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

int main() {
    random_device rd;
    mt19937 rng(rd());
    uniform_int_distribution<int> dist(1, 100);

    for (int i = 0; i < 5; i++) {
        cout << dist(rng) << endl;
    }
    return 0;
}

初学者建议先认 rand 那一套,老代码里到处都是,读得懂才有然后。<random> 是写新代码的选择,等哪天要均匀分布、要正态分布、要可复现的随机序列,再回来细聊。

做法头文件一句话点评
rand 加 srand<cstdlib> 加 <ctime>老搭档,教程和遗产代码都靠它
mt19937 加分布<random>现代做法,均匀可控,该早点换

现在拼完整版。五摊东西,埋种子、生成答案、循环猜、计数器、结算。

┌────────────────────────────────────────────────────────┐
│ 猜数字程序分五摊                                       │
├────────────────────────────────────────────────────────┤
│ 完整版还只有一个 main,里面分了五摊                    │
│                                                        │
│ +------------+----------------------+--------------+   │
│ |段落        |干什么                |用到什么      |   │
│ +------------+----------------------+--------------+   │
│ |埋种子      |让每次运行答案不同    |srand 加 time |   │
│ |生成答案    |定一个 1 到 100 的数  |rand 加取余   |   │
│ |循环猜      |读、比、提示,猜中才停|while 加 if   |   │
│ |计数器      |记猜了几次            |一个 int      |   │
│ |结算        |跳出后报告成绩        |break 跳这儿  |   │
│ +------------+----------------------+--------------+   │
│                                                        │
│   五摊按顺序排,循环那块是本章的组合拳                 │
│   拆函数留到下一章,现在先堆在 main 里                 │
└────────────────────────────────────────────────────────┘
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

int main() {
    srand((unsigned int)time(nullptr));
    int answer = rand() % 100 + 1;
    int guess = 0;
    int count = 0;

    while (guess != answer) {
        cout << "猜一个 1 到 100 的数: ";
        cin >> guess;
        count++;

        if (guess > answer) {
            cout << "猜大了" << endl;
        } else if (guess < answer) {
            cout << "猜小了" << endl;
        } else {
            cout << "猜中了,一共猜了 " << count << " 次" << endl;
        }
    }
    return 0;
}

和 1.1 节的差别只有两行,一个 srand,一个 rand % 100 + 1。流程一行没动,这就是流程控制值钱的地方,壳子搭好,换内容只是换两行。

顺手指出一个脆弱处。cin 里喂个字母,第三章讲过,failbit 一置,cin 直接摆烂,guess 再也不更新,循环停不下来。交差之前先用字母试一次,崩了就知道要补第三章那套三板斧。

四、小结

┌──────────────────────────────────────────────────────────┐
│ 本章收口                                                 │
├──────────────────────────────────────────────────────────┤
│ 一章学完,手上就该有这几样东西                           │
│                                                          │
│ ┌────────────────────────────────────────────────────┐   │
│ │  选择      if 三形态,switch 加 break              │   │
│ └────────────────────────────────────────────────────┘   │
│ ┌────────────────────────────────────────────────────┐   │
│ │  循环      while、do while、for 各就各位           │   │
│ └────────────────────────────────────────────────────┘   │
│ ┌────────────────────────────────────────────────────┐   │
│ │  跳转      break 跳出,continue 跳过本次           │   │
│ └────────────────────────────────────────────────────┘   │
│                                                          │
│   猜数字一个游戏,把选择、循环、跳转全用上了             │
│   读代码先找循环,再看循环里的分支                       │
│   下一章把 main 里这五摊收进函数                         │
└──────────────────────────────────────────────────────────┘
知识一句话最容易栽在哪
if 三形态单双多,分支一个个加条件顺序写反
else 配对找最近的没配对 if缩进骗人,花括号救命
switch整数对标签,break 跳出忘 break 全穿透
while先判后跑条件恒真,忘了更新
do while先跑后判,至少一次漏分号
for三段一格,计数专用边界小于等于
break出圈嵌套里只跳一层
continue跳过本轮更新不走就死循环
嵌套循环外层一步内层一轮计数器重用
随机数先播种再 rand不播种答案不变

五句话带走。流程控制不是新语法,是把程序的走法从直线变成会看路。选择看条件,循环看真假,跳转让走法更利索。if 的花括号一律写上,这是本章性价比最高的一条。猜数字一个游戏把全章的关键字用全了,读它三遍胜过读三十个片段。写死循环别慌, Ctrl+C 加检查条件,两步搞定。

课后练习四道,由易到难。改猜数字,加次数上限,七次没中就报答案是几,重开一轮。九九乘法表倒过来打,第九行九个等式第一行一个。打印 100 以内的素数,用取余判一遍,break 提前收工。写个菜单,用 do while 包着,选 0 才退出,选错给提示。

附录|流程控制速查卡

类别关键词一句话提醒
单分支if 加花括号条件真才进
双分支if 配 else二选一必执行
多分支if else if 链严条件放前面
三目条件 ? 甲 : 乙一行二选一,加括号
多路开关switch case default标签是整数常量,break 别忘
先判循环while条件假一次不进
先跑循环do while至少一次,分号别省
计数循环for三段一格,分号不能省
跳出breakswitch 里也认,只跳一层
跳过continue回条件判断,不走完本轮
常见现象病根药方
程序停不下来条件恒真或没更新查赋值号,查计数器
屏幕刷到看不清死循环在打印Ctrl+C,先停再查
多跑或少跑一轮边界写成小于等于先跑三轮数一数
switch 分支全执行忘了 break每个 case 补上
else 对不上号缩进骗人花括号写上
内层循环不跑内层条件一开始就假两个计数器都打印
每次运行答案一样忘了 srand开头埋种子
喂字母程序卡死failbit 没清clear 加 ignore 重问