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,283 @@
### **C++ 格式化输入与输出 讲义**
#### **一、 引言**
##### **问题引入:数字反转**
【书本P21页 例2-7】我们来看一个问题:
> 输入一个三位浮点数,例如 `123.4`,要求把这个数字彻底翻转过来,变成 `4.321` 并输出。
乍一看,这个问题似乎很难下手。我们熟悉的 `cin` 只能整个地读取这个数字,`cout` 也只能整个地输出。我们如何能把——`1`、`2`、`3`、`.`、`4`——拆开,再按新的顺序重新组合呢?
---
#### **二、 解法一:数学运算**
在揭晓新工具之前,我们先用已有的知识尝试解决。
##### **思路铺垫**
对于 `123.4`,最碍事的就是那个小数点。如果我们能先把它变成整数 `1234`,问题就简化成了“整数拆分”。
##### **核心工具:提取器与削减器**
我们回顾一下拆分整数的两个工具:
1. **个位的提取器 (`% 10`)**
* **作用**:获取一个整数的**最右边一位**。
* **原理**:任何整数除以10的**余数**,正好就是它的个位数。
* **示例**:`1234 % 10` 结果是 `4`。
2. **数字的削减器 (`/ 10`)**
* **作用**:**砍掉**一个整数的**最右边一位**。
* **原理**:在C++中,`整数 / 整数` 的结果会**直接舍弃小数部分**。
* **示例**:`1234 / 10` 结果是 `123`。
##### **步骤拆解**
1. **第一步(提取)**:使用 **提取器 (`% 10`)** 获得当前数字的个位数。
2. **第二步(削减)**:使用 **削减器 (`/ 10`)** 砍掉个位数,得到一个更小的数,为下一次操作做准备。
---
**具体案例**
让我们来手动拆解整数 `479`。
**分步演示**:
* **初始状态**: 我们有一个整数 `n = 479`。
* **第一轮操作 (处理 `479`)**:
1. **提取**: `479 % 10` -> 我们得到了数字 **`9`**。
2. **削减**: `479 / 10` -> `n` 现在变成了 **`47`**。
* *我们成功地把 `9` 从 `479` 中分离了出来,并且得到了一个更简单的问题:如何拆解 `47`。*
* **第二轮操作 (处理 `47`)**:
1. **提取**: `47 % 10` -> 我们得到了数字 **`7`**。
2. **削减**: `47 / 10` -> `n` 现在变成了 **`4`**。
* *小结:我们又分离出了 `7`,问题变得更简单了:如何拆解 `4`。*
* **第三轮操作 (处理 `4`)**:
1. **提取**: `4 % 10` -> 我们得到了数字 **`4`**。
2. **削减**: `4 / 10` -> `n` 现在变成了 **`0`**。
* *小结:我们分离出了最后的 `4`,`n` 变成了 `0`,说明拆解工作已全部完成!*
而对于我们的题目:**输入一个三位浮点数,例如 `123.4`,要求把这个数字彻底翻转过来,变成 `4.321` 并输出。**
我们就也能通过整数拆分的办法来解决了。
1. **预处理**:`double num = 123.4; int n = num * 10;` -> 得到整数 `1234`。
2. **逐位拆分**:
* `int d = n % 10;` // d = 4
* `int c = (n / 10) % 10;` // c = 3
* `int b = (n / 100) % 10;` // b = 2
* `int a = n / 1000;` // a = 1
3. **重新组合输出**:
* `cout << d << "." << c << b << a;`
**结论**:通过整数拆分的方法可以解决本问题,且一定要掌握通过数学运算拆分整数的办法。
---
#### **常见问题**
1. **问:对一个个位数(比如 `8`)使用这两个工具会怎样?**
* **答**:完全符合规则,不会出问题!
* **提取器**:`8 % 10` 的结果是 `8`。(8除以10,商0余8,成功提取出了最后一位数字 8)
* **削减器**:`8 / 10` 的结果是 `0`。(8除以10,整数商是0,成功削减了最后一位数字 8)
2. **问:如果数字里有 `0` 呢?比如 `205`?**
* **答**:规则同样适用!
* **第一轮**: `205 % 10` -> `5`;`205 / 10` -> `20`。
* **第二轮**: `20 % 10` -> `0`;`20 / 10` -> `2`。
* **第三轮**: `2 % 10` -> `2`;`2 / 10` -> `0`。
---
#### **要点回顾**
1. **个位的提取器 (`% 10`)**:它的作用就是帮你**看到并取走**一个整数最右边的那一位。
2. **数字的削减器 (`/ 10`)**:它的作用就是**砍掉**一个整数最右边的那一位,让数字变小。
3. 而且“提取”后必须“削减”,这样才能为处理下一位数字做准备。它们总是成对出现。
---
#### **三、 `printf` **
##### **核心概念与语法**
`printf` 的全称是 **Print Formatted(格式化打印)**。
它的作用是:**先写好一段带有“空位”的模板句子,然后再把变量按顺序填入这些空位中。**
**语法格式:**
> `printf("包含格式说明符(占位符)的字符串", 变量1, 变量2, ...);`
##### **`printf` 的使用方法**
1. **打印纯文本**
```cpp
printf("Hello, World!\n"); // \n 代表换行
```
2. **打印单个变量**
`printf` 在于用**格式说明符**(以`%`开头的占位符)去指定“空位”的类型,也就是先挖个坑占个座,之后我来告诉你什么数据会放在这里你进行输出就可以了。
```cpp
int age = 30;
// %d 是专门用来放整数的占位符
printf("我的年龄是 %d 岁。\n", age);
```
**逻辑解释:**
1. `"我的年龄是%d岁。\n"` 是输出的模板字符串。
2. `%d` 是一个占位符。
3. `printf` 会在输出时,自动按照顺序用后面 `age` 变量的值(30)去替换 `%d` 的位置。
3. **混合打印多个变量**
`printf` 也可以极其方便地在一句话中同时输出多个、多种类型的变量,而不需要像 `cin` 或 `cout` 那样反复使用 `<<` 拼接。
```cpp
int age = 15;
double height = 168.5;
char rank = 'S';
// 依次使用 %d, %lf, %c 作为占位符
printf("我今年%d岁,身高是%lf厘米,综合评级:%c\n", age, height, rank);
```
**核心规则**:
* **一一对应:** 字符串里有 3 个坑(`%d`, `%lf`, `%c`),后面就必须跟着 3 个变量(`age`, `height`, `rank`)。
* **按顺序入座:** `printf` 会严格按照从左到右的顺序,把第一个变量填入第一个占位符,第二个变量填入第二个占位符,以此类推。
* **类型必须匹配:** 第一个是整数类型 `age`,所以必须用 `%d`;第二个是小数类型 `height`,必须用 `%lf/%f`;第三个是字符类型 `rank`,必须用 `%c`。
##### **常用格式说明符(书本P24页,重要!)**
占位符的顺序必须与后面变量的顺序一一对应,且数据类型必须兼容!
| 占位符 | 对应数据类型 | 说明 |
| ---------- | -------------- | ------------------------------------------------------------ |
| **`%d`** | `int` | 打印整数 |
| **`%f`** | `float`、`double` | 打印浮点数 (默认保留6位小数) |
| **`%lf`** | `float`、`double` | 输出时与 `%f` 相同;小数位数由精度指定 |
| **`%c`** | `char` | 打印单个字符 |
| **`%.mf`** | `float/double` | **技巧:** 打印浮点数,并**精确保留m位小数**。例如 `%.2f` 就是保留两位。 |
### `cout` 与 `printf` 写法对比
| 操作目标 | `cout` 写法 | `printf` 写法 |
| ---------------- | ------------------------------- | ------------------------------- |
| 打印纯文本 | `cout << "输出的内容";` | `printf("输出的内容");` |
| 打印单个变量 | `cout << num1;` | `printf("%d", num1);` |
| 混合打印多个变量 | `cout << num1 << "和" << num2;` | `printf("%d和%d", num1, num2);` |
---
#### **四、 `scanf` **
##### **概念与语法**
`scanf` 的全称是 **Scan Formatted(格式化扫描/输入)**。
它的逻辑和 `printf` 基本一致,只不过从输出数据 变成了 输入数据:它会根据你提供的格式字符串模板,去键盘的输入中**抓取**对应的数据。
**语法格式:**
> `scanf("格式控制字符串", &变量1, &变量2, ...);`
##### **要注意 `scanf` 必须加 `&`(取地址符)**
**原理:**
* `cin` 很智能,你告诉它抽屉的名字(如 `age`),它就能自己找到。
* `scanf` 是一位老员工,你必须把这个抽屉在内存中的**具体物理地址**交给它,它才能把数据放进去。
那么在变量名前加上 `&`,就相当于提取了这个变量的地址,所以这个符号也被称为取地址符!
> **警告:** 如果忘记写 `&`,程序在运行到输入阶段时会直接崩溃!
**基础使用示例:**
```cpp
int age;
printf("请输入您的年龄:");
scanf("%d", &age); // 从键盘输入获取一个整数,存入 age 所在的内存地址
// 现在 age 中就储存了输入的数据
```
##### **`scanf` 的使用技巧**
1. **一次性读取多个数据**
```cpp
int a;
double b;
scanf("%d%lf", &a, &b); // 从键盘输入中读取一个整数和一个double类型的数据
```
*用户输入时用空格、Tab或回车 (空白字符) 隔开即可,`scanf` 会自动跳过它们。*
2. **易错点:不要在格式字符串里乱加标点,除非题目格式明确要求了**
* **错误写法**:`scanf("%d,%d", &a, &b);`
* **后果**:那么用户输入时**必须严格**按照格式输入:**[一个整数 逗号 一个整数]** 的格式,例如`5, 10` 。如果不带逗号输入`[5 10]`的话就会读取失败。
* **正确、安全的写法**:`scanf("%d%d", &a, &b);` 保持格式字符串的干净,只保留占位符。
---
#### **五、 实战演练**
##### **实战1:数字反转**
**解题思路**:我们不把 `123.4` 看作数字,而是看作“3个字符 + 1个小数点 + 1个字符”。利用 `scanf` 的格式匹配能力,将它们分离到不同的`char`变量中!
```cpp
#include <cstdio>
int main() {
char a, b, c, d;
// 输入格式为 %c%c%c.%c,匹配 [字符 字符 字符 小数点 字符] 的格式将4个字符分离出来
scanf("%c%c%c.%c", &a, &b, &c, &d);
// 按照 d.cba 的倒序格式输出
printf("%c.%c%c%c\n", d, c, b, a);
return 0;
}
```
*用 `scanf` 和 `printf`问题就会变得非常简单!*
##### **实战2:再分肥宅水(书本P23页)**
**题目要求**:$t$ 毫升水均分给 $n$ 名同学。输出每人获得多少毫升(严格保留3位小数)以及一共需要多少个杯子(每人2个)。
```cpp
#include <cstdio>
int main() {
double t;
int n;
// 用 %lf 读 double, 用 %d 读 int
scanf("%lf%d", &t, &n);
// t/n 对应 %.3f (保留三位小数), n*2 对应 %d,中间用 \n 换行
printf("%.3f\n%d\n", t / n, n * 2);
return 0;
}
```
---
#### **六、 总结**
* `cin/cout`:语法简单直观,能自动识别类型,是我们日常编程的常用选择。
* `scanf/printf`:
* **优点**:执行效率更高;拥有强大的**格式控制能力**,能实现精准的输入匹配和带精度的输出。
* **缺点**:语法稍显复杂,需要手动匹配占位符和变量类型,并且 `scanf` 必须带 `&`。
* **如何选择?** 在需要精确控制输入输出格式(如本讲的例题),就非常推荐使用 `scanf/printf` 。
@@ -0,0 +1,38 @@
### 参考答案
#### **一、 填空题**
1. `/ 10`
2. `45`
3. `cstdio`
4. `&`
5. `double`
6. `User ID: 99`
7. `%.1f`
8. `7`
9. `1`
10. `2`
#### **二、 单项选择题**
1. B
2. C
3. C
4. B (整数除法,结果是2)
5. B
6. C
7. B (`365/10` -> `36`, `36%10` -> `6`)
8. B
9. B
10. B (`8/3` 商2余2)
#### **三、 判断题**
1. × (反了,`/` 是除法,`%` 是取余)
2. √
3. × (不安全,用户必须严格输入 `10,20` 才能被读取,不通用)
4. √
5. × (必须包含 `<cstdio>`)
6. √
7. × (可以同时输出多个,如 `printf("%d %d", a, b);`)
8. √
@@ -0,0 +1,74 @@
## C++ 整数提取与格式化输入输出 课后习题
姓名: \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
#### **一、 填空题**
1. C++ 中,获取一个整数个位数的“提取器”是 `% 10`,砍掉个位数的“削减器”是 \_\_\_\_\_\__\_\_\_\_。
2. 表达式 `456 % 10` 的计算结果是 `6`,表达式 `456 / 10` 的计算结果是 \_\_\_\_\_\_\_\_\_\_\_。
3. 要使用 `printf` 和 `scanf`,必须在代码顶部包含头文件 `#include <________________>`。
4. 在使用 `scanf` 读取数据时,必须在变量名前面加上符号 \_\_\_\_\_\_\_\_,它叫做取地址符。
5. 格式说明符 `%d` 用来对应 `int` 类型的整数,`%c` 用来对应 `char` 类型的字符,而 `%lf` 用来对应 \_\_\_\_\_\_\_\_ 类型的变量。
6. 阅读代码 `printf("User ID: %d", 99);`,它的输出结果是:\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_。
7. 如果想用 `printf` 输出一个浮点数,并严格保留 `1` 位小数,应该使用的格式说明符是 \_\_\_\_\_\_\_\_\_\_\_。
8. `int n = 87; printf("%d", n % 10);` 这段代码的输出结果是 \_\_\_\_\_\_\_\_\_\_\_。
9. `int n; scanf("%d", &n);` 如果用户输入 `123`,那么表达式 `n / 100` 的结果是 \_\_\_\_\_\_\_\_\_\_\_。
10. 表达式 `(528 / 10) % 10`它的计算结果是 \_\_\_\_\_\_\_\_\_\_\_。
#### **二、 单项选择题**
( ) 1. 为了获取整数 `921` 的个位数 `1`,应该使用哪个表达式?
A. `921 / 10` B. `921 % 10` C. `921 / 100` D. `921 % 100`
( ) 2. 下列哪条语句可以**正确**地用 `printf` 输出整数变量 `score` 的值?
A. `printf(score);` B. `printf("score");`
C. `printf("%d", score);` D. `printf("%d", &score);`
( ) 3. 下列哪条语句可以**正确**地用 `scanf` 读取一个整数并存入变量 `age` 中?
A. `scanf("%d", age);` B. `scanf("%c", &age);`
C. `scanf("%d", &age);` D. `scanf("%d", &age)`
( ) 4. `int a = 5, b = 2;`,`printf("结果是%d", a / b);` 的输出是?
A. `结果是2.5` B. `结果是2` C. `结果是%d` D. `结果是3`
( ) 5. `int n = 73; n = n / 10;` 执行后,`n` 的值变成了?
A. `73` B. `7` C. `3` D. `7.3`
( ) 6. 忘记在 `scanf` 的变量前加 `&` 符号,最可能导致什么后果?
A. 程序正常运行 B. 程序会卡住
C. 程序在运行时会崩溃或读入错误数据 D. 电脑会蓝屏
( ) 7. `int num; scanf("%d", &num);` 如果用户输入 `365`,那么 `printf("%d", (num/10)%10);` 会输出什么?
A. `3` B. `6` C. `5` D. `36`
( ) 8. 要输出一个浮点数并保留两位小数,应该用哪个格式说明符?
A. `%f` B. `%.2f` C. `%2f` D. `%d.2`
( ) 9. 拆分一个正整数 `n` 的所有位的通用步骤是?
A. 不断地:先用 `/ 10` 削减,再用 `% 10` 提取
B. 不断地:先用 `% 10` 提取,再用 `/ 10` 削减
C. 只需不断地 `/ 10`
D. 只需不断地 `% 10`
( ) 10. `int x=8, y=3; printf("%d %d", x/y, x%y);` 的输出结果是?
A. `2.66 2` B. `2 2` C. `3 2` D. `2 3`
#### **三、 判断题 (对的打“√”,错的打“×”)**
( ) 1. 运算符 `/` 是用来求余数的,运算符 `%` 是用来做除法的。
( ) 2. `printf` 中的占位符(如 `%d`)和后面的变量,它们的顺序和类型都必须一一对应。
( ) 3. `scanf("%d,%d", &a, &b);` 是一个安全的写法,用户输入 `10 20` 就能正确读取。
( ) 4. 对于任何正整数 `n`,`n / 10` 都能去掉它的个位数。
( ) 5. `printf` 和 `scanf` 都是 C++ 自带的核心功能,不需要包含任何头文件。
( ) 6. 整数 `5 % 10` 的结果是 `5`。
( ) 7. `printf` 只能输出一个变量,不能同时输出多个。
( ) 8. 拆分整数 `520` 时,`520 % 10` 的结果是 `0`。