📄 一句话概括
调用一个函数="喊名字 + 递实参",然后程序跳进函数体执行,执行完再跳回调用点接着往下走。这一节把"跳进去、跳回来"这件事讲清,并交代本节三篇的分工:传值调用(6.3.1)、嵌套调用(6.3.2)、递归调用(6.3.3)。
📝 本节和前面几节的关系
6.2 讲完了"进出口"(参数与返回值),这一节开始讲"怎么用"。其实前面每个 Demo 都已经在调用函数了(cout、自己写的 printLine),只是没把它当主角。搞懂"跳进去再跳回来"这件事,第 7 章的指针、第 8 章的引用才不悬空。
一、调用语句的三要素
add(3, 5);
- 函数名——喊谁
- 实参表(圆括号里)——递什么
- 分号——说明这是一条语句
有返回值的函数,调用还能当表达式用(回看 3.3:表达式必有值和类型):
int s = add(3, 5) * 2; // 调用的结果直接参与运算
cout << add(3, 5); // 结果直接交给 cout
if (add(3, 5) > 6) { ... } // 结果直接用于判断
这就是"函数是给代码起的名字"的真正含义——名字一旦能用,它就跟一个普通值一样到处都能用。
二、调用那一刻,程序在干什么(五步)
以 int a = square(4); 为例:
| 步骤 | 发生了什么 |
|---|---|
| 1 | 记住位置——把"我执行到哪一行了"存起来(返回地址) |
| 2 | 复制实参——把 4 复制给形参 n |
| 3 | 跳进去——进入 square 的函数体,从上往下执行 |
| 4 | 交结果——遇到 return,把值交出来 |
| 5 | 跳回来——回到刚才记住的那一行,继续往下 |
这套"记住位置、一层层记"的机制就是调用栈(call stack)。它一次性解释了两件事: - 嵌套调用为什么能一层层返回(6.3.2) - 递归为什么必须设出口(6.3.3,没出口就会"栈溢出")
一句话对照:函数是"能进去、还能出来"的代码块——这一点和 5.3.4 的 goto 完全不同(goto 跳过去就回不来,也没法把结果带回来)。
三、本节三篇的分工
| 小节 | 主题 | 一句话 |
|---|---|---|
| 6.3.1 传值调用 | 本章唯一的传参方式 | 实参复制给形参,函数改不动原件 |
| 6.3.2 嵌套调用 | A 调 B、B 调 C | 一层层进去、一层层回来 |
| 6.3.3 递归调用 | 函数自己调自己 | 必须要有出口,否则栈溢出 |
四、动手 Demo:看清"跳过去、回来继续"
#include <iostream>
using namespace std;
int square(int n) {
cout << " 进入 square(" << n << ")" << endl;
int r = n * n;
cout << " 离开 square(" << n << "),交回 " << r << endl;
return r;
}
int main() {
cout << "开始" << endl;
cout << "第一句" << endl;
int a = square(4);
cout << "回到 main,a = " << a << endl;
cout << "最后一句" << endl;
return 0;
}
看到什么算成功:
开始
第一句
进入 square(4)
离开 square(4),交回 16
回到 main,a = 16
最后一句
关键在顺序:square 的那两行夹在中间——这证明程序真的"跳进去、干完、又跳回来",而不是从头到尾顺序读下来。这也是"函数调用会打断顺序流"最直观的证据。
30 秒实验:在 int a = square(4); 之前和之后各加一句 cout << "标记" << endl;,观察它们把 square 的两行夹住。养成用缩进表现层次的习惯——到 6.3.2 嵌套调用时,这个习惯直接决定你能不能一眼看懂输出顺序。
五、常见误区
- "调用就是从文件第一行开始执行"——不是。执行从
main开始(2.1.4),只在被调用时才跳进别的函数。 - "函数名不加括号也是调用"——
add;只是"提到这个名字",什么都不会发生。圆括号才是"调用"的标志。 - "实参和形参同名就是同一个变量"——6.2.2 已说明:两间不同的房子。
- "调用不花代价"——有代价:记返回地址、复制实参、建立栈帧、再拆掉。调用得极频繁的短函数会被这些开销拖慢,这正是 6.6 要讲的内联函数存在的理由。
自测三问(点开对答案)
1. 调用函数时程序做了哪五步?——记位置、复制实参、跳进去执行、交回结果、跳回调用点。 2. 有返回值的调用为什么能参与运算?——因为函数调用也是表达式(有值和类型)。 3. 为什么递归必须设出口?——每次调用都会在调用栈上"记住位置",没出口就一直堆,最后栈溢出。- ☐ 6.3.1 传值调用(下一节)
相关笔记
- 返回值 —— 出口的规矩
- 表达式 —— 调用也是表达式
- goto 语句 —— 跳过去就回不来的对照
- 🔷 C++基础 · 索引 —— 全课路线图