📄 一句话概括
传递地址=调用时把 &变量 递进去,形参用指针接住,函数里用 *p 改原件。这是 6.3.1 那个"怎么都换不动"的 swap 唯一正确的打开方式:void swap(int *a, int *b) 配 swap(&x, &y)。两处缺一不可——形参要写成指针,调用要加 &。
📝 本节和前面几节的关系
这是 6.3.1 伏笔的正式回收点。当时你被"改的是复印件"憋了一句"想改原件,要等第 7 章的指针"——现在指针到账了。机制来自 7.4.2(*p 是左值,指的就是原件那块内存);写法来自 7.4.1(指针形参的声明)。本节之后,7.8.2 会给出第三种、也是最漂亮的写法:引用。
一、把 swap 写对
老写法(6.3.1,换不动)
void swapWrong(int a, int b) { // 形参是 int(值)
int t = a; a = b; b = t; // 交换的是两份副本
}
swapWrong(x, y); // 传的是值
新写法(本节,换得动)
void swapRight(int *a, int *b) { // ① 形参是 int*(指针)
int t = *a; *a = *b; *b = t; // ② 体内用 *a 操作"原件"
}
swapRight(&x, &y); // ③ 调用时用 & 取地址递进去
三处必须同时对上:
| 位置 | 传值版 | 传地址版 |
|---|---|---|
| 形参声明 | int a |
int *a |
| 函数体内的操作 | 直接用 a |
必须用 *a |
| 调用处 | swap(x, y) |
swap(&x, &y) |
任何一处漏改,结果就回到"换不动"。最常见的两个错误:调用时忘了 &(编译不过或警告),函数体里忘了 *(换了个寂寞)。
二、实例12:调用自定义函数交换两个变量值
#include <iostream>
using namespace std;
void swapWrong(int a, int b) { // 传值:换的是副本
int t = a; a = b; b = t;
}
void swapRight(int *a, int *b) { // 传地址:换的是原件
int t = *a; *a = *b; *b = t;
}
int main() {
int x = 1, y = 2;
cout << "交换前: x = " << x << ", y = " << y << endl; // 1, 2
swapWrong(x, y);
cout << "传值后: x = " << x << ", y = " << y << endl; // 1, 2(没变)
swapRight(&x, &y);
cout << "传地址后: x = " << x << ", y = " << y << endl; // 2, 1(变了)
// 用一个真实的数组验证:交换两个元素
int arr[4] = {10, 20, 30, 40};
swapRight(&arr[0], &arr[3]);
cout << "数组首尾交换: ";
for (int i = 0; i < 4; i++) cout << arr[i] << " "; // 40 20 30 10
cout << endl;
return 0;
}
看到什么算成功:
交换前: x = 1, y = 2
传值后: x = 1, y = 2 ← 毫无变化
传地址后: x = 2, y = 1 ← 成功交换
数组首尾交换: 40 20 30 10
最后那一段最实用:swapRight(&arr[0], &arr[3]) 把数组首尾两个元素换了——这就是"排序"的基础动作。后面学排序算法(冒泡、快速排序)时,swap 是最小的工作单元。6.3.1 里写不对的那几行,现在成为你能用的工具了。
30 秒实验:把 swapRight(&x, &y); 里的两个 & 去掉(写成 swapRight(x, y);),重新编译——编译器直接报错(invalid conversion from 'int' to 'int*')。这正是 7.4.1 说的"指针类型必须匹配"在替你把关:想在编译期就发现问题,靠的就是类型。
三、什么时候该传地址
| 情况 | 建议 | 原因 |
|---|---|---|
| 要改调用方的变量 | 传地址 | 只有拿着地址才能改原件 |
| 只是读参数、不改 | 传值 | 天然隔离,函数爱怎么折腾都不影响外面 |
| 参数是大数组/大对象 | 传地址(或 const 引用) |
省掉整份拷贝,也避免"改不动"的困惑 |
| 参数是字符数组 | 传地址(数组名本身就是地址) | 数组名就是首地址(7.5),压根没法"传值" |
一句话判断法:问自己"函数结束后,我希不希望调用方的变量变了?" 希望 → 传地址;不希望 → 传值。
四、常见误区
- "形参写成指针,函数里就能直接改
a"——不行。指针形参在函数里也是个局部变量,直接改a只是改指针自己的指向(7.4.1),必须写*a才改到原件。 - "调用时加不加
&都行"——必须加。少了&递进去的是值,类型不匹配,编译直接报错。 - "传地址就等于把整个变量搬进函数"——不是。搬进去的只是一个地址(4 或 8 字节),原变量还留在原地。
- "传地址比传值危险,能不用就不用"——危险的不是传地址,而是指针不判空、乱指(7.4.4)。该用的时候必须用,否则
swap这类基本工具都写不出来。 - "数组传进函数时也是复制一份"——不是。数组名本身就是地址(7.5),传进函数后操作的就是原数组——这也是 7.8.3 要展开的点。
自测三问(点开对答案)
1. 写一个能真正交换两个 int 的函数,形参怎么写?——`void swap(int *a, int *b)`,调用 `swap(&x, &y);` 2. `swapRight(x, y)`(忘了 `&`)会怎样?——编译报错,类型不匹配(`int` 换不成 `int*`)。 3. 函数里写 `a = b;`(a 是指针形参)改的是什么?——改的是**指针自己的指向**,没碰到原件(必须写 `*a = *b;`)。- ☐ 7.6.2 指向函数的指针(下一节)
相关笔记
- 传值调用 —— 伏笔的起点,本节正式回收
- 指针在函数中的应用 —— 三种玩法总览
- 使用引用传递参数 —— 第三种写法:引用版 swap
- 形参与实参 —— "复印件"的原始解释
- 🔷 C++基础 · 索引 —— 全课路线图