🔷 第六章 函数
📄 一句话概括

传值调用 = 把实参的"值"复制一份给形参,函数只在复印件上折腾。它是 C++ 默认的调用方式,也是本章唯一的方式。最经典的一课:写个 swap 想交换两个数——换不动,因为换的是复印件。

📝 本节和前面几节的关系

6.3 讲了"跳进函数、跳回来",这一节回答"跳进去时带了什么"——答案:只带值,不带地址。这正是 6.2.2"形参是实参的复印件"那条结论正式上岗的地方。等你学到第 7 章指针、第 8 章引用,"传地址"才会出现——那时你才真的拥有"改原件"的能力。

一、什么是传值调用

int add(int a, int b);        // 形参
add(x, y);                    // 实参 → 只把"值"复制进去

一句话特点:函数拿到的只是副本,改副本不影响原件。

传值调用(本章) 传地址调用(第 7、8 章)
传过去的是什么 值的副本 变量的地址
函数能改原件吗 不能 能
安全性 高(外面改不动,天然隔离) 低(外部数据会被改)
代价 复制一份(数据大时费时) 传地址本身很小,但风险高

二、经典翻车现场:写不动的交换函数

void swap(int x, int y) {        // 看起来没问题
    int t = x;
    x = y;
    y = t;                       // 在函数内部看,x 和 y 确实换过来了
}
int a = 3, b = 5;
swap(a, b);
cout << a << " " << b << endl;   // 输出仍是 3 5 !

为什么? swap(a, b) 只把 3 和 5 这两个值复制给了 x、y。x、y 换得再漂亮,跟 a、b 也是两码事。

一句话比喻:就像把文件复印一份给对方改——你手里的原件,一个字母都不会变。

三、实例 04:使用传值调用

#include <iostream>
using namespace std;

void swap(int x, int y) {                    // 传值:收到的只是复印件
    cout << "  换之前:x = " << x << ", y = " << y << endl;
    int t = x;  x = y;  y = t;
    cout << "  换之后:x = " << x << ", y = " << y << endl;
}

int main() {
    int a = 3, b = 5;
    cout << "调用前:a = " << a << ", b = " << b << endl;
    swap(a, b);
    cout << "调用后:a = " << a << ", b = " << b << endl;
    return 0;
}

看到什么算成功:

调用前:a = 3, b = 5
  换之前:x = 3, y = 5
  换之后:x = 5, y = 3
调用后:a = 3, b = 5

三行输出把"传值"钉死了:中间两行证明函数里真的换了,最后一行证明外面真的一点没变。

顺带看一个正面例子——传值其实也是保护:函数里那些坐标、计数一旦被误改,调用方的原始数据毫发无伤。安全,是传值调用送的礼物。

30 秒实验:把 swap(a, b) 改成 swap(3, 5)(直接传两个常量),照样编过运行。这说明"能不能改原件"跟函数体写法无关,只跟传进去的东西里有没有"地址"有关。试完改回来。

四、常见误区

  1. "函数里改了参数,外面就改了"——本章永远不成立。想改外部,必须传地址(第 7、8 章)。
  2. "那本章的函数据就没法改外部数据了?"——还有两条侧门:① 改全局变量(6.4 会讲);② 改传进来的容器内容(第 7 章讲数组时会见到——数组名会"退化成地址",那是下一章的惊喜)。
  3. "传值一定浪费,能不用就不用"——小数据(int/double/char)传值又快又安全,本来就是首选。只有数据很大(大数组、长字符串)时,复制的代价才值得计较。
  4. "swap 这个名字取错了"——名字没问题,方式有问题。等第 8 章,同一个 swap 会以正确的形式再出场一次。
自测三问(点开对答案) 1. 传值调用传的是什么?——实参值的**副本**,不是变量本身。 2. 为什么 `swap(a, b)` 换不动 `a`、`b`?——函数只拿到复制出来的 `x`、`y`,互换的是它们,原件另有其物。 3. 想通过函数改外部变量,该怎么做?——传地址(指针或引用),第 7、8 章的内容。

相关笔记