NOI C++ 课程体系 / 语法入门 / 第6讲
CSP-J 入门级 · 语法入门 · 循环结构专题

C++ while 与 do-while 循环 完全指南

让程序学会"重复做事" —— while 循环、do-while 循环、多层嵌套循环逐一讲透,死循环陷阱、break/continue 用法、循环执行流程一次看清。

6 大教学模块 3 大循环结构精讲 6 个精讲案例 5 大易错点 1 张速查表 15 道分层练习
本讲 · 第 10/56 讲while 与 do-while 循环
模块 01

情景导航

用"工厂流水线"的故事建立循环的直觉,再用生活类比看清 while、do-while 和嵌套循环的本质,最后给出循环结构的准确定义和一句好记的口诀。

故事故事引入 —— 工厂流水线上的"不知疲倦的工人"

小强参观了一家智能工厂,发现流水线上的机器人有个共同特点:它们都"不知疲倦"地重复做着同一件事,但每件事都有"做到什么时候就停"的规则:

在包装车间,机器人不停地往箱子里装矿泉水,每装一瓶就数一个数:1、2、3……直到数到 24 瓶就停手,把箱子封好送去仓库 —— 这就像 while 循环:先检查"箱子满了没有",没满就继续装。

在质检车间,质检员拿起一瓶水先检查标签贴没贴好,如果贴歪了就撕掉重贴,贴好了才放行。注意,第一瓶水她一定会先检查一遍,不可能"不检查就放行" —— 这就像 do-while 循环:先做一次检查,再判断"还有没有不合格的"。

在组装车间,情况更复杂:每个工位上都有两排传送带,外圈传送带每走一步,内圈传送带也走一步。外圈走完 10 步,内圈也刚好走完 10 步 —— 这就是 多层循环(嵌套循环):一个循环里面套着另一个循环。

"先判断再做事 —— 先做事再判断 —— 循环里套循环,工厂的流水线就是计算机循环结构的原型!"

小强恍然大悟:原来循环结构就是让程序"重复做同一件事"的魔法,只要写好"什么时候开始"和"什么时候停止",计算机就能不知疲倦地帮你完成重复工作。

💡 这个故事告诉我们什么?

工厂流水线和计算机循环有三个共同要素:

一、循环变量:计数器(装了几瓶了);二、循环条件:什么时候停(满 24 瓶就停);三、循环体:重复做的事(往箱子里装一瓶)。

计算机里的循环也是如此:你需要一个"计数器"、一个"停止条件"和一段"重复执行的代码"。但计算机比工人更严格 —— 如果忘记写"停止条件",程序就会永远跑下去,这就是传说中的"死循环"。

类比生活类比 —— 循环结构的四大场景

把三种循环结构和身边的场景对一对号,抽象的语法立刻变得亲切起来:

🔄

跑步机 · while 循环

先看你"还有力气吗",有就继续跑,没力了才停。先判断,再执行。

对应:while(条件) { 循环体 }
🚪

安检门 · do-while 循环

不管有没有人,先过一遍安检门,再看"还有没有人要过"。先执行,再判断。

对应:do { 循环体 } while(条件);
💡

俄罗斯套娃 · 嵌套循环

打开一个大娃娃里面还有一个小娃娃,小娃娃里面还有更小的。外层走一步,内层要走完全部。

对应:外层 for/while 套内层 for/while
🔥

红绿灯 · 死循环

如果红绿灯的定时器坏了,一直绿灯或一直红灯,车子就永远走不了或永远堵着。程序也一样,条件永远为真就会"死循环"。

对应:while(1) { ... } 永远不停
定义什么是循环结构

循环结构是程序三大基本结构之一(顺序、选择、循环),它让程序能够根据条件反复执行某段代码。在 C++ 中,while 和 do-while 是最基础的两种循环语句。

循环类型语法格式判断时机循环体至少执行几次
whilewhile(条件) { 语句 }先判断,后执行0 次(条件一开始为假就不执行)
do-whiledo { 语句 } while(条件);先执行,后判断至少 1 次
嵌套循环外层循环套内层循环外层每走一步,内层从头到尾走一遍取决于外层和内层条件

本课将按上表顺序逐一讲解。while 是"先问再做",do-while 是"先做再问",嵌套循环是"循环套循环",三种结构各有适用的场景。

循环口诀 while 先判后执行,条件为假一次不跑;
do-while 先跑后判,至少一次不能少;
循环变量要初始化,循环体内要更新;
嵌套循环记心间,外层走一步内层跑一圈;
break 跳出整个圈,continue 跳过本次连。

口诀里藏着本课的全部核心知识。下面我们就把 while、do-while 和嵌套循环一个一个拆开,看清各自的"语法、执行流程和陷阱"。

模块 02

知识探究

while 循环打地基,do-while 循环对比学习,嵌套循环层层深入,最后用动画看懂循环的执行流程。

1while 循环 —— 先判断,再执行

while 循环是 C++ 中最基础的循环语句,它的执行流程是:先判断条件,条件为真(true)才执行循环体,执行完再回头判断。如果一开始条件就为假,循环体一次都不会执行。

while ( 条件 ) { 循环体语句 }
while:关键字,表示"只要" 条件:一个返回 true/false 的表达式 { }:花括号包裹循环体 循环体:被重复执行的代码

while 循环的执行流程可以用下面这张流程图来描述:

步骤动作说明
1. 初始化给循环变量赋初值在 while 之前完成,如 int i = 1;
2. 判断条件检查条件是否为真条件为 true 则进入循环体,为 false 则跳出
3. 执行循环体执行花括号内的语句可以是一条或多条语句
4. 更新变量修改循环变量的值如 i++,让条件 eventually 变为假
5. 回到步骤 2再次判断条件重复直到条件为假

使用 while 循环,牢记三条铁律:

铁律说明
循环变量必须初始化在 while 之前给计数器赋初值,否则条件判断没有意义
循环体内必须更新变量否则条件永远不变,程序会"死循环"(永远停不下来)
花括号不能忘多行语句必须用 { } 包裹,否则只有第一行在循环体内
C++while 循环:输出 1 到 5
#include <iostream>
using namespace std;
int main(){
    int i = 1;                   // 1. 初始化
    while (i <= 5) {              // 2. 判断条件
        cout << i << " ";          // 3. 执行循环体
        i++;                        // 4. 更新变量
    }
    cout << endl;
    return 0;
}
运行结果
1 2 3 4 5

解读:i 从 1 开始,每次判断 i <= 5 为真就输出 i 并加 1,当 i 变成 6 时条件为假,循环结束。注意 i++ 是循环体的最后一句,千万不要忘记,否则 i 永远是 1,条件永远为真,程序就"死循环"了。

2do-while 循环 —— 先执行,再判断

do-while 循环和 while 循环的唯一区别在于:do-while 先执行一次循环体,然后再判断条件。也就是说,无论条件一开始是真还是假,循环体至少执行一次。

do { 循环体语句 } while ( 条件 ) ;
do:关键字,表示"做" { }:花括号包裹循环体 while(条件):在末尾判断 注意末尾的分号 ; 不能忘

do-while 的执行流程:

步骤动作说明
1. 执行循环体先执行一遍 { } 内的语句不判断条件,直接执行
2. 判断条件检查条件是否为真为真则回到步骤 1 继续,为假则退出
3. 回到步骤 1 或退出循环或结束至少执行了一次循环体
C++do-while 循环:至少执行一次
#include <iostream>
using namespace std;
int main(){
    int i = 1;
    do {
        cout << i << " ";
        i++;
    } while (i <= 5);
    cout << endl;
    return 0;
}
运行结果
1 2 3 4 5

解读:这个例子和 while 的效果一样,因为 i 从 1 开始,条件一开始就是真的。但如果是下面的情况,区别就出来了:

C++while vs do-while 的关键区别
// while 版本:i=10, 条件 10<=5 为假,一次不执行
int i = 10;
while (i <= 5) {
    cout << i << " ";
    i++;
}
// 输出:(什么都不输出)

// do-while 版本:i=10, 先执行一次,再判断
i = 10;
do {
    cout << i << " ";
    i++;
} while (i <= 5);
// 输出:10(至少执行了一次)

口诀:while 先判后执行,条件假则一次不跑;do-while 先跑后判,至少一次不能少。

3while vs do-while —— 到底该用哪个?

两种循环都能实现"重复执行",但适用场景不同。下面用一张表说清楚:

对比项whiledo-while
判断时机先判断条件,再执行循环体先执行循环体,再判断条件
循环体最少执行次数0 次至少 1 次
语法特点while(条件) { } 在顶部判断do { } while(条件); 在末尾判断,末尾有分号
典型场景已知范围或可预先判断的场景需要"至少做一次"的场景(如菜单选择、输入验证)
死循环写法while(1) { } 或 while(true) { }do { } while(1);
while 适合的场景
  • 文件读取(先判断是否到末尾)
  • 已知范围的计数(如 1 到 100)
  • 条件可能一开始就不满足
do-while 适合的场景
  • 菜单选择(至少显示一次菜单)
  • 输入验证(至少要求输入一次)
  • 游戏主循环(至少运行一轮)
4嵌套循环 —— 循环套循环

嵌套循环就是在一个循环的循环体里再写一个循环。外层循环每执行一次,内层循环都要从头到尾完整地走一遍。理解嵌套循环的关键是:外层走一步,内层跑一圈。

while (外层条件) {
  while (内层条件) { 内层循环体 }
}
外层循环:控制"行" 内层循环:控制"列" 外层每走一步,内层从头到尾走一遍 内层循环变量通常在外层中初始化

理解嵌套循环执行次数的方法:内层循环体总共执行次数 = 外层循环次数 × 内层循环次数。

C++嵌套循环:打印 1×1 到 3×3 的乘法表
#include <iostream>
using namespace std;
int main(){
    int i = 1;
    while (i <= 3) {            // 外层:i = 1, 2, 3(共3次)
        int j = 1;
        while (j <= 3) {          // 内层:j = 1, 2, 3(每轮3次)
            cout << i << "×" << j << "=" << i*j << "	";
            j++;
        }
        cout << endl;
        i++;
    }
    return 0;
}
运行结果
1×1=1	1×2=2	1×3=3
2×1=2	2×2=4	2×3=6
3×1=3	3×2=6	3×3=9

解读:外层 i 从 1 到 3,每轮外层执行时,内层 j 都从 1 到 3 走一遍。内层循环体总共执行 3 × 3 = 9 次。注意内层循环变量 j 必须在外层循环体内初始化(每次外层开始都要重新从 1 开始),如果写在 while 外面,j 只会初始化一次,后面轮次 j 的值就不对了。

口诀:外层走一步,内层跑一圈;内层变量外层初,莫忘更新莫忘分号。
5串起来看:while 循环的逐步执行之旅

跟着动画一步步看 while 循环的执行过程,设 i = 1,条件 i <= 3:

🎬 动画演示:while(i <= 3) { cout << i; i++; } 的执行过程

1
初始化:i = 1,准备开始循环
2
判断条件:i = 1,1 <= 3 为 真,进入循环体
3
执行循环体:输出 1,然后 i++ 使 i 变成 2
4
回到判断:i = 2,2 <= 3 为 真,进入循环体
5
执行循环体:输出 2,然后 i++ 使 i 变成 3
6
回到判断:i = 3,3 <= 3 为 真,进入循环体
7
执行循环体:输出 3,然后 i++ 使 i 变成 4
8
回到判断:i = 4,4 <= 3 为 假,循环结束!
9
最终输出:1 2 3,程序继续执行循环后面的代码

解读:while 循环每次都先"敲门"(判断条件),"有人"(条件为真)才"进门"(执行循环体)。当 i 变成 4 时,"敲门"发现"没人"(条件为假),循环就结束了。

案例6 个由浅入深的案例

案例 1:1 到 N 累加 —— while 循环的经典应用

题目:输入一个正整数 N,计算 1 + 2 + 3 + ... + N 的和。

C++1到N累加
#include <iostream>
using namespace std;
int main(){
    int n;
    cin >> n;
    int i = 1;        // 计数器初始化
    int sum = 0;      // 累加器初始化
    while (i <= n) {    // 当 i 不超过 n 时循环
        sum = sum + i;    // 把 i 加到 sum 里
        i++;              // i 加 1,准备下一次
    }
    cout << sum << endl;
    return 0;
}
运行结果
输入:100
输出:5050

解读:sum 从 0 开始,每次循环加上当前的 i,i 从 1 递推到 n。sum = sum + i 这种"自己加自己"的写法是累加器的标准模式,一定要熟练掌握。

案例 2:猜数字游戏 —— do-while 的典型场景

题目:程序随机生成一个 1 到 100 之间的数字(这里用 42 代替),让用户不断猜,直到猜对为止,并提示"太大了"或"太小了"。

C++猜数字游戏
#include <iostream>
using namespace std;
int main(){
    int secret = 42;    // 秘密数字(实际用 rand() 生成)
    int guess;
    int attempts = 0;   // 记录猜了多少次
    do {
        cout << "请输入 1~100 的数字:";
        cin >> guess;
        attempts++;
        if (guess > secret) {
            cout << "太大了!" << endl;
        } else if (guess < secret) {
            cout << "太小了!" << endl;
        } else {
            cout << "猜对了!你用了 " << attempts
                 << " 次。" << endl;
        }
    } while (guess != secret);
    return 0;
}
运行结果(模拟一次游戏)
请输入 1~100 的数字:50
太大了!
请输入 1~100 的数字:25
太小了!
请输入 1~100 的数字:35
太小了!
请输入 1~100 的数字:42
猜对了!你用了 4 次。

解读:do-while 非常适合这种"至少要求输入一次"的场景 —— 用户必须先输入一个数字,程序再判断猜得对不对,不对就继续猜。用 while 也可以写,但需要额外写一次输入代码在循环前面,do-while 更简洁。

案例 3:打印乘法表 —— 嵌套循环的经典应用

题目:输入一个正整数 N,打印 N×N 的乘法表。

C++N×N 乘法表
#include <iostream>
using namespace std;
int main(){
    int n;
    cin >> n;
    int i = 1;
    while (i <= n) {
        int j = 1;
        while (j <= n) {
            cout << i << "×" << j << "=" << i*j << "	";
            j++;
        }
        cout << endl;
        i++;
    }
    return 0;
}
运行结果(输入 5)
1×1=1	1×2=2	1×3=3	1×4=4	1×5=5
2×1=2	2×2=4	2×3=6	2×4=8	2×5=10
3×1=3	3×2=6	3×3=9	3×4=12	3×5=15
4×1=4	4×2=8	4×3=12	4×4=16	4×5=20
5×1=5	5×2=10	5×3=15	5×4=20	5×5=25

解读:外层 i 控制行号(1 到 n),内层 j 控制列号(1 到 n)。外层每走一步,内层从头到尾走 n 遍,总共执行 n × n 次输出。这是嵌套循环最经典的应用。

案例 4:do-while 输入验证 —— 确保输入合法

题目:要求用户输入一个 1 到 100 之间的正整数,如果输入不合法就提示重新输入,直到合法为止。

C++输入验证
#include <iostream>
using namespace std;
int main(){
    int age;
    do {
        cout << "请输入年龄(1~100):";
        cin >> age;
        if (age < 1 || age > 100) {
            cout << "输入不合法,请重新输入!" << endl;
        }
    } while (age < 1 || age > 100);
    cout << "年龄 " << age << " 验证通过!" << endl;
    return 0;
}
运行结果(模拟输入)
请输入年龄(1~100):-5
输入不合法,请重新输入!
请输入年龄(1~100):150
输入不合法,请重新输入!
请输入年龄(1~100):25
年龄 25 验证通过!

解读:do-while 天然适合输入验证场景 —— 至少要让用户输入一次,然后判断合不合法,不合法就继续循环要求重新输入。这里用 || 把两个"不合法"条件连起来,只要有一个成立就继续循环。

案例 5:星号矩形 —— 嵌套循环的图形输出

题目:输入行数 R 和列数 C,用 * 号打印一个 R 行 C 列的矩形。

C++打印星号矩形
#include <iostream>
using namespace std;
int main(){
    int r, c;
    cin >> r >> c;
    int i = 1;
    while (i <= r) {
        int j = 1;
        while (j <= c) {
            cout << "*";
            j++;
        }
        cout << endl;    // 每行结束后换行
        i++;
    }
    return 0;
}
运行结果(输入 3 5)
*****
*****
*****

解读:外层控制行数(3 行),内层控制每行的列数(5 列)。内层循环负责在一行里打印 *,外层循环负责换行。如果把内层的 <= c 改成 <= i,就能打印直角三角形,这是竞赛中常见的图形题。

案例 6:现场捉虫 —— 三处循环高频错误

题目:下面这段程序想计算 1 到 10 的累加和,却藏着三处错误,你能全部找出来吗?

C++待捉虫的程序
#include <iostream>
using namespace std;
int main(){
    int i = 1;
    int sum = 0;
    while (i <= 10) {
        sum = sum + i;
        // 错误一:忘了 i++,i 永远是 1,死循环!
    }
    cout << "sum = " << sum << endl;

// 错误二:while 后面多写了分号
    int j = 1;
    while (j <= 5); {
        cout << j << " ";
        j++;
    }
    cout << endl;

// 错误三:sum 没初始化,结果不确定
    int total;
    int k = 1;
    while (k <= 3) {
        total = total + k;
        k++;
    }
    cout << "total = " << total << endl;
    return 0;
}
错误问题所在修正方法
错误一循环体内没有 i++,i 永远是 1,条件 i <= 10 永远为真,程序"死循环"在循环体末尾加上 i++
错误二while (j <= 5) 后面多了一个分号 ;,导致循环体变成空语句,{ } 里的代码只执行一次去掉 while 后面的分号
错误三total 没有初始化(没有赋初值 0),C++ 中局部变量不初始化值是随机的,累加结果不可预测写成 int total = 0;

解读:这三处错误分别对应"忘记更新变量导致死循环""while 后误加分号""变量未初始化"三大循环陷阱,后面的易错点环节还会逐个细讲。

模块 03

实践应用

三道动手题:跟踪循环输出、捉出三处循环错误、综合运用循环写一个小程序。

实践 1 写输出

题目:下面程序输出什么?

C++待跟踪的程序
#include <iostream>
using namespace std;
int main(){
    int i = 1, j = 1;
    while (i <= 3) {
        j = 1;
        while (j <= i) {
            cout << "*";
            j++;
        }
        cout << endl;
        i++;
    }
    return 0;
}
提示:内层循环条件是 j <= i,i 每轮加 1,所以每行星号数量不同。注意 j 在外层中重新赋值为 1。
点击查看参考答案
答案:
*
**
***
过程:第一轮 i=1,内层 j 从 1 到 1 输出 1 个 *;第二轮 i=2,内层 j 从 1 到 2 输出 2 个 *;第三轮 i=3,内层 j 从 1 到 3 输出 3 个 *。这是一个直角三角形图案。
点评:嵌套循环中,内层循环变量 j 必须在外层循环体内重新初始化(j = 1),否则第二轮开始 j 的值不对。
实践 2 捉虫挑战

题目:下面程序想打印 1 到 5 的平方,但结果不对,找出错误并改正。

C++出错的程序
int i = 1;
while (i <= 5); {
    cout << i << "^2 = " << i*i << endl;
    i++;
}
// 输出:什么也没有,或者程序卡住
提示:看看 while 后面有没有多余的分号?
点击查看参考答案
错误:while (i <= 5) 后面多了一个分号 ;。这导致 while 循环的循环体是空语句(什么都不做),条件永远为真,程序"死循环"。{ } 里的代码变成了普通语句,只执行一次(但永远不会走到那里,因为前面死循环了)。
改正:去掉 while 后面的分号,写成 while (i <= 5) {
正确输出:1^2 = 1
2^2 = 4
3^2 = 9
4^2 = 16
5^2 = 25
实践 3 编程题

题目:质数判断器 —— 用 while 循环判断一个数是不是质数

输入一个正整数 n(n > 1),判断它是不是质数。质数是只能被 1 和自身整除的大于 1 的自然数。

提示:从 2 开始除到 n-1,如果有一个能整除就不是质数。优化:只需除到 sqrt(n) 即可(竞赛技巧,暂不要求)。
点击查看参考答案
C++参考代码
#include <iostream>
using namespace std;
int main(){
    int n;
    cin >> n;
    int d = 2;
    bool isPrime = true;
    while (d < n) {
        if (n % d == 0) {
            isPrime = false;
            break;
        }
        d++;
    }
    if (isPrime) {
        cout << n << " 是质数" << endl;
    } else {
        cout << n << " 不是质数" << endl;
    }
    return 0;
}
示例:输入 7 → 输出"7 是质数";输入 12 → 输出"12 不是质数"。
点评:用 while 循环从 2 开始逐个尝试除 n,一旦找到一个能整除的数就用 break 跳出循环,标记为非质数。break 是"强行跳出循环"的利器,后面会详细讲。
模块 04

总结提升

一张表收拢 while、do-while 和嵌套循环,一句口诀记住循环要点,再用"一题三解"体会不同循环的写法。

总结核心要点总结
循环类型语法判断时机最少执行次数一句话记住
whilewhile(条件) { }先判后执行0 次条件为假一次不跑
do-whiledo { } while(条件);先执行后判至少 1 次末尾分号不能忘
嵌套循环外层套内层外层每步内层全跑看条件外层走一步内层跑一圈
循环三大铁律 循环变量先初始化,
循环体内要更新,
while 后别加分号;
嵌套循环记心间,
内层变量外层初,
break 跳出整个圈,continue 跳过本次连。
拓展拓展案例 —— 打印 1 到 N 的三种写法

同样是"输出 1 到 N",用 while、do-while 和嵌套循环(配合 if)各写一遍:

C++一题三解:输出 1 到 N
#include <iostream>
using namespace std;
int main(){
    int n = 5;

// 解法一:while 循环
    int i = 1;
    while (i <= n) {
        cout << i << " ";
        i++;
    }
    cout << endl;

// 解法二:do-while 循环(需要特殊处理 n=0 的情况)
    int j = 1;
    do {
        cout << j << " ";
        j++;
    } while (j <= n);
    cout << endl;

// 解法三:嵌套循环 + if(不推荐,但逻辑上可行)
    int a = 1;
    while (a <= n) {
        int b = 1;
        while (b <= 1) {
            if (a <= n) cout << a << " ";
            b++;
        }
        a++;
    }
    cout << endl;

return 0;
}
运行结果
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5

解读:三种写法输出相同,但 while 是最自然的选择。do-while 多写了一次(j 从 1 开始,如果 n=0 就会多输出一个 1)。嵌套循环在这里是"杀鸡用牛刀",但可以帮助理解嵌套的执行逻辑。实际写程序时,选择最简洁、最不容易出错的写法。

🧠 思考与启发

循环是编程中最强大的工具之一。有了循环,程序可以处理任意数量的数据、完成重复的任务、生成复杂的图形。想想看:如果没有循环,你要输出 1 到 10000 的数字,就得写 10000 行 cout 语句!

想一想:while(1) 和 while(true) 哪个更好?它们都会让程序"死循环",但在实际编程中,我们通常用 break 语句在适当的时候跳出循环。再想想:嵌套循环中,如果外层循环走 M 步、内层循环走 N 步,内层循环体总共执行了多少次?(答案:M × N 次。这个结论在计算程序时间复杂度时非常重要。)

模块 05

学习检测

5 个最容易丢分的循环陷阱、一张考前必背速查表,以及可以贴在文具盒上的知识点速记卡。

易错 1

忘记更新循环变量:死循环

while 循环的循环体内必须有修改循环变量的语句(如 i++),否则条件永远不变,程序会"死循环"(永远停不下来),在考试中会导致超时。

错误写法
int i = 1;
while (i <= 10) {
    cout << i << " ";
    // 忘了 i++,死循环!
}
正确写法
int i = 1;
while (i <= 10) {
    cout << i << " ";
    i++;  // 别忘了!
}
口诀:循环体内要更新,变量不变更死循环。
易错 2

while 后面误加分号

while (条件); 后面的分号会让循环体变成空语句,{ } 里的代码变成普通语句只执行一次(或者根本走不到),造成"什么都不输出"的诡异现象。

错误写法
int i = 1;
while (i <= 5); {    // 分号是虫子!
    cout << i << " ";
    i++;
}
正确写法
int i = 1;
while (i <= 5) {     // 没有分号
    cout << i << " ";
    i++;
}
口诀:while 后面别加分,加了分号循环空。
易错 3

循环变量未初始化

C++ 中局部变量不初始化时值是随机的(垃圾值)。如果循环变量没赋初值,条件判断的结果不可预测,程序行为完全不确定。

错误写法
int i;           // 未初始化,值是随机的
while (i <= 10) { // i 可能是 100000,直接跳出
    ...
}
正确写法
int i = 1;       // 明确赋初值
while (i <= 10) {
    ...
}
口诀:变量使用前先初始化,否则结果全看运气。
易错 4

do-while 末尾忘加分号

do-while 语句的语法要求在 while(条件) 后面加一个分号 ;,这个分号是语句的一部分,不是花括号的结束。忘记写分号会导致编译错误。

错误写法
do {
    cout << "hello";
} while (false)      // 忘加分号!
正确写法
do {
    cout << "hello";
} while (false);     // 有分号
口诀:do-while 末尾有分号,别忘别多加。
易错 5

嵌套循环内层变量忘记在外层中初始化

嵌套循环中,内层循环变量必须在外层循环体内初始化。如果写在 while 外面,只有第一轮会初始化,后续轮次内层变量的值会"继承"上一轮的结果,导致逻辑错误。

错误写法
int j = 1;         // 在外层外面!
while (i <= 3) {
    while (j <= 3) {
        cout << j << " ";
        j++;
    }
    cout << endl;
    i++;
}
// 输出:1 2 3(只有一行)
正确写法
while (i <= 3) {
    int j = 1;       // 在外层里面!
    while (j <= 3) {
        cout << j << " ";
        j++;
    }
    cout << endl;
    i++;
}
口诀:内层变量外层初,每轮重开不迷路。
速查考前速查表
类型语法判断时机最少次数提醒
whilewhile(条件) { }先判后执行0 次别忘了更新变量
do-whiledo { } while(条件);先执行后判至少 1 次末尾分号不能忘
嵌套循环外层套内层外层每步内层全跑看条件内层变量外层初
breakbreak;立即跳出循环—只跳最内层
continuecontinue;跳过本次,进入下次—不跳出,只跳过
死循环while(1) { }条件永远为真∞用 break 跳出
📌知识点速记卡
while 先判后执行

条件为真才执行循环体,条件一开始为假则一次不执行。循环变量必须初始化、必须更新。

do-while 先执行后判

循环体至少执行一次。末尾 while(条件) 后面必须加分号 ;。

嵌套循环外层走一步内层跑一圈

内层循环变量必须在外层循环体内初始化,否则只有第一轮正确。

break 跳出整个循环

遇到 break 立即终止当前循环,执行循环后面的代码。只跳最内层循环。

continue 跳过本次循环

遇到 continue 跳过本次循环体剩余部分,直接进入下一次条件判断。不跳出循环。

死循环 while(1)

条件永远为真,程序永远不退出。必须配合 break 使用,否则程序"卡死"。

while 后别加分号

while(条件); 的分号会让循环体变空,{ } 里的代码只执行一次或永远走不到。

累加器初始化为 0

求和用 sum = 0 初始化,求积用 product = 1 初始化,这是循环中的标准模式。

课后练习

练习作业

巩固 while、do-while 和嵌套循环的编写与跟踪,分层练习,点击展开答案与解析。

总结练习3 道综合题
总结 1 写输出

题目:下面程序输出什么?
int i = 1, sum = 0; while (i <= 5) { sum += i; i += 2; } cout << sum;

提示:i 每次加 2,所以 i 的值是 1, 3, 5,然后变成 7 跳出。sum = 1 + 3 + 5。
点击查看参考答案
答案:9。i 依次取 1、3、5(i=7 时跳出),sum = 0+1+3+5 = 9。
总结 2 判断题

题目:判断 —— do-while 循环的循环体可能一次都不执行。

提示:回忆 do-while 的执行流程:先执行,再判断。
点击查看参考答案
答案:错误。do-while 先执行循环体再判断条件,所以循环体至少执行一次。while 循环的循环体才可能一次都不执行。
总结 3 编程题

题目:输入一个正整数 N,用 do-while 循环计算 1 × 2 × 3 × ... × N 的结果(即 N 的阶乘)。

提示:累乘器初始化为 1,循环变量从 1 到 N,每次乘上当前的 i。
点击查看参考答案
C++参考代码
#include <iostream>
using namespace std;
int main(){
    int n;
    cin >> n;
    int i = 1;
    long long fact = 1;
    do {
        fact = fact * i;
        i++;
    } while (i <= n);
    cout << fact << endl;
    return 0;
}
示例:输入 5 → 输出 120(1×2×3×4×5 = 120)。
A 组基础巩固(4 题)
A1 填空题

题目:while 循环的执行流程是______(先判断/先执行),do-while 循环的循环体至少执行______次。

点击查看参考答案
C++完整参考代码
#include <iostream>
using namespace std;
int main(){
    int i = 5, cntWhile = 0;
    while (i < 5) {        // 条件一开始就为假
        cntWhile++;        // 循环体一次都不执行
    }
    int j = 5, cntDo = 0;
    do {
        cntDo++;           // 先无条件执行一次,再判断条件
    } while (j < 5);
    cout << "while 执行 " << cntWhile << " 次" << endl;
    cout << "do-while 执行 " << cntDo << " 次" << endl;
    return 0;
}
// 实际输出:
// while 执行 0 次
// do-while 执行 1 次

答案:先判断;1。

思路:把两种循环的执行流程摆在一起对比。while 是"判断 → 执行 → 判断 → 执行……",do-while 是"执行 → 判断 → 执行 → 判断……"。只差第一步的顺序,结果就差很多:初始条件为假时,while 的循环体一次都没碰,do-while 却已经老老实实做完一轮了。

考点:while 与 do-while 的执行流程与语法差别,对应本讲"while 循环"和"do-while 循环"两节。一句话结论:while 可能执行 0 次,do-while 至少执行 1 次。

易错:典型错答是"先执行"或"至少 0 次",都是把两个循环记反了。另一个高频坑是写 do-while 时漏掉末尾分号——必须写成 do { ... } while(条件);,少一个分号就编译报错。

A2 填空题

题目:下面 while 循环执行了______次,输出结果是______。
int i = 10; while (i > 0) { cout << i << " "; i -= 3; }

点击查看参考答案
C++完整参考代码
#include <iostream>
using namespace std;
int main(){
    int i = 10, times = 0;
    while (i > 0) {
        cout << i << " ";
        i -= 3;          // i 的变化:10 → 7 → 4 → 1 → -2
        times++;
    }
    cout << endl;
    cout << "循环执行了 " << times << " 次" << endl;
    return 0;
}
// 实际输出:
// 10 7 4 1
// 循环执行了 4 次

答案:执行 4 次,输出 10 7 4 1。

思路:不要凭感觉数次数,画一张"值变化表"最稳。每轮先判断 i > 0,再输出 i,然后 i 减 3:i = 10(第 1 次)→ 7(第 2 次)→ 4(第 3 次)→ 1(第 4 次)→ -2,此时 i > 0 不成立,循环结束。共 4 次。

考点:while 循环的条件判断时机与循环变量更新,对应"while 循环"和"循环执行次数统计"两节。注意循环次数由"跨过了多少个满足条件的值"决定,不是由起点终点简单相减。

易错:错答"5 次"或"输出到 -2"很常见:前者把 i = -2 那一轮也算进去了(那一轮只在判断处被挡下,循环体没进);后者忘了条件判断在循环体之前,程序永远不会输出 -2。另外别忘了 i -= 3 必须写在循环体内,否则死循环。

A3 写输出

题目:下面程序输出什么?
int i = 1, j = 1; while (i <= 2) { j = 1; while (j <= i) { cout << j; j++; } cout << endl; i++; }

提示:注意 j 在外层循环中重新赋值为 1,每轮内层从 1 开始。
点击查看参考答案
C++完整参考代码
#include <iostream>
using namespace std;
int main(){
    int i = 1, j = 1;
    while (i <= 2) {
        j = 1;                    // 每轮内层都从 1 重新开始
        while (j <= i) {
            cout << j;
            j++;
        }
        cout << endl;
        i++;
    }
    return 0;
}
// 实际输出:
// 1
// 12

答案:两行,第一行 1,第二行 12。

思路:外层变量 i 控制"行",内层变量 j 控制"每行输出几个数字"。i = 1 时内层 j 从 1 到 1,输出 1;i = 2 时内层 j 从 1 到 2,输出 12。注意内层循环结束时用 endl 换行,所以结果是竖着两行。

考点:嵌套 while 循环的跟踪,对应"嵌套循环"一节。关键点是内层循环变量的初始化和上界都由外层变量决定(这里上界是 i),所以每行长度递增。

易错:最容易错的是把 j = 1 漏掉或写错位置——如果 j 不在外层循环体内重置为 1,第一轮结束后 j = 2,第二轮内层就一次都不执行,结果变成只有一行 "1"。另外 endl 在 while 外面、i++ 之前,别把换行写到循环里,否则会多出空行。

A4 填空题

题目:do-while 循环的语法格式是______;while 循环的语法格式是______。两者最大的区别是______。

点击查看参考答案
C++完整参考代码
#include <iostream>
using namespace std;
int main(){
    // 格式一:while (条件) { 循环体 }   —— 先判断,后执行
    int i = 1;
    while (i <= 3) {
        cout << i << " ";
        i++;
    }
    cout << endl;
    // 格式二:do { 循环体 } while (条件); —— 先执行,后判断
    int j = 1;
    do {
        cout << j << " ";
        j++;
    } while (j <= 3);
    cout << endl;
    return 0;
}
// 实际输出:
// 1 2 3
// 1 2 3

答案:do-while 格式 do { 循环体 } while(条件);;while 格式 while(条件) { 循环体 };最大区别:do-while 先执行后判断(至少 1 次),while 先判断后执行(可能 0 次)。

思路:背语法其实是背"判断和执行谁在前"。while 的判断在最前面,所以条件为假时什么都别想执行;do-while 把判断挪到末尾(而且末尾必须跟分号),所以循环体先跑一遍,之后每次判断不是在门口拦人,而是在出口决定"要不要再来一圈"。

考点:两种循环的语法格式与语义差别,对应本讲第一节"while 循环"与第三节"do-while 循环"。这也是后面做"输入密码、最多重试 3 次"这类题目的语法基础。

易错:三个高频错误:① do 后面漏写 while(条件);② } while(条件) 末尾漏分号;③ 把"至少执行 1 次"答成"至少 0 次"。另外别把 while 的条件写成分号结尾(while(条件);),那会让循环体变成空语句,程序直接死循环。

B 组能力提升(4 题)
B1 写输出

题目:下面程序输出什么?

C++待跟踪的程序
int i = 1, j = 1;
while (i <= 3) {
    j = 1;
    while (j <= 3) {
        cout << i << "," << j << " ";
        j++;
    }
    cout << "|";
    i++;
}
提示:外层 i 从 1 到 3,内层 j 每次都从 1 到 3。| 是每行结束的分隔符。
点击查看参考答案
C++完整参考代码
#include <iostream>
using namespace std;
int main(){
    int i = 1, j = 1;
    while (i <= 3) {
        j = 1;                        // 内层变量每轮重置为 1
        while (j <= 3) {
            cout << i << "," << j << " ";
            j++;
        }
        cout << "|";                  // 每行结束后打一个分隔符
        i++;
    }
    cout << endl;
    return 0;
}
// 实际输出:1,1 1,2 1,3 |2,1 2,2 2,3 |3,1 3,2 3,3 |

答案:1,1 1,2 1,3 |2,1 2,2 2,3 |3,1 3,2 3,3 |

思路:外层 i 从 1 走到 3,内层 j 每次都从 1 走到 3。熟练以后可以直接用"组合计数":外层 3 轮 × 内层 3 轮 = 9 对数字,按 i 从小到大、j 从小到大排列即可,不用一格格硬推。

考点:双重循环的遍历顺序,对应"嵌套循环"一节,也是后面枚举、二维图案打印的基础。核心结论:外层走 M 步、内层走 N 步,内层循环体共执行 M × N 次。

易错:① 忘了 j = 1 重置,第二轮开始内层不执行,输出会缺一大半;② 把 cout << "|" 写进内层循环,分隔符会变成每对数字后面都跟一个;③ 用 endl 代替 "|" 会让 9 对数字变成 9 行,和题目的分隔符效果不一致。

B2 编程题

题目:输入一个正整数 N,用 while 循环计算 N!(N 的阶乘),并统计结果中有多少个 0(末尾连续零的个数)。

提示:先算阶乘,再对结果反复 % 10 和 / 10 统计末尾零的个数。
点击查看参考答案
C++参考代码
#include <iostream>
using namespace std;
int main(){
    int n;
    cin >> n;
    int i = 1;
    long long fact = 1;
    while (i <= n) {
        fact *= i;
        i++;
    }
    cout << "fact = " << fact << endl;
    int zeros = 0;
    while (fact % 10 == 0) {
        zeros++;
        fact /= 10;
    }
    cout << "末尾零的个数 = " << zeros << endl;
    return 0;
}
示例:输入 10 → fact = 3628800,末尾有 2 个零。

思路:先用 while 循环连乘求出 n!,再用 while 反复去掉末位的 0 来数出个数。

易错:用 int 存阶乘会溢出(20! 就超过 int 范围);把「多少个 0」理解成全部数字中 0 的个数,而不是末尾连续零的个数。

B3 写输出

题目:下面程序输出什么?(注意 break 的作用)

C++break 跟踪
int i = 1;
while (i <= 10) {
    if (i == 5) break;
    cout << i << " ";
    i++;
}
cout << "done" << endl;
提示:break 会立即跳出循环,不执行循环体剩余部分。
点击查看参考答案
C++完整参考代码
#include <iostream>
using namespace std;
int main(){
    int i = 1;
    while (i <= 10) {
        if (i == 5) break;     // i=5 时立即跳出整个 while
        cout << i << " ";
        i++;
    }
    cout << "done" << endl;
    return 0;
}
// 实际输出:1 2 3 4 done

答案:1 2 3 4 done

思路:沿着 i = 1,2,3,4 走,每轮都满足 i == 5 吗?不满足,所以正常输出。走到 i = 5 时,if 条件成立,break 执行,循环立刻结束——注意 break 之后的那行输出和 i++ 都不会执行,程序跳到循环后面继续跑,于是接着打印 done。

考点:break 语句的作用范围,对应本讲"break 与 continue"一节。break 结束的是离它最近的那一层循环,循环后面的代码照常继续执行。

易错:① 错答成 1 2 3 4 5 done——把 i = 5 也输出了一遍,其实 break 在输出之前就跳走了;② 错答成 1 2 3 4——以为 break 会让整个程序结束,其实它只跳出循环,后面的 cout << "done" 仍然会执行(那要 return 或 exit 才能做到);③ 把 break 和 continue 弄混:continue 只会跳过本轮剩下的部分,循环还会继续。

B4 判断题

题目:判断 —— continue 语句会跳出整个循环,不再执行后续的循环。

提示:回忆 continue 和 break 的区别。
点击查看参考答案
答案:错误。continue 只跳过本次循环体的剩余部分,进入下一次条件判断,循环本身不会跳出。break 才会跳出整个循环。

考点:continue 只结束本次循环、进入下一次条件判断,而 break 才跳出整个循环。

易错:把 continue 当成 break 使用,以为它能提前结束循环。

C 组挑战拓展(4 题)
C1 编程题

题目:输入一个正整数 N,打印 N 行的杨辉三角(帕斯卡三角形)的前 N 行。

杨辉三角特点:每行首尾都是 1,中间的数等于上一行同列和前一列之和。例如 5 行:

    1
   1 1
  1 2 1
 1 3 3 1
1 4 6 4 1
提示:用二维数组存储杨辉三角的值,外层控制行,内层控制列。这个题目可以结合数组知识一起完成。
点击查看参考答案
C++参考代码
#include <iostream>
using namespace std;
int main(){
    int n;
    cin >> n;
    int a[100][100] = {0};
    int i = 1;
    while (i <= n) {
        int j = 1;
        while (j <= i) {
            if (j == 1 || j == i) {
                a[i][j] = 1;
            } else {
                a[i][j] = a[i-1][j-1] + a[i-1][j];
            }
            cout << a[i][j] << " ";
            j++;
        }
        cout << endl;
        i++;
    }
    return 0;
}
说明:杨辉三角是嵌套循环的经典应用。每行首尾为 1,中间值 = 上一行同列 + 上一行前一列。等学了数组和 for 循环后,可以用更简洁的写法。

思路:用二维数组一行一行构造,每行首尾固定为 1,中间元素由上一行相邻两数相加得到。

易错:数组初值没清零导致中间元素带脏数据;内层循环边界写成 j < i 会漏掉行末的 1。

C2 编程题

题目:输入一个正整数 N,用 while 循环和 do-while 循环分别统计 1 到 N 中有多少个数字包含字母 "1"(如 1、11、101 等)。

提示:用 % 10 取个位、/ 10 去个位的方法逐位检查。注意 do-while 版本需要特殊处理 N=0 的情况。
点击查看参考答案
C++参考代码
#include <iostream>
using namespace std;
int main(){
    int n;
    cin >> n;
    int count = 0;
    int i = 1;
    while (i <= n) {
        int temp = i;
        bool hasOne = false;
        while (temp > 0) {
            if (temp % 10 == 1) {
                hasOne = true;
                break;
            }
            temp /= 10;
        }
        if (hasOne) count++;
        i++;
    }
    cout << "1到" << n << "中包含1的数有 "
         << count << " 个" << endl;
    return 0;
}
示例:输入 20 → 输出"1到20中包含1的数有 11 个"(1,10,11,12,13,14,15,16,17,18,19 共 11 个,注意 1 本身也包含 1)。

思路:外层枚举 1~N,内层用 % 10 逐位检查是否出现数字 1,一旦找到就跳出内层。

C3 编程题

题目:用 do-while 循环实现一个"密码验证"程序:允许用户最多输入 3 次密码,输入正确则提示"登录成功",3 次都不对则提示"账户已锁定"。

提示:用计数器记录输入次数,do-while 至少要求输入一次,结合 if 判断是否输入正确。
点击查看参考答案
C++参考代码
#include <iostream>
#include <string>
using namespace std;
int main(){
    string password;
    int attempts = 0;
    bool success = false;
    do {
        cout << "请输入密码:";
        cin >> password;
        attempts++;
        if (password == "123456") {
            success = true;
            break;
        }
        cout << "密码错误!剩余机会:"
             << (3 - attempts) << endl;
    } while (attempts < 3);
    if (success) {
        cout << "登录成功!" << endl;
    } else {
        cout << "账户已锁定!" << endl;
    }
    return 0;
}
说明:do-while 保证至少要求输入一次密码,最多 3 次机会。用 break 在密码正确时立即跳出循环,用 success 标志记录是否登录成功。

思路:用 do-while 先让用户输入再判断,配合 attempts 计数和 success 标志控制最多试三次。

易错:用普通 while 先判断条件,导致一次密码都没输入就退出;次数判断写成 < 3 会少给一次机会。

C4 编程题

题目:输入两个正整数 a 和 b,用 while 循环求它们的最大公约数(GCD)。

欧几里得算法:用较大数除以较小数取余数,然后用较小数除以余数,反复操作直到余数为 0,此时的除数就是最大公约数。

提示:核心循环:while(b != 0) { int t = b; b = a % b; a = t; } 最后 a 就是 GCD。
点击查看参考答案
C++参考代码
#include <iostream>
using namespace std;
int main(){
    int a, b;
    cin >> a >> b;
    int x = a, y = b;
    while (y != 0) {
        int t = y;
        y = x % y;
        x = t;
    }
    cout << "GCD(" << a << ", " << b
         << ") = " << x << endl;
    return 0;
}
示例:输入 48 18 → 输出 GCD(48, 18) = 6。原理:48 % 18 = 12,18 % 12 = 6,12 % 6 = 0,余数为 0 时除数 6 就是最大公约数。这是竞赛中常用的欧几里得算法,时间复杂度 O(log min(a,b))。

思路:用辗转相除法,每轮把除数变成新的被除数、余数变成新的除数,直到余数为 0。

易错:直接对 a、b 操作会破坏原值,最后无法输出原数;取余前没保证除数不为 0 会导致除以零。