B 班 第 3 课:循环

适用对象: 已完成第 2 课(运算符 + if 判断)的同学 使用方式: 自学讲义。本课和第 2 课一样偏厚——知识点 4 个、练习 8 题,做完大约需要 1.5 小时 学完本课,你应该能:

  1. 把 Python 的 whilefor i in range(...) 翻译成 C++ 的 whilefor
  2. 熟练使用 break / continue(这两个关键字两边长得一模一样)
  3. 认出并写出三种循环套路:累加、计数、打擂台找最值
  4. 又认识 1 个报错 + 1 个不报错但结果全错的坑

1. while 循环:几乎不用改(15 分钟)

Python 你这样写:

n = 1
s = 0
while n <= 10:
    s += n
    n += 1
print(s)

C++ 改成这样:

#include <bits/stdc++.h>
using namespace std;

int main() {
    int n = 1, s = 0;
    while (n <= 10) {
        s += n;
        n += 1;
    }
    cout << s << endl;
    return 0;
}

输出:55(1 加到 10)。

只有这几个不同:

⚠️ 大坑:忘记改变循环变量 = 死循环

亲手做一次这个错误(运行前想清楚会发生什么,再按 F11,如果卡住按 Ctrl+C 强制结束):

int n = 1, s = 0;
while (n <= 10) {
    s += n;
    // 忘了写 n += 1,n 永远是 1
}
cout << s << endl;

这个程序会永远运行下去,屏幕什么都不显示——因为 n 永远满足 n <= 10,条件永远为真。

📝 写 while 循环前先想清楚两件事:循环变量怎么变化?什么时候停? 这两个想不清楚,十有八九是死循环。


2. for 循环:range() 翻译表(20 分钟)

Python 的 for i in range(...) 是 C++ 里最常用的循环写法 for (初始; 条件; 变化) 的简化版。逐个对照:

Python 你这样写:

for i in range(5):
    print(i)

C++ 改成这样:

#include <bits/stdc++.h>
using namespace std;

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

输出:0 1 2 3 4

for 括号里的三段话,分号隔开,从左到右读:先做①一次 → 检查②是否满足 → 循环体执行完做③ → 回到检查②,直到②不满足才停。

range() 翻译表:

Python C++ 含义
range(n) for (int i = 0; i < n; i++) 0 到 n-1
range(a, b) for (int i = a; i < b; i++) a 到 b-1
range(a, b, 2) for (int i = a; i < b; i += 2) 步长 2
range(n, 0, -1) for (int i = n; i > 0; i--) 倒着数到 1

📝 i++ 就是 i = i + 1i-- 就是 i = i - 1 这两个符号以后天天见,记熟。

动手检查点 1(5 分钟)

新建 check1.cpp:用 for 循环倒着输出 10 到 1(每个数换行)。


3. break 和 continue:两边写法一模一样(10 分钟)

好消息:这两个关键字 Python 和 C++长得完全一样,意思也完全一样:

找 100 的最小因数(大于 1):

#include <bits/stdc++.h>
using namespace std;

int main() {
    for (int i = 2; i <= 100; i++) {
        if (100 % i == 0) {
            cout << i << endl;
            break;    // 找到就立刻停,不用再往后找
        }
    }
    return 0;
}

输出:2

跳过 3 的倍数,输出 1~10 剩下的数:

#include <bits/stdc++.h>
using namespace std;

int main() {
    for (int i = 1; i <= 10; i++) {
        if (i % 3 == 0) continue;   // 是 3 的倍数就跳过这一轮
        cout << i << " ";
    }
    cout << endl;
    return 0;
}

输出:1 2 4 5 7 8 10


4. 三种循环套路:累加、计数、打擂台(15 分钟)

几乎所有涉及循环的题目,都是下面三种模式的组合:

① 累加(求和):

int sum = 0;
for (int i = 1; i <= n; i++) sum += i;

② 计数(数满足条件的个数):

int cnt = 0;
for (int i = 1; i <= n; i++) {
    if (a[i] % 2 == 0) cnt++;   // 满足条件就 +1
}

③ 打擂台(找最大/最小值,不用 sort):

int maxV = a[0];              // 先假设第一个是冠军
for (int i = 1; i < n; i++) {
    if (a[i] > maxV) maxV = a[i];   // 挑战者更强就换冠军
}

📝 打擂台的比喻: maxV 是当前的擂台冠军,每来一个新数就挑战一次,赢了就换人当冠军,输了冠军不变——扫完一遍,剩下的就是真正的最大值。


5. 读错误:1 个报错 + 1 个不报错(10 分钟)

错误 1:数组越界(不一定报错,但结果乱套)

int a[5] = {1, 2, 3, 4, 5};
for (int i = 0; i <= 5; i++) {      // 条件用了 <=,多循环了一次
    cout << a[i] << " ";
}

a 只有下标 0~4,i == 5 时访问 a[5]数组外面的内存——编译器大概率不会报错,但会打印一个乱七八糟的数字(垃圾值),运气不好还会让程序直接崩溃。

📝 数组开 5 个,有效下标是 0~4,循环条件永远用 <,不要手滑写成 <=

错误 2:循环条件里把 == 写成 =(不报错,逻辑全崩)

#include <bits/stdc++.h>
using namespace std;

int main() {
    int i = 0;
    while (i = 5) {          // 想写 i == 5(判断),手滑写成 i = 5(赋值)
        cout << "循环了一次" << endl;
        break;                // 靠这行强制跳出,不然是死循环
    }
    return 0;
}

这行的道理和第 2 课讲过的 if (x = 5) 一样:赋值表达式的值是 5(非零 = 真),条件永远成立。第 2 课说过"结果不对先查 == 有没有写成 =",这条规则在循环条件里同样适用。


6. 本课练习(35 分钟起步,做不完当课后作业)

怎么用这些练习(比题目本身更重要,先读完这几行)

从这一课起,每道题的帮助分成三层,一层一层点开:

里面是什么 什么时候点开
提示 1:往哪个方向想 只告诉你用哪个套路 + 一个反问,不给代码 自己想满 5 分钟还没头绪
提示 2:关键的那一步 最关键的一两行 + 这题的坑 看了提示 1 又想了 5 分钟还写不动
参考答案 完整代码 提示 2 也试过了,确实写不出来

⚠️ 直接翻到参考答案,等于这道题没做。 你会觉得"我看懂了",然后下次遇到同类题还是写不出来——看懂和写得出来,中间隔着很远的距离。

📝 每点开一层,在纸上记一笔:我是卡在哪儿才看的。 一学期下来,这张纸比任何讲义都准确地告诉你:你到底缺什么。

📝 看完参考答案之后,把它关掉,从空文件重写一遍。 这一步不做,前面全白费。


⭐ 基础题 1:找错(不用电脑,先用眼睛)

下面的程序想输出 1~5,但会陷入死循环,找出问题:

#include <bits/stdc++.h>
using namespace std;

int main() {
    int i = 1;
    while (i <= 5) {
        cout << i << " ";
    }
    cout << endl;
    return 0;
}
提示 1:往哪个方向想

程序停不下来,只有一个原因:循环条件永远为真

那就盯着条件看:i <= 5。这个条件里只有一个变量 i

问自己一句:i 在整段代码里,被改过吗?

提示 2:关键的那一步

while 循环要活下去,靠三件东西:

  1. 初值——int i = 1; ✅ 有
  2. 条件——i <= 5 ✅ 有
  3. 变化——让 i 一步步走向条件不成立 ❓

数一数这段代码有几件。 缺的那一件,就写在循环体里。

参考答案

循环体里没有让 i 变化,i 永远是 1,永远满足 i <= 5。应该在循环体里加一行 i++;

#include <bits/stdc++.h>
using namespace std;

int main() {
    int i = 1;
    while (i <= 5) {
        cout << i << " ";
        i++;
    }
    cout << endl;
    return 0;
}

⭐ 基础题 2:翻译(文件名 t2.cpp

把下面的 Python 程序翻译成 C++:

for i in range(1, 11, 2):
    print(i)
提示 1:往哪个方向想

回第 2 节那张 range() 翻译表。

range 有三个参数:起点、终点、步长。C++ 的 for 也有三段:for (初始化; 条件; 每轮末尾做什么)

问自己:这三个参数分别落到 for 的哪一段里?

提示 2:关键的那一步

两个容易写错的地方:

  1. ⚠️ range 的终点是取不到的——range(1, 11) 到 10 为止。所以条件写 i < 11不是 i <= 11
  2. 步长写在第三段i += 2,不是 i++
参考答案
#include <bits/stdc++.h>
using namespace std;

int main() {
    for (int i = 1; i < 11; i += 2) {
        cout << i << endl;
    }
    return 0;
}

输出 1、3、5、7、9,每个一行。


⭐ 基础题 3:累加 1~n(文件名 t3.cpp

输入一个正整数 n,输出 1 加到 n 的和。

输入样例:
100

输出样例:
5050
提示 1:往哪个方向想

第 4 节三套路里的第一个:累加

准备一个"篮子"变量,循环里一次往里放一个数。

动手之前先答一个问题:这个篮子,应该写在 for 的里面还是外面?

提示 2:关键的那一步
int sum = 0;                 // 必须在 for 外面
for (int i = 1; i <= n; i++) sum += i;

两个坑,都在这一行上:

参考答案
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    cin >> n;
    int sum = 0;
    for (int i = 1; i <= n; i++) sum += i;
    cout << sum << endl;
    return 0;
}

📝 交之前自测 n = 1:循环只跑一轮,sum 应该是 1。最小情况是所有题的通用自测,花 10 秒,能挡掉一大半错误。


⭐⭐ 实战题 4:阶乘(文件名 t4.cpp

输入一个正整数 n(1 ≤ n ≤ 12),输出 n 的阶乘(n! = 1×2×3×…×n)。

输入样例:
5

输出样例:
120
提示 1:往哪个方向想

还是累加套路,只是把加号换成乘号:sum += i 变成 fact *= i

⚠️ 但有一样东西必须跟着变。 想一想:如果篮子的初值还是 0,0 × 1 × 2 × 3 × … 等于多少?

提示 2:关键的那一步
long long fact = 1;          // 乘法的起点是 1,不是 0
for (int i = 1; i <= n; i++) fact *= i;

📝 为什么题面限定 n ≤ 12? 12! = 479001600,约 4.79 亿;int 最大约 21 亿,刚好装得下。13! = 62 亿,直接溢出——算出来是个莫名其妙的负数,还不报错。

所以这里用 long long(能装到约 922 亿亿)留一点余量。大数值的正式处理以后学。

参考答案
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    cin >> n;
    long long fact = 1;
    for (int i = 1; i <= n; i++) fact *= i;
    cout << fact << endl;
    return 0;
}

n 稍微大一点(比如 20!)int 就装不下了,这里用 long long 留一点余量——大数值的正式处理以后学


⭐⭐ 实战题 5:数偶数个数(文件名 t5.cpp

输入 n 和 n 个整数,输出其中偶数的个数。

输入样例:
6
3 8 5 12 7 4

输出样例:
3
提示 1:往哪个方向想

三套路里的第二个:计数。准备一个 cnt = 0,符合条件就 cnt++

⚠️ 先答一个问题再动手:这 6 个数,一定要先全部存下来吗?

提示 2:关键的那一步

不用存。 读一个、判一个、扔掉——循环里就借一个 x 反复用:

int x;
cin >> x;
if (x % 2 == 0) cnt++;       // ⚠️ cnt++ 要在 if 的大括号里面

⚠️ cnt++ 写到 if 外面,就变成"数了多少个数",不管是不是偶数——结果永远等于 n

📝 判偶数用 x % 2 == 0别写成 x / 2,那是除法不是判断。

参考答案
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    cin >> n;
    int cnt = 0;
    for (int i = 0; i < n; i++) {
        int x;
        cin >> x;
        if (x % 2 == 0) cnt++;
    }
    cout << cnt << endl;
    return 0;
}

这题不用数组也能做——读一个数马上判断,不用存下来。


⭐⭐ 实战题 6:找最大值和最小值(文件名 t6.cpp

输入 n 和 n 个整数,输出其中的最大值和最小值(空格分隔)。不许用 max/min/sort,练打擂台。

输入样例:
5
7 2 9 4 6

输出样例:
9 2
提示 1:往哪个方向想

三套路里的第三个:打擂台

擂台赛开打之前,得先有一个人站在台上当冠军。

⚠️ 问题就在这里:第一任擂主该选谁?很多人随手写 int maxV = 0;——别这么写。 想一想,什么样的输入会让这个写法出错?

提示 2:关键的那一步

擂主的初值,必须是输入里真实存在的一个数。

maxV = 0 等于偷偷做了个假设:"数据里一定有正数"。⚠️ 如果输入全是负数(比如 -5 -2 -8),它会输出 0——而 0 根本不在输入里

正确做法:先读进来第一个数,让它当初任冠军,剩下 n−1 个来挑战:

int x;
cin >> x;                      // 第一个数
int maxV = x, minV = x;        // 两个擂台的初任冠军都是它

两个擂台同时打——一个专收最大值挑战,一个专收最小值挑战,写在同一个循环里。

参考答案
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    cin >> n;
    int x;
    cin >> x;                       // 先把第一个数读进来
    int maxV = x, minV = x;         // 它同时当两个擂台的初任冠军
    for (int i = 2; i <= n; i++) {  // 剩下的 n-1 个依次上台挑战
        cin >> x;
        if (x > maxV) maxV = x;     // 比冠军大 → 换人
        if (x < minV) minV = x;     // 比冠军小 → 换人
    }
    cout << maxV << " " << minV << endl;
    return 0;
}

📝 这题从头到尾不需要数组——读一个比一个,比完就扔。变量 x 一个人反复用了 n 次。

📝 自测 n = 1:循环一次都不跑,maxVminV 都是那唯一的数,输出正确。边界能自己对,说明初值取对了。

对照:如果先把数存起来再打擂台(数组是下一课的内容,现在看看就行):

#include <bits/stdc++.h>
using namespace std;

int a[1005];                        // 数组开在 main 外面

int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i];
    int maxV = a[0], minV = a[0];   // 同样是拿真实存在的数当初值
    for (int i = 1; i < n; i++) {
        if (a[i] > maxV) maxV = a[i];
        if (a[i] < minV) minV = a[i];
    }
    cout << maxV << " " << minV << endl;
    return 0;
}

⚠️ 两种写法的擂主初值都是 a[0] / 第一个数,不是 0。 这一条和用不用数组无关,是打擂台本身的规矩。


⭐⭐⭐ 冲刺题 7:猜数字游戏(文件名 t7.cpp

程序心里"想"了一个数 answer = 42(直接写死在代码里)。玩家最多猜 5 次,每次输入一个数:猜大了输出 猜大了,猜小了输出 猜小了,猜对了输出 猜对了!立刻结束(不用等猜满 5 次)。5 次都没猜中,输出 次数用完了,答案是 42

输入样例(每行一次猜测):
50
30
42

输出样例:
猜大了
猜小了
猜对了!
提示 1:往哪个方向想

主体是「最多 5 次」的 for 循环 + 猜对时 break 跳出。这部分第 3 节讲过。

⚠️ 难点不在循环,在循环结束之后。 循环停下来有两种可能:猜对了跳出,和5 次用完了自然结束。这两种要输出的东西不一样。

问自己:循环结束以后,程序怎么知道刚才是哪一种?

提示 2:关键的那一步

立一面旗子。 进循环前把旗子放倒,猜对了就升起来:

bool win = false;            // 旗子:还没猜中
// ... 猜对时:
win = true;
break;

循环外面再看一眼旗子:

if (!win) {                  // 旗子没升起来 = 5 次都没中
    cout << "次数用完了,答案是 42" << endl;
}

⚠️ if (!win) 必须写在循环外面。 写在里面,每猜错一次就输出一遍。

📝 bool 是个新类型,只能装 true / false!win 就是 Python 的 not win

参考答案
#include <bits/stdc++.h>
using namespace std;

int main() {
    int answer = 42;
    bool win = false;                 // 记录有没有猜中
    for (int i = 1; i <= 5; i++) {
        int guess;
        cin >> guess;
        if (guess == answer) {
            cout << "猜对了!" << endl;
            win = true;
            break;                     // 猜对了,不用再猜
        } else if (guess > answer) {
            cout << "猜大了" << endl;
        } else {
            cout << "猜小了" << endl;
        }
    }
    if (!win) {
        cout << "次数用完了,答案是 42" << endl;
    }
    return 0;
}

bool win 是一个新类型:只能装 true(真)或 false(假),刚好用来记"猜中了没有"这种是非题。!win 就是"win 不成立",对应 Python 的 not win


⭐⭐⭐ 冲刺题 8:数字反转(文件名 t8.cpp

输入一个正整数,把它的每一位倒过来输出(比如 1234 → 4321)。提示: 上节课学的整数除法 / 和取余 % 联手就能拆出每一位——n % 10 拿到最后一位,n / 10 去掉最后一位,循环到 n 变成 0。

输入样例:
1234

输出样例:
4321
提示 1:往哪个方向想

题面已经把两把钥匙给你了:n % 10 拿到最后一位,n / 10 去掉最后一位。

所以每一轮干两件事:打印最后一位、砍掉最后一位

⚠️ 剩下的问题只有一个:循环什么时候停? 提示:砍到最后,n 会变成什么?

提示 2:关键的那一步

砍到最后 n 会变成 0,所以条件就是 while (n > 0)

while (n > 0) {
    cout << n % 10;    // 先打印最后一位
    n = n / 10;         // 再砍掉它
}

⚠️ 这两行的顺序不能反。 先砍再打印,最后一位就丢了。

⚠️ cout << n % 10 后面别加空格或 endl,题目要求连成一串。

参考答案
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    cin >> n;
    while (n > 0) {
        cout << n % 10;    // 打印最后一位
        n = n / 10;         // 去掉最后一位
    }
    cout << endl;
    return 0;
}

每一轮打印一位,n 越变越小,直到变成 0 循环结束——这就是"边拆边打印"的经典写法。

⚠️ 拿 1200 试一下:输出是 0021 因为是一位一位打印的,反转后跑到开头的那两个 0 也照样打了出来。

📝 换一种写法,结果就不一样了。 不边拆边打印,而是把每一位拼成一个新的整数

#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    cin >> n;
    int rev = 0;
    while (n > 0) {
        rev = rev * 10 + n % 10;   // 已有的整体左移一位,新拆下的补到个位
        n = n / 10;
    }
    cout << rev << endl;
    return 0;
}

手玩一遍 1234:rev 依次是 4 → 43 → 432 → 4321。而 1200 依次是 0 → 0 → 2 → 21,输出 21——因为 rev 是一个整数,整数没有前导零这回事。

⚠️ 同一道题,两种写法在样例 1234 上一模一样,在 1200 上却不一样。 题面没规定 1200 该输出什么,所以你光看样例是看不出该用哪个的

📝 遇到这种地方,回题面找那句限定条件(比如"保证输入的末位不为 0")。找不到就两种都想一遍,别自己随便假设一个


7. 学有余力:循环套循环(加餐,可跳过)

循环里面还能再放一个循环,叫嵌套循环——外层走一步,内层要跑完一整圈:

#include <bits/stdc++.h>
using namespace std;

int main() {
    for (int i = 1; i <= 3; i++) {
        for (int j = 1; j <= 3; j++) {
            cout << i * j << " ";
        }
        cout << endl;
    }
    return 0;
}

输出:

1 2 3
2 4 6
3 6 9

外层 i 走一次(比如 i=2),内层 j 就要从 1 跑到 3 跑一整圈,打印 2 4 6,再换行;然后外层 i 才走到下一步。完整的九九乘法表就是这个思路扩展到 9×9——这个作为课后自愿挑战,下节课会正式学。


本课要点速查

Python C++ 备注
while cond: while (cond) { 条件加括号,冒号变大括号
range(n) for (int i=0; i<n; i++) 0 到 n-1
range(a, b) for (int i=a; i<b; i++) a 到 b-1
range(a, b, 2) for (int i=a; i<b; i+=2) 步长 2
range(n, 0, -1) for (int i=n; i>0; i--) 倒着数
break break 一模一样
continue continue 一模一样

三种循环套路:

sum += a[i];              // ① 累加
if (cond) cnt++;          // ② 计数
if (a[i] > maxV) maxV = a[i];  // ③ 打擂台

两个新坑:

症状 原因
程序卡住不动 忘了让循环变量变化(死循环)
结果是垃圾值或崩溃 数组下标越界(<= 该用 <

本课新符号: whileforbreakcontinuebooltrue/false!(在 while 条件里)


结束前的自我检查

  1. 不看讲义,把 range(n)range(a,b)range(n,0,-1) 翻译成 C++ 写出来
  2. 练习 8 题至少完成到题 7(⭐⭐⭐ 猜数字),每题所有样例都亲手验证过
  3. 向别人解释:什么样的程序会陷入死循环?怎么检查自己的循环会不会死循环?
  4. 合上讲义,把"打擂台找最大值"的模板一次性写对