📄 一句话概括
第六章给了你一把拆刀——把一段活儿打包成函数。五块拼图:定义与声明(怎么写出来)、参数与返回值(怎么进怎么出)、调用(传值 / 嵌套 / 递归)、地盘与寿命(作用域与存储类别)、一名多玩法(重载与内联)。至此,"写一个完整小程序"的全部基本零件都到齐了。
📝 本节和前面几节的关系
第一章学搭环境、第二章学数据、第三章学运算、第四章学判断、第五章学循环——都是在一个 main 里写完整逻辑。第六章第一次让你把逻辑装进零件盒:函数。下一章(数组)开始,你会处理"一串数据",那时函数的价值会更明显——因为数组相关的操作几乎都要靠函数来传递和返回。
一、本章知识地图
| 块 | 小节 | 一句话要点 |
|---|---|---|
| 定义与声明 | 6.1 / 6.1.1 / 6.1.2 | 四部件:返回类型 · 函数名 · 参数表 · 函数体;分号 = 声明,花括号 = 定义;声明在调用之前 |
| 参数 | 6.2.1 / 6.2.2 / 6.2.3 / 6.2.4 | 空函数(void)· 形参是实参的复印件 · 默认参数(从右往左连续)· 可变参数(...,必须自己报个数) |
| 返回值 | 6.2.5 | return 两件事:交回值 + 立即结束;类型匹配 · 只能一个 · 每条路径都要有 |
| 调用 | 6.3 / 6.3.1 / 6.3.2 / 6.3.3 | 跳进去跳回来 · 传值=复印件 · 嵌套=先进后出 · 递归=出口 + 向出口靠近 |
| 地盘与寿命 | 6.4 / 6.7 ~ 6.7.4 | 作用域三层(全局/局部/块)+ 就近遮蔽;存储类别四兄弟:auto / static / register / extern |
| 一名多玩法 | 6.5 / 6.6 | 重载:靠参数表区分(返回值不算);内联:就地展开,用空间换时间(决定权在编译器) |
二、必须记住的六个结论
- 函数 = 给一段代码起的名字。四个部件:返回类型 · 函数名 · 参数表 · 函数体。
- 分号 = 声明,花括号 = 定义。声明可以写多次,定义只能有一次。
- 入口可以多,出口最多一个。不交结果用
void;return交回值的同时立即结束函数。 - 实参是复制给形参的(传值调用)——函数改的是复印件。想改原件,要等第 7 章的指针、第 8 章的引用。
- 作用域 ≠ 生存期。函数里的
static变量:外面看不见名字,命却活到程序结束。 - 重载靠参数表区分(返回值不算);内联只是建议(用空间换时间,决定权在编译器)。
三、动手 Demo:两关自测
第一关(写程序):成绩处理小工具
用本章学的函数,把下面四件事各写成一个函数,再在 main 里串起来:
#include <iostream>
using namespace std;
int readScore() { // ① 输入一个成绩
int s;
cout << "请输入成绩(0-100):";
cin >> s;
return s;
}
char getGrade(int score) { // ② 按分数定等级
if (score >= 90) return 'A';
else if (score >= 80) return 'B';
else if (score >= 60) return 'C';
else return 'D';
}
void printResult(int score, char grade) { // ③ 打印一行(空函数)
cout << " 成绩 " << score << " 分,等级 " << grade << endl;
}
double average(int sum, int count) { // ④ 求平均
return (double)sum / count; // 别忘了强制转换(3.3.2)
}
int main() {
int n;
cout << "要输入几个成绩?";
cin >> n;
int sum = 0;
for (int i = 1; i <= n; i++) {
int s = readScore();
printResult(s, getGrade(s)); // 嵌套调用(6.3.2)
sum += s;
}
cout << "平均分:" << average(sum, n) << endl;
return 0;
}
看到什么算成功:每输入一个成绩打印一行等级,最后打印带小数的平均分。
检查清单:① 四个函数各自的返回值类型对不对;② average 里有没有做 double 转换(不转会得到整数);③ printResult 有没有写成 void。
第二关(找错):四处问题
下面这段想"找出三个数里的最大值并打印",有四处问题:
#include <iostream>
using namespace std;
void showMax(int m) {
cout << "最大值是 " << m << endl;
}
int main() {
int x = 10, y = 25, z = 18;
int m = getMax(x, y, z); // 问题①
showMax(m);
cout << "m 是 " << n << endl; // 问题②
return 0;
}
int getMax(int a, int b, int c) {
int m = a;
if (b > m) m = b;
if (c > m); m = c; // 问题③
// 问题④
}
答案与理由:
| # | 问题 | 原因与修法 |
|---|---|---|
| ① | getMax 没声明就在 main 里用 |
报 not declared;在 main 前加 int getMax(int a, int b, int c);(6.1.2) |
| ② | 变量 n 不存在 |
名字写错了,应为 m(2.3.1 标识符) |
| ③ | if (c > m); m = c; 多了一个分号 |
分号成了空语句,m = c; 变成无条件执行(4.2 的老坑) |
| ④ | 函数体末尾漏了 return m; |
交回垃圾值(6.2.5);补 return m; |
修好的版本:
int getMax(int a, int b, int c); // ② 提前声明
int main() {
int x = 10, y = 25, z = 18;
int m = getMax(x, y, z);
showMax(m);
cout << "m 是 " << m << endl;
return 0;
}
int getMax(int a, int b, int c) {
int m = a;
if (b > m) m = b;
if (c > m) m = c; // ③
return m; // ④
}
30 秒实验:把修好的程序跑一遍,应输出 最大值是 25 和 m 是 25。然后把 return m; 再注释掉,看输出变成什么(垃圾值)。
四、常见误区(本章汇总)
- "编译通过 = 逻辑对"——本章又是一堆"不报错陷阱":漏
return、return a, b;只返回 b、内联不生效、extern写成了定义。 - "函数里有 return 就万事大吉"——还得看是不是每条路径都有(
if里 return 了、else没 return 就是坑)。 - "形参改了外面就变了"——本章永远不成立。复印件思维要刻进骨头。
- "static 变量和普通局部变量差不多"——差在生存期:它记着上次的值,也一直占着内存。
- "一个函数越长越强"——一个函数只干一件事。函数名就是这段代码的目录标题。
自测六问(点开对答案)
1. 函数定义的四个部件?——返回类型、函数名、参数表、函数体。 2. 声明和定义差什么?——声明是函数头 + 分号;定义是函数头 + 函数体(花括号)。 3. 函数的入口和出口各有几个上限?——入口可以有多个,出口最多一个。 4. 传值调用为什么改不动外面的变量?——传过去的是值的副本。 5. 递归的两个必备条件?——出口 + 每次调用都向出口靠近。 6. 作用域和生存期的区别?——作用域管名字能看见的范围;生存期管变量从生到死多久。- ☑ 第七章 数组、指针和引用 —— 已完结:小结(数组=一排柜子、指针=门牌号、引用=别名)