CC++ & Algorithm

传值与传引用

困难8
语言版本:C++
概述:理解传递参数时复制一份(传值)和直接共用一份(传引用)的区别,就像复印照片与共享电子文档。

传值与传引用:复印照片还是共享电子文档?

给好朋友分享一张照片,你有两种方式:一种是把它复印一份给他,他可以在复印件上涂鸦、剪裁,但你的原版照片丝毫不变;另一种是直接把电子版发给他,你们俩同时查看同一个文件,他修改后你也会看到变化。C++ 里的函数参数传递也遵循同样的道理——传值就像复印照片,传引用就像共享电子文档。

传值(pass by value):复制一份,互不影响

默认情况下,C++ 函数的参数采用传值方式。调用函数时,实参的值会被复制一份给形参。函数内部对形参的任何修改,都只作用于那个副本,外面的原变量纹丝不动。

看下面的例子,我们想给一个数加 1,但主函数里的变量并没有被改变:

#include <iostream>
using namespace std;

void addOne(int x) {    // 传值,x 是 num 的副本
    x = x + 1;          // 修改的是副本
    cout << "函数内x = " << x << endl;
}

int main() {
    int num = 5;                     // 定义变量num,初始值5
    addOne(num);                     // 调用函数,传入num的副本
    cout << "主函数中num = " << num << endl;  // 仍然是5!
    return 0;
}

输出:

函数内x = 6
主函数中num = 5

生活中的例子:你给同桌抄作业,抄的是你写的复印件。他在复印件上改来改去,你自己的作业本一个字都没变。传值就是这种“给了复制品”的方式。

适用场景:当你只需要读取变量的值,而不希望函数意外修改它时,用传值更安全。比如计算成绩的总分,只需要传入成绩的副本,函数内部怎么算都不会改掉原成绩。

传引用(pass by reference):共用同一份,改谁都变

如果希望函数直接修改外部的变量,就要用传引用。在函数形参的类型后面加一个 & 符号(称为引用符号),就像给出一个共享链接。此时形参就是实参的别名,对形参的任何操作都等同于对原变量的操作——你改了,我也看到了。

上面那个加 1 的例子,改成传引用看看:

#include <iostream>
using namespace std;

void addOneRef(int &x) {  // 加上&表示传引用,x是num的别名
    x = x + 1;            // 改别名等于改原变量
    cout << "函数内x = " << x << endl;
}

int main() {
    int num = 5;                         // 定义变量num
    addOneRef(num);                      // 传引用,不带&符号,但函数接收时是引用
    cout << "主函数中num = " << num << endl;  // 变成6了!
    return 0;
}

输出:

函数内x = 6
主函数中num = 6

生活中的例子:你和朋友在同一份在线文档里写班级活动计划。他添加一条“买零食”,你刷新页面也立刻看到新增内容。传引用就是这种“共享同一份文档”的方式。

适用场景:当函数需要修改传入的变量,或者要避免复制大型数据(比如数组、结构体)带来的性能开销时,用传引用。比如编写一个 swap 函数交换两个变量的值,就必须用传引用。

传值与传引用的核心区别

对比项传值传引用
是否复制复制一份副本不复制,共用原变量
修改形参影响实参?不影响影响
安全性安全(不会意外修改外部变量)有风险(可能误改原变量)
效率(对大型数据)慢(复制开销大)快(不复制)
语法普通类型参数类型后加 &

新手常见错误

错误1:在需要修改原变量的地方忘记加 &

很多同学写交换函数时,误用了传值:

// 错误示范:传值无法交换
void swapBad(int a, int b) {
    int temp = a;   // 交换的是副本
    a = b;
    b = temp;
}
// 调用后原变量不变

正确的写法必须加上 &,让形参成为实参的别名。

错误2:传引用却传入常量或表达式

传引用要求实参必须是一个可修改的左值(即变量)。不能直接传入常量(如 5)或表达式(如 a+b),因为常量没有存储地址,无法被引用。

void setZero(int &x) { x = 0; }
// 调用:
setZero(10);           // 错误!10是常量
setZero(a + b);        // 错误!表达式不是左值

错误3:用传引用仅仅为了读取,但无意中修改了原数据

如果你只是读取数据(比如打印成绩),用传值或 const 引用更安全。不小心用普通引用,可能会在函数里意外改掉数据:

void printScore(int &score) {
    score = score + 100;    // 误操作:把原成绩改了!
    cout << "成绩:" << score << endl;
}

正确的做法:如果只读,用 const int &(常引用)或传值。

完整示例:用传引用实现交换函数

下面是一个完整的可运行程序,演示如何通过传引用交换两个变量的值。这是 C++ 初学函数时最经典的例子。

#include <iostream>
using namespace std;

// 交换两个整数,使用传引用
void swapValues(int &a, int &b) {  // a和b是传入变量的别名
    int temp = a;                  // 用临时变量保存a的值
    a = b;                         // 将b的值赋给a(修改原变量x)
    b = temp;                      // 将原a的值赋给b(修改原变量y)
}

int main() {
    int first = 10;                // 定义变量first,初始值10
    int second = 20;               // 定义变量second,初始值20

    cout << "交换前:first = " << first << ", second = " << second << endl;

    swapValues(first, second);     // 传引用,实际交换了first和second

    cout << "交换后:first = " << first << ", second = " << second << endl;
    return 0;
}

输出:

交换前:first = 10, second = 20
交换后:first = 20, second = 10

小窍门:当你需要函数修改传入的变量时,用传引用。如果只是读取数据,用传值更安全(不会意外改掉原数据)。试试自己写一个 swap 函数(交换两个数),用传引用实现。

相关知识点指引

  • const 引用:如果你只想读取数据又不想复制(提高效率),可以写成 void show(const int &x),这样既不能修改原变量,又没有复制开销。
  • 指针与引用:指针也能达到类似“修改原变量”的效果,但语法更复杂(需要取地址和解引用)。引用是 C++ 更安全的替代品。
  • 引用作为返回值:函数可以返回引用,比如 int& getElement(int arr[], int index),这样可以对数组元素直接赋值。
  • 函数重载:传值和传引用可以构成重载(参数类型不同),但要注意重载决议的细节。

例题精讲

1单选题

以下代码输出什么? void modify(int x) { x = 10; } int main() { int a = 5; modify(a); cout << a; return 0; }

A5
B10
C0
D不确定
2填空题
以下程序试图交换两个整数的值,但结果不正确。请在划线处填入合适的修改方式(使用引用参数):

void swap(___ x, ___ y) {
    int temp = x;
    x = y;
    y = temp;
}
int main() {
    int a = 3, b = 7;
    swap(a, b);
    // 期望输出 a=7, b=3
}
3判断题

下面的代码中,函数`fun`通过传引用修改了全局数组的第一个元素。 int arr[] = {1,2,3}; void fun(int (&r)[3]) { r[0] = 100; } int main() { fun(arr); cout << arr[0]; // 输出100 return 0; }

4单选题

以下关于传值和传引用的说法,哪一项是正确的?

A传值时,函数内对参数的任何修改都会影响实参。
B传引用时,函数内对参数的修改不会影响实参。
C使用传引用可以避免大对象拷贝的开销,但同时可能意外修改原对象。
D传引用必须使用指针实现,与传值没有本质区别。
5填空题
以下程序希望在函数中返回两个数的和并同时通过参数返回差值。请完成函数定义,使用引用参数。

void calc(int a, int b, int &sum, ___ &diff) {
    sum = a + b;
    diff = a - b;
}
int main() {
    int s, d;
    calc(10, 3, s, d);
    cout << s << ' ' << d; // 输出13 7
}