Skip to content

第 12 章 不知道次数怎么办——while 与 do-while


🏗️ 前情回顾:上章你问了「知道次数用 for——但不知道次数怎么办?」这一章就是答案。while 和 do-while 不看次数,只看条件——密码输错一次再来一次,直到对了为止。


🎯 本章目标

学完这一章,你能:

  • while 循环处理"不知道要重复多少次"的场景
  • 区分 whiledo-while:前者先判断后执行,后者先执行后判断
  • 在 for、while、do-while 之间做出正确的选择
  • 设计正确的循环条件,避免死循环
  • 理解"循环前的准备"和"循环内的更新"这两个关键

📖 故事引入

你和朋友去吃自助餐。规则很简单:只要你还能吃,就可以继续拿

你不会在进门的时候就想"我要吃 8 盘"——那太蠢了。你的策略是:拿一盘,吃完,感觉"还能吃吗?"→ 能,再去拿。不能,放下筷子。

这个过程就是 while 循环的精神——不知道要重复多少次,只知道"什么时候该停"

另一个场景:你登录电脑。系统提示"请输入密码"。

  • 你输入密码,系统判断对错
  • 错了 → "请重试" → 你再输入 → 再判断
  • 对了 → 进入桌面

你不知道会输错几次——可能一次就对,可能输错三次。循环次数取决于条件,不取决于事先的数字。

这就是 while 的用武之地。如果说 for 是"跑 5 圈",while 就是"跑到腿软为止"。


🧱 知识讲解

12.1 while 循环:先问再走

while 的语法比 for 更简单:

cpp
while (条件) {
    // 条件成立就执行,执行完再回来检查条件
}

for 对比:

cpp
// for 版本:跑 5 圈
for (int i = 1; i <= 5; i++) {
    cout << "第" << i << "圈" << endl;
}

// while 版本:跑到腿软
int i = 1;               // 初始准备(对应 for 的初始化)
while (i <= 5) {         // 条件判断
    cout << "第" << i << "圈" << endl;
    i++;                 // 更新(对应 for 的迭代)
}

两个版本效果完全一样。区别在于:for 把三要素浓缩在括号里,while 把初始化和迭代拆到了外面。

while 的核心逻辑:先看条件,条件成立才进循环体;循环体跑完再回来看条件;条件不成立了就退出。

12.2 ❌ 经典翻车现场:忘了更新变量

先看这段代码——有什么问题?

cpp
int i = 1;
while (i <= 5) {
    cout << i << " ";    // 🤔 输出:1 1 1 1 1 1 1……停不下来了!
}

程序卡住了!屏幕疯狂刷 1,风扇狂转——这是死循环。为什么?因为 i 始终是 1,i <= 5 永远成立,循环永远退不出来。

✅ 正确版本:循环体内别忘了更新

cpp
int i = 1;
while (i <= 5) {
    cout << i << " ";
    i++;                 // ✅ 每轮结束 i 加 1,最终会走到 6,退出循环
}
// 输出:1 2 3 4 5

这就是 while 和 for 最大的区别:for 把三要素锁在括号里,不容易忘;while 把初始化和迭代拆出来,你必须自己管理——忘了任何一步都是灾难。

12.3 经典场景:密码验证

cpp
string password;
string correct = "cpp123";

cout << "请输入密码:";
cin >> password;

while (password != correct) {
    cout << "密码错误,请重试:";
    cin >> password;
}

cout << "登录成功!欢迎回来。" << endl;

运行效果:

请输入密码:abc
密码错误,请重试:12345
密码错误,请重试:cpp123
登录成功!欢迎回来。

这里 while 循环跑了几次?取决于你输错了几次——可能 0 次(一次就对),可能 3 次,可能 10 次。while 不在乎次数,它只盯着条件。

12.4 do-while:先走再问

while 的问题是——如果一开始条件就不成立,循环体一次都不执行。

但有些场景要求"至少做一次,然后看要不要继续"。比如:

  • 菜单程序:先显示菜单让用户选,选"退出"才结束
  • 游戏:先玩一局,问"再来一局吗?"

这时候用 do-while

cpp
do {
    // 循环体:先执行
} while (条件);   // ← 注意这里有分号!

关键区别

whiledo-while
判断时机先判断,后执行先执行,后判断
最少执行次数0 次1 次
适合场景可能一次都不需要做至少要做一次

菜单程序的例子:

cpp
int choice;
do {
    cout << "======== 菜单 ========" << endl;
    cout << "1. 开始游戏" << endl;
    cout << "2. 查看成绩" << endl;
    cout << "3. 退出" << endl;
    cout << "请选择:";
    cin >> choice;

    switch (choice) {
        case 1: cout << "游戏开始!" << endl; break;
        case 2: cout << "你的历史最高分:999" << endl; break;
        case 3: cout << "再见!" << endl; break;
        default: cout << "无效选项,请重选" << endl;
    }
} while (choice != 3);

菜单至少显示一次——用户看到菜单才能选择。所以用 do-while 刚好。

⚠️ while (条件) 后面没有分号,但 do { ... } while (条件); 的末尾有分号。这是一个常见的"分号惊魂"——编译报错了别慌,先检查这里。

12.5 死循环:当条件永远成立

这是每个程序员都会经历的"社死"瞬间——程序卡住不动了,风扇狂转,只能强制关闭。罪魁祸首就是死循环

cpp
int i = 1;
while (i > 0) {    // i 永远不会 ≤ 0,条件永远成立
    cout << i++ << " ";
}

更经典的死循环:

cpp
int x = 1;
while (x != 10) {
    x += 2;        // x 是 1, 3, 5, 7, 9, 11... 永远跳不到 10!
}

预防死循环的三个检查点

  1. 循环变量初始化了吗?
  2. 循环体内有更新循环变量的语句吗?
  3. 更新方向会让条件最终变为 false 吗?

💡 如果刻意要写无限循环(比如游戏主循环),C++ 的标准写法是 while (true) { ... }。但要在循环体内用 break 退出(下一章会讲)。

12.6 循环条件设计技巧

写好循环条件,需要三个习惯:

习惯一:在循环前做好"第一次准备"

cpp
// 读入数据直到遇到 0 停止
int num;
cin >> num;                  // 先读第一个
while (num != 0) {
    // 处理 num
    cin >> num;              // 再读下一个(在循环末尾)
}

这种"读入 → 判断 → 处理 → 再读入"的模式极其常见,信息学竞赛题里到处都是。

习惯二:用"哨兵"简化条件

cpp
// 统计一串数字的和,以 -1 结束
int sum = 0, num;
while (cin >> num && num != -1) {
    sum += num;
}

cin >> num 本身也能作为条件——如果输入成功且不是 -1,就继续。这种写法比先读再判断更紧凑。

习惯三:循环条件要向着"能退出"的方向发展

cpp
// ❌ 危险:条件依赖用户输入,但用户不知道要输入什么才能退出
int x;
while (x != 42) {
    cin >> x;
    cout << x * 2 << endl;
}

// ✅ 改进:用哨兵值(如 -1 或 0)明确退出条件
int x;
while (cin >> x && x != -1) {
    cout << x * 2 << endl;
}

12.7 for vs while vs do-while:选谁?

场景推荐原因
知道确切次数(1到100)for三要素紧凑,不会漏
遍历数组/容器for天生一对
输入直到满足条件while次数不定
游戏主循环whilewhile(true) + break
至少执行一次do-while先做再判断
菜单程序do-while菜单至少显示一次

简单口诀:"有数用 for,没数用 while,必须一次用 do-while。"


✋ 动手试试

试试 1:写一个"猜数字"游戏。程序预设一个秘密数字(如 42),让用户反复猜,每次猜完提示"大了"或"小了",直到猜对为止。用 while 循环。

试试 2:故意把 while 循环里的 i++ 删掉,运行看看。你制造了一个死循环——理解它是怎么发生的,以后就能避免。

试试 3:改写试试 1 的猜数字游戏,用 do-while 实现。对比两种写法,哪个更自然?

试试 4:写一个"数字反转"程序。输入一个正整数,用 while 循环逐位取出来并反转输出。例如输入 1234,输出 4321。(提示:重复做 n % 10 取个位,n /= 10 丢掉个位,直到 n 变成 0。)


⚠️ 你踩过这些坑吗

  • [ ] 坑 1:while 里忘了更新变量——int i = 1; while (i <= 5) { cout << i; } → 死循环!i 永远是 1,条件永远成立。循环体内一定要有让条件最终不成立的代码。
  • [ ] 坑 2:do-while 末尾忘了分号——do { ... } while (条件) 缺分号,编译报错。do-while 末尾必须有分号。
  • [ ] 坑 3:用 != 但更新方向不对——int x = 1; while (x != 10) { x += 2; } x 跳到 11 绕过了 10。确认更新路径能走到目标值,或者改用 <= 或哨兵值。
  • [ ] 坑 4:混淆 while 和 do-while 的执行顺序——可能"一次都不需要做"的场景(如文件可能为空),用 do-while 会强行处理一条不存在的数据。该用 while 就用 while。

📝 练习

基础题

1. 选择题

(1)以下哪个场景最适合用 while 而不是 for?
A. 输出 1 到 100   B. 输入密码直到正确   C. 求 1+2+...+n   D. 判断一个数是不是素数

(2)do-while 和 while 的最大区别是:
A. do-while 更快   B. do-while 至少执行一次   C. do-while 不需要条件   D. do-while 不能嵌套

(3)以下代码执行后,循环体执行了几次?

cpp
int i = 5;
while (i > 0) {
    i--;
}

A. 4 次   B. 5 次   C. 6 次   D. 0 次

2. 填空题

(1)如果条件一开始就不成立,while 循环体会执行 ____ 次,do-while 循环体会执行 ____ 次。

(2)do { ... } while (...); 末尾的 ____ 不能忘。

(3)循环三要素在 while 中需要自己管理的是:________

3. 代码找错

以下代码想输出 1 到 5,为什么卡住了?修改成正确的版本:

cpp
int i = 1;
while (i <= 5) {
    cout << i << " ";
}

4. 判断题

判断以下说法是否正确:

  • ( ) while 循环最少执行 0 次
  • ( ) while (true) { } 是一个合法的无限循环
  • ( ) do-while 循环的条件写在循环体之前

提高题

5. 编程题 — 位数统计

输入一个正整数,用 while 循环统计它是几位数,并输出每一位数字(从低位到高位)。

示例运行:

请输入一个正整数:12345
这个数是 5 位数
从低位到高位:5 4 3 2 1

6. 编程题 — 最大公约数

输入两个正整数 a 和 b,用辗转相除法求它们的最大公约数(GCD)。算法:

  1. 计算 a % b 得到余数 r
  2. 如果 r = 0,b 就是最大公约数
  3. 否则,令 a = b, b = r,回到步骤 1

用 while 循环实现。

示例运行:

请输入两个数:12 18
最大公约数:6

7. 编程题 — 数位中是否包含数字 7

输入一个正整数,判断它的各位数字中是否包含数字 7。如果包含,输出"有7";否则输出"没有7"。

示例运行:

请输入一个数:1729
有7

挑战题

8. 编程题 — 数字黑洞(角谷猜想)

"数字黑洞"是指对一个正整数反复做某种操作,最终会落入一个循环。角谷猜想:

  • 如果 n 是偶数,把它除以 2
  • 如果 n 是奇数,把它乘以 3 再加 1
  • 重复操作,最终总会变成 1

输入一个正整数 n,输出操作过程中经过的每一个数,直到变成 1。

示例运行:

请输入一个数:6
6 → 3 → 10 → 5 → 16 → 8 → 4 → 2 → 1
共 8 步

9. 编程题 — 回文数判断

"回文数"是指正着读和反着读一样的数,如 121、12321。输入一个正整数,判断它是不是回文数。

(提示:用 while 循环反转数字,然后比较反转前后的值是否相等。)


🧠 本章小结

┌─────────────────────────────────────────────────────────┐
│                                                         │
│   while (条件) { ... }    先判断,后执行(最少0次)       │
│   do { ... } while (条件); 先执行,后判断(最少1次)      │
│                                                         │
│   选择口诀:                                             │
│   知道次数 → for                                        │
│   不知道次数 → while                                    │
│   至少一次 → do-while                                   │
│                                                         │
│   死循环 = 条件永远成立 + 没有出口                        │
│   预防:初始化了吗?更新了吗?能走到退出条件吗?            │
│                                                         │
│   while 经典模式:                                       │
│   · 密码验证:while (input != correct) { ... }           │
│   · 逐位处理:while (n > 0) { n%10; n/=10; }            │
│   · 哨兵输入:while (cin >> x && x != -1) { ... }       │
│                                                         │
└─────────────────────────────────────────────────────────┘

📝 配套练习

共8题。while的核心差异是"不知道次数"——J0046(截半)/J0066(不定量输入)/J0067(猜次数)三种场景各一题。。★核心(课堂必做) ◆拓展(课后练习)

级别题号链接覆盖知识点
★ 核心J0046https://hydro.ac/d/srqc/p/J0046while、/=、条件循环
◆ 拓展J0056https://hydro.ac/d/srqc/p/J0056while、取模、逐位拆解
◆ 拓展J0053https://hydro.ac/d/srqc/p/J0053for+while、break、质数
◆ 拓展J0054https://hydro.ac/d/srqc/p/J0054while、回文、质数双重判定
◆ 拓展J0066https://hydro.ac/d/srqc/p/J0066while读入不定量、数组倒序
◆ 拓展J0067https://hydro.ac/d/srqc/p/J0067while、冰雹猜想、状态变化
◆ 拓展J0055https://hydro.ac/d/srqc/p/J0055while、等比累加、阈值
◆ 拓展luogu-P1420https://hydro.ac/p/luogu-P1420while、连续计数、重置条件

💡 练习建议:先完成 1 道★核心题,确保掌握本章基本方法;再完成 7 道◆拓展题,覆盖不同变式和细节。


配套练习

共8题。while的核心差异是"不知道次数"——J0046(截半)/J0066(不定量输入)/J0067(猜次数)三种场景各一题。★核心(课堂必做) · ◆拓展(课后练习)

级别题号链接覆盖知识点
★ 核心J0046https://hydro.ac/d/srqc/p/J0046while、/=、条件循环
◆ 拓展J0056https://hydro.ac/d/srqc/p/J0056while、取模、逐位拆解
◆ 拓展J0053https://hydro.ac/d/srqc/p/J0053for+while、break、质数
◆ 拓展J0054https://hydro.ac/d/srqc/p/J0054while、回文、质数双重判定
◆ 拓展J0066https://hydro.ac/d/srqc/p/J0066while读入不定量、数组倒序
◆ 拓展J0067https://hydro.ac/d/srqc/p/J0067while、冰雹猜想、状态变化
◆ 拓展J0055https://hydro.ac/d/srqc/p/J0055while、等比累加、阈值
◆ 拓展luogu-P1420https://hydro.ac/p/luogu-P1420while、连续计数、重置条件

练习建议:先在课堂完成 1 道★核心题,掌握本章基本方法;课后完成 7 道◆拓展题,覆盖不同变式和细节。

自查清单

  • [ ] 我能用 while 处理"不知道重复几次"的场景
  • [ ] 我能区分 while 和 do-while,知道各用在哪里
  • [ ] 我理解死循环的原因——忘了更新变量或条件永远成立
  • [ ] 我能设计正确的循环条件,让它最终能退出
  • [ ] 我会在 for、while、do-while 之间做出选择

🚀 下章预告:循环里干的事,本身也需要循环怎么办?比如打印一张表——外层管行、内层管列,循环套着循环跑。下章学嵌套循环,一层不够来两层。