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

同一个参数有两个身份:定义时写的是形参(函数自己开的一个空盒子,等着装东西),调用时递过去的是实参(调用方手里的真货)。调用发生的那一刻,实参的值被复制一份装进形参盒子——所以函数里怎么改形参,外面的实参都毫发无伤。

📝 本节和前面几节的关系

6.2.1 讲的是"不收货"的空函数,这一节讲"收货"的细节,并引出本章第一个大概念:复制。它会一路贯穿到 6.3.1 的传值调用——那里用一个"交换两个数却交换不了"的经典例子把这件事彻底钉死。旧知识全用得上:参数本质就是变量声明(2.3.2),赋值 = 是从右往左装盒子(3.1.4)。

一、名字的来源

int add(int a, int b) {        // ← a、b 是形参(形式上的参数)
    return a + b;
}

int main() {
    int x = 3, y = 5;
    int s = add(x, y);         // ← x、y 是实参(实际传的参数)
    return 0;
}
形参 实参
写在哪 函数定义/声明的参数表里 函数调用的圆括号里
是什么 变量(函数自己开的盒子) 值或表达式(调用方手里的货)
个数 定义几个就几个 必须和形参个数、类型对得上
名字重要吗 只在函数体内部用 与形参叫什么名字毫无关系

记忆锚点:形参是"名义上的",实参是"实际上有的"。中文这两个字起得极准——"形" = 形式上的、占位的;"实" = 真的。

二、调用那一刻发生了什么(三步)

以 add(x, y) 为例:

  1. 开盒子——函数为形参 a、b 新开两个盒子(和外面的 x、y 毫无关系)
  2. 复制值——把实参的值拷进去:a = 3、b = 5
  3. 干活 + 销毁——函数体在自己的盒子里算,交回结果;函数一结束,这些盒子全部消失

所以:函数里写 a = 100;,外面的 x 仍然是 3。这不是 bug,是语言的规定——它就是下一节 6.3.1 要正式讲的传值调用。

三、动手 Demo:亲手验证"改不动原件"

#include <iostream>
using namespace std;

void tryChange(int n) {                 // n 是形参
    cout << "函数里,改之前 n = " << n << endl;
    n = 100;                            // 改的是函数自己的盒子
    cout << "函数里,改之后 n = " << n << endl;
}

int main() {
    int n = 7;                          // 这里的 n 是实参(同时是 main 的局部变量)
    cout << "调用前,main 里 n = " << n << endl;
    tryChange(n);
    cout << "调用后,main 里 n = " << n << endl;
    return 0;
}

看到什么算成功:

调用前,main 里 n = 7
函数里,改之前 n = 7
函数里,改之后 n = 100
调用后,main 里 n = 7

最后一行是本节的全部重点:函数里明明把它改成了 100,回到 main 还是 7。两个 n 只是同名,是两间不同的房子。

30 秒实验:把 tryChange(n) 改成 tryChange(7)(直接传常量)——照样能跑。说明实参只要是"值"就行,不必是变量;传常量进去,函数里更没可能改变任何外部的东西。再试着把两个 n 改成不同名字(函数里叫 value),结果完全一样——名字根本不参与这件事。

四、常见误区

  1. "形参和实参是同一个变量"——不是。值相同,位置完全不同;同名只是巧合。
  2. "实参个数可以和形参不一样"——必须匹配。本章只有两个特例:默认参数(6.2.3,可以少传)和可变参数(6.2.4,可以多传)。
  3. "类型不匹配但能编过就没事"——编译器会做隐式转换(3.3.2 的类型楼梯):add(3.7, 5) 传进 int a 会被截断成 3,不报错,但结果不是你要的。参数类型要老实对上。
  4. "想通过形参改外部的值,是编译器坏了"——是语言就这么规定的。真要改外部,得用指针(第 7 章)或引用(第 8 章)——那时传的就不是"值"而是"地址"了。
自测三问(点开对答案) 1. 形参和实参分别写在哪?——形参在函数定义/声明的参数表里;实参在调用的圆括号里。 2. 函数里改了形参,为什么外面的实参不变?——调用时只是把实参的**值复制**给形参,两者是两个独立的盒子。 3. 形参和实参的名字必须一样吗?——完全不必,名字只在各自的范围内起作用。

相关笔记