第四章 流程控制结构

第四章 流程控制结构

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

1.1 先看成品

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

cpp 复制代码
#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;
}

跑起来是这个节奏。

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

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

text 复制代码
┌──────────────────────────────────────────────────────────┐
│ 猜数字游戏走这几步                                       │
├──────────────────────────────────────────────────────────┤
│ 先想数,再循环猜,猜中了才结算                           │
│                                                          │
│ ┌────────────────────────────────────────────────────┐   │
│ │  开局      埋随机种子,生成 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 之间,要写两个条件用 && 连。

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

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

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

2.2 if 的三种形态

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

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

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

cpp 复制代码
if (score >= 60) {
    cout << "及格了" << endl;
}

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

cpp 复制代码
if (num % 2 == 0) {
    cout << "偶数" << endl;
} else {
    cout << "奇数" << endl;
}

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

cpp 复制代码
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。缩进好看不等于逻辑好看,下面这段看着是两层,其实是一层。

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

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

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

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

2.3 条件运算符

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

cpp 复制代码
int max;
if (a > b) {
    max = a;
} else {
    max = b;
}

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

cpp 复制代码
int max = (a > b) ? a : b;

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

2.4 switch 与穿透

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

cpp 复制代码
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 对上的时候归它,写不写都行,建议写。

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

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

cpp 复制代码
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 的规矩一句话,条件是真的,身体就再跑一遍,每跑完一遍重新问一次。

cpp 复制代码
int i = 1;
while (i <= 5) {
    cout << i << endl;
    i++;
}

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

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

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

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

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

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

3.2 do while 先跑后判

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

cpp 复制代码
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 里是这个样子。

cpp 复制代码
int i = 1;
while (i <= 100) {
    cout << i << endl;
    i++;
}

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

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

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

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

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

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

cpp 复制代码
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。两个都是本章的关键词,去的地方不一样。

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

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

cpp 复制代码
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 嵌套循环与九九乘法表

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

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

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

cpp 复制代码
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 拿当前时刻当种子,每次跑都不一样。

cpp 复制代码
#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> 头文件,随机数引擎加分布,两行搞定,分布更均匀,写法也更直白。

cpp 复制代码
#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> 现代做法,均匀可控,该早点换

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

text 复制代码
┌────────────────────────────────────────────────────────┐
│ 猜数字程序分五摊                                       │
├────────────────────────────────────────────────────────┤
│ 完整版还只有一个 main,里面分了五摊                    │
│                                                        │
│ +------------+----------------------+--------------+   │
│ |段落        |干什么                |用到什么      |   │
│ +------------+----------------------+--------------+   │
│ |埋种子      |让每次运行答案不同    |srand 加 time |   │
│ |生成答案    |定一个 1 到 100 的数  |rand 加取余   |   │
│ |循环猜      |读、比、提示,猜中才停|while 加 if   |   │
│ |计数器      |记猜了几次            |一个 int      |   │
│ |结算        |跳出后报告成绩        |break 跳这儿  |   │
│ +------------+----------------------+--------------+   │
│                                                        │
│   五摊按顺序排,循环那块是本章的组合拳                 │
│   拆函数留到下一章,现在先堆在 main 里                 │
└────────────────────────────────────────────────────────┘
cpp 复制代码
#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 再也不更新,循环停不下来。交差之前先用字母试一次,崩了就知道要补第三章那套三板斧。

四、小结

text 复制代码
┌──────────────────────────────────────────────────────────┐
│ 本章收口                                                 │
├──────────────────────────────────────────────────────────┤
│ 一章学完,手上就该有这几样东西                           │
│                                                          │
│ ┌────────────────────────────────────────────────────┐   │
│ │  选择      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 三段一格,分号不能省
跳出 break switch 里也认,只跳一层
跳过 continue 回条件判断,不走完本轮
常见现象 病根 药方
程序停不下来 条件恒真或没更新 查赋值号,查计数器
屏幕刷到看不清 死循环在打印 Ctrl+C,先停再查
多跑或少跑一轮 边界写成小于等于 先跑三轮数一数
switch 分支全执行 忘了 break 每个 case 补上
else 对不上号 缩进骗人 花括号写上
内层循环不跑 内层条件一开始就假 两个计数器都打印
每次运行答案一样 忘了 srand 开头埋种子
喂字母程序卡死 failbit 没清 clear 加 ignore 重问
相关推荐
会编程的小孩2 小时前
Qt学习路线大纲图
c++
洁洁!2 小时前
手搓 C++ 线程池详细讲解以及如何面试应答
java·c++·面试
坤坤子吖2 小时前
C++继承:继承的基本用法与访问规则
开发语言·c++·笔记·学习
Demon--hx2 小时前
C++ 文件流:理解 ifstream/ofstream 与 RAII 机制
开发语言·c++
纪念 2292 小时前
C++ stack和queue
开发语言·c++
CodeYp20193 小时前
第6讲:模板初阶
开发语言·c++·模板
.YM.Z3 小时前
【C++11】初识——右值引用和移动语义,可变参数模板
c++·c++11
要练八块腹肌3 小时前
红黑树的实现以及对map和set的封装
c++
骑着蜗牛撵大象3273 小时前
从三次握手到 epoll:C++ 写出可落地的 TCP Socket 服务端
网络·c++·tcp/ip·高并发·tcp·epoll