适用对象: 已完成第 2 课(运算符 + if 判断)的同学 使用方式: 自学讲义。本课和第 2 课一样偏厚——知识点 4 个、练习 8 题,做完大约需要 1.5 小时 学完本课,你应该能:
while 和 for i in range(...)
翻译成 C++ 的 while 和 forbreak /
continue(这两个关键字两边长得一模一样)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)。
只有这几个不同:
while (n <= 10){ }s += n、n += 1
这类"复合赋值"两边写法完全一样(+= -=
*= /= %= 都能直接搬)亲手做一次这个错误(运行前想清楚会发生什么,再按 F11,如果卡住按 Ctrl+C 强制结束):
int n = 1, s = 0;
while (n <= 10) {
s += n;
// 忘了写 n += 1,n 永远是 1
}
cout << s << endl;
这个程序会永远运行下去,屏幕什么都不显示——因为
n 永远满足 n <= 10,条件永远为真。
📝 写 while 循环前先想清楚两件事:循环变量怎么变化?什么时候停? 这两个想不清楚,十有八九是死循环。
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 + 1;i-- 就是
i = i - 1。 这两个符号以后天天见,记熟。
新建 check1.cpp:用 for 循环倒着输出 10 到
1(每个数换行)。
好消息:这两个关键字 Python 和 C++长得完全一样,意思也完全一样:
break:立刻跳出整个循环continue:跳过这一轮剩下的代码,直接进入下一轮找 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
几乎所有涉及循环的题目,都是下面三种模式的组合:
① 累加(求和):
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
是当前的擂台冠军,每来一个新数就挑战一次,赢了就换人当冠军,输了冠军不变——扫完一遍,剩下的就是真正的最大值。
错误 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
课说过"结果不对先查 == 有没有写成
=",这条规则在循环条件里同样适用。
从这一课起,每道题的帮助分成三层,一层一层点开:
| 层 | 里面是什么 | 什么时候点开 |
|---|---|---|
| 提示 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;
}
程序停不下来,只有一个原因:循环条件永远为真。
那就盯着条件看:i <= 5。这个条件里只有一个变量
i。
问自己一句:i
在整段代码里,被改过吗?
while 循环要活下去,靠三件东西:
int i = 1; ✅ 有i <= 5 ✅ 有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)回第 2 节那张 range() 翻译表。
range 有三个参数:起点、终点、步长。C++ 的
for
也有三段:for (初始化; 条件; 每轮末尾做什么)。
问自己:这三个参数分别落到 for
的哪一段里?
两个容易写错的地方:
range
的终点是取不到的——range(1, 11) 到 10
为止。所以条件写 i < 11,不是
i <= 11i += 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
第 4 节三套路里的第一个:累加。
准备一个"篮子"变量,循环里一次往里放一个数。
动手之前先答一个问题:这个篮子,应该写在 for
的里面还是外面?
int sum = 0; // 必须在 for 外面
for (int i = 1; i <= n; i++) sum += i;两个坑,都在这一行上:
for 里面:每轮重新变成
0,最后只剩下最后一个数int sum; 不写
= 0:里面是内存里的垃圾值,结果时对时错——第
1 次课那道改错题就是这个#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
还是累加套路,只是把加号换成乘号:sum += i
变成 fact *= i。
⚠️ 但有一样东西必须跟着变。
想一想:如果篮子的初值还是 0,0 × 1 × 2 × 3 × …
等于多少?
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
三套路里的第二个:计数。准备一个
cnt = 0,符合条件就 cnt++。
⚠️ 先答一个问题再动手:这 6 个数,一定要先全部存下来吗?
不用存。 读一个、判一个、扔掉——循环里就借一个
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
三套路里的第三个:打擂台。
擂台赛开打之前,得先有一个人站在台上当冠军。
⚠️ 问题就在这里:第一任擂主该选谁?很多人随手写
int maxV = 0;——别这么写。
想一想,什么样的输入会让这个写法出错?
擂主的初值,必须是输入里真实存在的一个数。
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:循环一次都不跑,maxV 和
minV
都是那唯一的数,输出正确。边界能自己对,说明初值取对了。
对照:如果先把数存起来再打擂台(数组是下一课的内容,现在看看就行):
#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
输出样例:
猜大了
猜小了
猜对了!
主体是「最多 5 次」的 for 循环 + 猜对时
break 跳出。这部分第 3 节讲过。
⚠️ 难点不在循环,在循环结束之后。 循环停下来有两种可能:猜对了跳出,和5 次用完了自然结束。这两种要输出的东西不一样。
问自己:循环结束以后,程序怎么知道刚才是哪一种?
立一面旗子。 进循环前把旗子放倒,猜对了就升起来:
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
题面已经把两把钥匙给你了:n % 10
拿到最后一位,n / 10 去掉最后一位。
所以每一轮干两件事:打印最后一位、砍掉最后一位。
⚠️ 剩下的问题只有一个:循环什么时候停?
提示:砍到最后,n 会变成什么?
砍到最后 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")。找不到就两种都想一遍,别自己随便假设一个。
循环里面还能再放一个循环,叫嵌套循环——外层走一步,内层要跑完一整圈:
#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]; // ③ 打擂台两个新坑:
| 症状 | 原因 |
|---|---|
| 程序卡住不动 | 忘了让循环变量变化(死循环) |
| 结果是垃圾值或崩溃 | 数组下标越界(<= 该用 <) |
本课新符号:
while、for、break、continue、bool、true/false、!(在
while 条件里)
range(n)、range(a,b)、range(n,0,-1)
翻译成 C++ 写出来