172 lines
5.0 KiB
Markdown
172 lines
5.0 KiB
Markdown
# 第3课:自定义函数(教学拓展)
|
||
|
||
> 等级说明:自定义函数属于 GESP 四级知识。本课程保留这一课,是为了训练代码分解和复用能力,不作为一级、二级考试必考内容。
|
||
|
||
## 教学目标
|
||
- 掌握函数的定义、调用与声明
|
||
- 理解形参与实参、返回值、void函数
|
||
- 理解函数重载(同名不同参)
|
||
- 理解局部变量与全局变量的作用域
|
||
|
||
## 核心考点:函数的参数、返回值与调用过程
|
||
|
||
### 考什么
|
||
常见题型包括补全函数定义、判断形参与实参、根据调用过程求输出,以及把一段重复逻辑封装成函数。重点是看懂数据怎样进入函数、函数怎样处理、结果怎样返回主程序。
|
||
|
||
### 必须理解
|
||
定义函数时括号里的变量叫形参,是函数内部接收数据的“临时名字”;调用函数时传入的值叫实参。普通值传递会把实参复制给形参,函数内部修改形参通常不会改变外面的变量。执行到 `return` 时,函数立即结束并把结果交回调用位置;`void` 函数不返回结果。局部变量只在所属的大括号内有效,不同函数中的同名局部变量互不影响。
|
||
|
||
### 核心写法
|
||
```cpp
|
||
int maxValue(int a, int b) { // a、b 是形参
|
||
if (a > b) return a;
|
||
return b;
|
||
}
|
||
|
||
int answer = maxValue(x, y); // x、y 是实参
|
||
```
|
||
调用时先计算实参,再进入函数执行;遇到 `return` 后回到原调用位置,把返回值赋给 `answer`。
|
||
|
||
### 常见错误
|
||
- 声明的返回类型是 `int`,却忘记写 `return`。
|
||
- 把函数定义写进 `main` 内部;C++ 不允许在函数内部再定义普通函数。
|
||
- 以为修改普通形参就会修改实参,混淆“值的复制”和“原变量”。
|
||
- 在变量作用域之外使用局部变量。
|
||
|
||
### 判断是否掌握
|
||
能画出“实参传入→形参接收→函数执行→return 返回→主程序继续”的过程,并能独立编写一个有参数、有返回值的判断或计算函数。
|
||
|
||
---
|
||
|
||
## 题目一:判断素数
|
||
|
||
### 核心代码(一句话)
|
||
> 判断素数的核心:**从 2 试到 √n,只要有一个能整除,就不是素数。**
|
||
|
||
### 自然语言思路
|
||
素数就是"除了1和自己,不能被任何数整除"的数。
|
||
|
||
比如判断 17 是不是素数:
|
||
- 试 2:17 ÷ 2 = 8 余 1,不行
|
||
- 试 3:17 ÷ 3 = 5 余 2,不行
|
||
- 试 4:17 ÷ 4 = 4 余 1,不行
|
||
- 试到 √17 ≈ 4.1,所以只需试到 4
|
||
- 全都不行 → 17 是素数!
|
||
|
||
为什么只试到 **√n**?因为如果 n 有一个大于 √n 的因数,那一定会有一个小于 √n 的因数跟它配对。比如 36 = 6 × 6,一个因数大于 6,另一个必然小于 6。所以试到 √n 就够了。
|
||
|
||
**为什么要写成函数?** 判断素数的代码以后经常用到,封装成 `isPrime(n)`,以后需要时一行调用,不用重复写。
|
||
|
||
### 伪代码
|
||
```
|
||
函数 isPrime(n):
|
||
如果 n < 2: 返回 false // 1 不是素数
|
||
对于 i 从 2 到 sqrt(n):
|
||
如果 n % i == 0: // 能被整除
|
||
返回 false // 不是素数,直接返回
|
||
返回 true // 全都试过了,是素数
|
||
|
||
主程序:
|
||
输入 m
|
||
如果 isPrime(m): 输出 "YES"
|
||
否则: 输出 "NO"
|
||
```
|
||
|
||
### 真实代码
|
||
```cpp
|
||
#include <iostream>
|
||
#include <cmath>
|
||
using namespace std;
|
||
|
||
// 函数定义:判断素数
|
||
bool isPrime(int n) {
|
||
if (n < 2) return false; // 1 和负数不是素数
|
||
for (int i = 2; i <= sqrt(n); i++) {
|
||
if (n % i == 0) return false; // 找到因数,不是素数
|
||
}
|
||
return true; // 全部试完,是素数
|
||
}
|
||
|
||
int main() {
|
||
int m;
|
||
cin >> m;
|
||
if (isPrime(m)) {
|
||
cout << "YES" << endl;
|
||
} else {
|
||
cout << "NO" << endl;
|
||
}
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
### 注意点
|
||
1. **1 不是素数**:必须在函数开头处理 `n < 2` 的情况,这是 GESP 高频易错点。
|
||
2. **为什么 `i <= sqrt(n)` 而不是 `i < n`**:前者效率高很多。判断 100 是否是素数,试到 10 就够了,不用试到 99。
|
||
3. **`sqrt` 返回 double**:`i <= sqrt(n)` 是 int 和 double 比较,C++ 会自动转换,没问题。但如果担心精度,可以写成 `i * i <= n`。
|
||
4. **函数名要有意义**:`isPrime` 一看就知道是"判断素数",比 `f`、`check` 好得多。
|
||
5. **局部变量 i**:i 只在 isPrime 函数里有效,main 里访问不到它 —— 这就是**局部变量**的作用域。
|
||
|
||
---
|
||
|
||
## 题目二:(待橙子补充)
|
||
|
||
> 建议覆盖:函数重载、void函数等
|
||
|
||
---
|
||
|
||
### 核心代码(一句话)
|
||
>
|
||
|
||
### 自然语言思路
|
||
|
||
|
||
### 伪代码
|
||
|
||
|
||
### 真实代码
|
||
```cpp
|
||
|
||
```
|
||
|
||
### 注意点
|
||
|
||
|
||
---
|
||
|
||
## 题目三:(待橙子补充)
|
||
|
||
> 建议覆盖:局部变量与全局变量对比
|
||
|
||
---
|
||
|
||
### 核心代码(一句话)
|
||
>
|
||
|
||
### 自然语言思路
|
||
|
||
|
||
### 伪代码
|
||
|
||
|
||
### 真实代码
|
||
```cpp
|
||
|
||
```
|
||
|
||
### 注意点
|
||
|
||
|
||
---
|
||
|
||
## 本课打油诗
|
||
|
||
> 函数写好存身旁,起名调用响当当。
|
||
> 参数传进返结果,少写重复心不慌。
|
||
|
||
---
|
||
|
||
## 课后作业
|
||
(待补充)
|
||
1.
|
||
2.
|