Initial commit: C++ courseware

This commit is contained in:
xiaolinquan committed 2026-11-05 22:51:02 +08:00
commit 4a4ee7c287
1789 files changed
+373489

No files matched your search

@@ -0,0 +1,193 @@
## C++ `do-while` 循环 讲义
### **1. 引言**
#### **1.1 问题引入:书本例 4-5 猜数游戏**
想象一下,让机器人在心里想一个1到100之间的整数,然后让大家来猜。
* 你每猜一个数,会给你提示:“太大了 (Too large)” 或 “太小了 (Too small)”。
* 如果你猜中了,它会说:“猜对了 (You are right!!)”。
* 只要没猜对,你就要一直猜下去。
这个“一直猜下去”的过程,在编程中应该如何实现呢?
使用我们**循环**!但用我们之前学过的 `while` 循环,会遇到一个小小的问题。
`while` 循环的特点是“**先判断,后执行**”。它的逻辑是:
`while (猜的数 != 答案) { // 让你继续猜 }`
这里问题就来了:在第一次进入循环,准备进行判断时,你**还根本没有猜过任何数字**!`猜的数` 这个变量里没有有效的值,这个判断也就失去了意义。
这就引出了我们今天要学习的 `do-while` 循环。它专门用来解决这类“**无论如何,都得先做一次**”的问题。除了猜数游戏,还有很多类似场景:
* **ATM取款**:你必须先输入一次密码,银行系统才能去验证它是否正确。
* **游戏菜单**:程序至少要先显示一次菜单(开始游戏、加载存档、退出),你才能进行选择。
* **输入验证**:程序至少要先让你输入一次,才能判断你的输入是否合规。
---
### **2. 核心思想**
#### **2.1 `while` 循环**
我们先尝试用 `while` 循环来解决猜数问题,看看会发生什么。
```cpp
int main() {
int ans = 42; // 假设答案是42
int guess; // 准备存放你猜的数
// 问题:第一次判断时,guess 是多少?
while (guess != ans) {
cout << "请输入你猜的数字: ";
cin >> guess;
// ...后续判断大小的逻辑...
}
return 0;
}
```
在进入 `while` 的那一刻,`guess` 变量没有被初始化,它里面是一个不确定的值。用一个不确定的值去和 `ans` 比较,结果是完全不可预测的。
为了让 `while` 循环能跑起来,我们需要做一些妥协,比如在循环外先写一次输入代码,像这样:
```cpp
// ...
int ans = 42;
int guess;
cout << "请输入你猜的数字: ";
cin >> guess; // 在循环外先猜一次
while (guess != ans) {
// ...判断大小的逻辑...
cout << "请再猜一次: ";
cin >> guess; // 在循环内又写了一遍
}
// ...
```
但是这样做的话, `cin >> guess;` 这句代码重复了,对于这种先执行,后判断的流程,C++有更加优雅的方式完成。
#### **2.2 `do-while` **
`do-while` 循环就是“**先执行,后判断**”。它保证循环体内的代码**至少会被执行一次**。
它的语法结构如下:
```cpp
do {
// 循环体
// 这里的代码保证,无论如何都会先被执行一次。
} while (循环条件); // 注意这里的分号
```
* **`do`**:一个关键字,代表循环体开始,并且不管三七二十一,先把 `{}` 里面的代码做一遍!
* **`{ ... }`**:循环体,和 `while` 循环一样,是需要重复执行的代码。
* **`while (循环条件);`**:在循环体执行**之后**,才在这里进行条件判断。如果条件为真,就跳回到 `do` 的位置,开始下一次循环;如果为假,则循环结束,继续执行后面的代码。
#### **2.3 步骤拆解**
`do-while` 的执行流程我们可以拆解为以下几步:
1. **无条件执行**:程序遇到 `do`,先直接进入 `{}` 执行循环体内的所有语句。
2. **事后判断**:循环体执行完毕后,来到 `while(条件)` 这一行,计算括号内条件表达式的真假。
3. **决策**:
* 如果条件为 **真 (true)**,程序跳转回 `do` 关键字处,准备开始新一轮的循环(重复步骤1和2)。
* 如果条件为 **假 (false)**,循环正式结束。程序会越过这个循环结构,去执行 `while(...);` 后面的代码。
---
### **3. 实例**
#### **3.1 具体案例**
假设小洛机器人心里想的数字是 **42**,那么我们就可以通过`do-while`写出如下的代码。
**代码片段:**
```cpp
int ans = 42;
int guess;
do {
cout << "猜一个1-100的数: ";
cin >> guess;
if (guess > ans) { cout << "太大了!\n"; }
if (guess < ans) { cout << "太小了!\n"; }
} while (guess != ans);
cout << "猜对了!!\n";
```
---
### **4. 代码实现**
现在,我们来完成一个功能完整的“猜数游戏”。输入数字,来判断玩家猜测的数字是否正确,并且给出提示。
注意在真实的游戏中,答案不应该是固定的,而应该是一个随机数。
#### **4.1 完整代码**
```cpp
#include <iostream>
#include <cstdlib> // 用于 rand() 和 srand()
#include <ctime> // 用于 time()
int main() {
// 设置随机数种子
// 使用当前时间作为种子,确保每次运行程序时随机数都不同
srand(time(0));
// 生成一个 1 到 100 之间的随机数
// rand() % 100 的范围是 [0, 99],加1后范围变为 [1, 100]
int ans = rand() % 100 + 1;
int guess; // 用来存储玩家猜测的数字
// 3. 使用 do-while 循环让玩家猜测
do {
cin >> guess;
if (guess > ans) {
cout << "太大了!\n";
} else if (guess < ans) {
cout << "太小了!\n";
}
} while (guess != ans); // 只要猜的数不等于答案,就继续循环
// 4. 循环结束,说明猜对了
cout << "恭喜你,猜对了!\n";
return 0;
}
```
#### **4.2 代码解读**
* **`#include <cstdlib>` 和 `<ctime>`**:这两个头文件是生成“随机数”的关键。`<cstdlib>` 包含了 `rand()`(生成伪随机数)和 `srand()`(种子),`<ctime>` 包含了 `time()`(获取当前时间)。
* **`srand(time(0));`**:这是非常关键的一行。计算机生成的随机数其实是“伪随机”,它由一个“种子”开始,通过一个固定算法算出一串数。如果种子不变,那串数也永远不变。`time(0)` 能获取从1970年1月1日到现在的秒数,这个数时刻在变,是作为种子的绝佳选择。**这行代码通常在程序开始时只执行一次**。
* **`rand() % 100 + 1`**:这是一个生成特定范围随机数的经典公式。
* `rand()` 生成一个从 0 到 `RAND_MAX`(一个很大的数)的整数。
* `% 100` 对100取余,结果一定是 0 到 99 之间。
* `+ 1` 将 `[0, 99]` 的范围整体平移,得到 `[1, 100]`。
* **`do { ... } while (guess != ans);`**:整个“输入-判断-提示”的逻辑被包裹在 `do` 的循环体中,确保玩家至少能猜一次。然后 `while` 在每次猜测后检查是否猜中,决定是继续还是结束。
## **5. 总结**
**在什么时候用 `do-while`,什么时候用 `while`?**
* 标准就是:**循环体是否需要无条件地至少执行一次?**
* 如果**是**,比如菜单显示、输入验证等,`do-while` 是最佳选择。
* 如果**否**,也就是可能一次都不需要执行,那么 `while` 更合适。
#### **对比:`while` vs `do-while`**
| 特性 | `while` 循环 | `do-while` 循环 |
| :----------- | :--------------------------- | :------------------------------- |
| **核心原则** | **先判断,后执行** | **先执行,后判断** |
| **执行保证** | 可能执行 **0** 次或多次 | **至少**执行 **1** 次 |
| **语法结构** | `while (条件表达式) { ... }` | `do { ... } while (条件表达式);` |
@@ -0,0 +1,31 @@
### C++ do-while 循环 课后作业 - **答案**
#### **一、 填空题**
1. 执行,判断
2. 1 (或一)
3. 分号 (`;`)
4. `do-while`
5. 种子,头文件
#### **二、 单项选择题**
1. C 2. B 3. C 4. C 5. B 6. B 7. C 8. B 9. D 10. D
**选择题解析:**
2. `i` 初始值为10。先执行循环体,输出1个"Hello",然后`i`变为11。此时判断条件`i < 10` (11 < 10) 为假,循环结束。
6. `n` 初始值为5。先执行循环体,输出 "5 ",然后`n`变为4。此时判断条件`n > 5` (4 > 5) 为假,循环结束。
7. `srand` 只需要在程序开始时调用一次,为整个随机数序列播下种子。如果放在循环内部,可能会因为 `time(0)` 在一秒内返回值相同而导致短时间内生成的随机数都一样。
#### **三、 判断题**
1. √
2. × (当 `while` 循环条件初始为假,一次都不执行,`do-while` 无法模拟这种情况)
3. × (范围是 `[0, 99]`)
4. √
5. √
6. ×
7. √
8. √
9. √
10. √
@@ -0,0 +1,89 @@
### C++ do-while 循环 课后作业
姓名: \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
#### **一、 填空题**
1. `do-while` 循环的核心思想是“先 \_\_\_\_\_\_\_\_\_\_\_,后 \_\_\_\_\_\_\_\_\_\_\_”。
2. 与 `while` 循环不同,`do-while` 循环的循环体代码保证至少会被执行 __\_\_\_\_\_\_\___ 次。
3. `do-while` 循环的语法结构在末尾的 `while(条件)` 之后必须有一个 \_\_\_\_\_\_\_。
4. 在猜数字游戏中,因为玩家无论如何都需要先输入一次猜测的数字,所以使用 \_\_\_\_\_\_\_\_\_\_\_ 循环比 `while` 循环更合适。
5. 在 C++ 中,为了生成每次运行程序时都不同的随机数,我们需要使用 `time(0)` 函数来获取当前的\_\_\_\_\_\_\_\_\_\_\_,并通过`srand()`函数设置随机数 \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_,为了使用这两个函数工具,需要包含 `<ctime>` 和 `<cstdlib>` 两个头文件。
#### **二、 单项选择题**
( ) 1. 关于 `do-while` 和 `while` 循环,下列说法正确的是?
A. 两者没有任何区别
B. `while` 循环的循环体至少执行一次
C. `do-while` 循环的循环体至少执行一次
D. `do-while` 循环先判断条件再执行
( ) 2. 观察下面的代码,它会输出多少个 "Hello"?
```cpp
int i = 10;
do {
cout << "Hello" << endl;
i++;
} while (i < 10);
```
A. 0 个 B. 1 个 C. 10 个 D. 无限个
( ) 3. 哪个场景最适合使用 `do-while` 循环?
A. 遍历一个已知长度的数组
B. 计算 1 到 100 的累加和
C. 程序启动后,先显示菜单让用户选择,如果选择无效则重新显示
D. 从 100 倒数到 1
( ) 4. 要生成一个 `[1, 50]` 范围内的随机整数,正确的表达式是?
A. `rand() % 49` B. `rand() % 50`
C. `rand() % 50 + 1` D. `rand() % 51`
( ) 5. 在猜数字游戏中,`do-while` 循环的循环条件通常是?
A. `guess == ans` B. `guess != ans`
C. `guess > ans` D. `guess < ans`
( ) 6. 分析以下代码,最终输出结果是什么?
```cpp
int n = 5;
do {
cout << n << " ";
n--;
} while (n > 5);
```
A. 什么都不输出 B. 5 C. 5 4 3 2 1 D. 导致死循环
( ) 7. 关于 `srand(time(0));` 这行代码,下列描述错误的是?
A. 它的作用是初始化随机数种子 B. `time(0)` 提供一个不断变化的值
C. 它应该被放在 `do-while` 循环的内部,以保证每次循环都足够随机
D. 它能确保程序每次运行时,生成的随机数序列都不同
( ) 8. `do-while` 循环的条件判断发生在哪个时间点?
A. 每次循环体执行之前
B. 每次循环体执行之后
C. 仅在整个循环开始前判断一次
D. 仅在循环体执行到一半时
( ) 9. 以下哪个 `do-while` 的语法是正确的?
A. `do while(x<5) { ... }`
B. `do { ... } while x<5;`
C. `do { ... } while(x<5)`
D. `do { ... } while(x<5);`
( ) 10. 如果有一个程序需要反复验证用户输入的密码,直到输入正确为止,最合适的循环结构是?
A. `if-else` 结构
B. `while` 循环,并在循环外先获取一次输入
C. `do-while` 循环
D. 选项 B 和 C 都可以,但 C 更简洁优雅
#### **三、 判断题 (对的打“√”,错的打“×”)**
( ) 1. `do-while` 循环的循环条件即使初始就为 `false`,循环体也依然会执行一次。
( ) 2. 在任何情况下,一个 `while` 循环都可以被等价地改写为 `do-while` 循环。
( ) 3. `rand() % 100` 生成的随机数范围是 `[0, 100]`,即包含0和100。
( ) 4. 在 `do { ... } while(条件表达式);` 结构中,在执行完循环体后,`条件表达式` 变为 `false` 时循环终止。
( ) 5. 因为 `do-while` 循环体至少执行一次,所以它永远不会导致“一次都不执行”的情况。
( ) 6. `srand(time(0));` 这行代码的作用是生成一个随机数。
( ) 7. `do-while` 循环适合于需要先获取用户输入再进行判断的逻辑。
( ) 8. 如果忘记了 `do-while` 语句末尾的分号,程序将无法通过编译。
( ) 9. 在一个 `do-while` 循环体内,必须有能够影响到循环条件的语句,否则可能导致死循环。
( ) 10. `while(guess != ans)` 和 `while(!(guess == ans))` 这两个循环条件是完全等价的。