C++引用传递
困难23引用传递:别让复制拖慢你的程序
你有没有和朋友共用过一本书?你翻到第50页,朋友翻到的也是第50页。你在页边画了个小星星,朋友翻开也能看到。如果朋友把星星擦掉,你看到的也会消失。
在C++里,引用传递就像这样——函数里的参数和外面的变量是同一个东西,没有复制品。你改函数里的参数,外面的变量也会跟着变。这比复制一份数据快得多,但也需要小心:你不想改的东西,可能一不小心就被改了。
什么是引用传递?——你的变量有了“小名”
每个人都有自己的名字,但你的好朋友可能给你起了一个外号。叫外号的时候,指的仍然是同一个人。
引用(Reference) 就是变量的“外号”。当你把变量通过引用传给函数时,函数内部用的参数名只是外号,实际操作的是原来那个变量本身。
看个生活中的例子:
假设你有一张记满零花钱本子的纸,上面写着“口袋里的钱:20元”。你参加了一个“变有钱”魔术活动,魔术师说“给我你钱的别名”,然后他对着这个别名说“翻倍!”——实际上他操作的是你的真实零花钱,所以你的钱就变成了40元。
如果他用的是复制(值传递),他只能复制一张新纸,在上面写“40元”,而你原来的纸上还是20元。
关键点:
- 引用不新分配内存,它只是已有内存的另一个名字。
- 函数里对引用参数的修改,直接影响函数外面原来的变量。
引用传递怎么写?——&符号是钥匙
在函数定义中,参数类型后面加一个 & 符号,就表示这个参数是引用。比如 int &a 表示 a 是某个整型变量的引用。
#include <iostream>
using namespace std;
// 值传递:交换的是副本,不影响原件
void swapByValue(int a, int b) {
int temp = a;
a = b;
b = temp;
// 这里 a 和 b 是拷贝的,改完就销毁了
}
对比引用传递:
#include <iostream>
using namespace std;
// 引用传递:交换的是原件
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
cout << "函数内部:a = " << a << ", b = " << b << endl;
}
int main() {
int x = 5, y = 10;
cout << "调用前:x = " << x << ", y = " << y << endl;
swap(x, y); // 传递的是 x 和 y 的“别名”
cout << "调用后:x = " << x << ", y = " << y << endl;
return 0;
}
运行结果:
调用前:x = 5, y = 10
函数内部:a = 10, b = 5
调用后:x = 10, y = 5
看到了吗?swap函数里交换的 a 和 b 就是外面的 x 和 y,所以外面也交换了。
引用传递有什么好处?——省时省力还能直接改
1. 避免复制大块数据
假设你有一个超长的数组(比如几百个学生的考试成绩),如果按值传递,函数会复制整个数组,既慢又占内存。用引用传递,只传递一个“别名”,什么都不用复制。
#include <iostream>
using namespace std;
// 引用方式打印数组,不复制
void printArray(const int arr[], int size) {
// 注意:数组本身传递时是地址,但这里用 const 保护防止误改
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
其实数组名本身就是地址,但引用同样能避免复制。
2. 直接修改外部变量
像上面的交换函数,或者修改成绩、增加零花钱等,用引用比用指针更直观,写法简单。
#include <iostream>
using namespace std;
// 给你的零花钱加 5 元,直接改原件
void addMoney(int &money) {
money += 5;
}
int main() {
int pocketMoney = 20; // 零花钱
cout << "原来有:" << pocketMoney << " 元" << endl;
addMoney(pocketMoney); // 传入引用
cout << "现在有:" << pocketMoney << " 元" << endl;
return 0;
}
输出:
原来有:20 元
现在有:25 元
3. 让函数返回多个值
一个函数只能 return 一个值,但用引用参数可以“带出”多个结果。比如同时计算全班平均分和最高分:
#include <iostream>
using namespace std;
// 计算平均分和最高分,通过引用输出两个结果
void calcStats(int scores[], int n, double &avg, int &maxScore) {
int sum = 0;
maxScore = scores[0];
for (int i = 0; i < n; i++) {
sum += scores[i];
if (scores[i] > maxScore) maxScore = scores[i];
}
avg = (double)sum / n;
}
int main() {
int scores[] = {88, 76, 95, 62, 81}; // 考试成绩
int stuNum = 5; // 学生人数
double average; // 平均分
int highest; // 最高分
calcStats(scores, stuNum, average, highest); // 通过引用带回结果
cout << "平均分:" << average << ",最高分:" << highest << endl;
return 0;
}
输出:
平均分:80.4,最高分:95
新手最容易犯的错误
错误1:忘记写 &,变成值传递
这是最常见的坑。你明明想改原件,却复制了一份副本。
// 错误版本:没有 &
void wrongAdd(int a) { // a 是副本
a += 10;
}
int main() {
int num = 5;
wrongAdd(num);
cout << num; // 输出 5,没变!
return 0;
}
正确写法:void rightAdd(int &a) { a += 10; }
错误2:引用绑定到临时变量(临时值)
引用必须绑定到一个变量上,不能绑定到一个计算结果或字面量上。
int &ref = 5; // 错误!5 不是变量
int &ref2 = (3 + 4); // 错误!临时值不行
正确的引用必须绑定到一个有名字的内存变量上:
int x = 5;
int &ref = x; // 正确
错误3:误以为引用和指针一样(但别担心,四级不要求指针)
引用比指针更安全,因为它一旦绑定就不能再指向别的变量。但新手容易混淆“别名”和“地址”。记住:引用就是同一个东西的不同名字。
完整示例:用引用传递修改考试成绩
小明考试得了78分,老师想让他加10分作为进步奖。我们用引用传递直接修改:
#include <iostream>
using namespace std;
// 给成绩加分,直接改原变量
void addBonus(int &score, int bonus) {
score += bonus;
}
int main() {
int xiaoMingScore = 78; // 小明的成绩
int xiaoHongScore = 92; // 小红的成绩
cout << "加分前:小明 " << xiaoMingScore << " 分,小红 " << xiaoHongScore << " 分" << endl;
addBonus(xiaoMingScore, 10); // 小明加10分
addBonus(xiaoHongScore, 5); // 小红加5分
cout << "加分后:小明 " << xiaoMingScore << " 分,小红 " << xiaoHongScore << " 分" << endl;
return 0;
}
运行结果:
加分前:小明 78 分,小红 92 分
加分后:小明 88 分,小红 97 分
相关知识点指引
- 值传递:和引用传递相反,复制一份副本,函数内修改不影响外面。适合小数据且不想改变原值的情况。
- 常量引用(
const int &):既能避免复制,又能防止函数内意外修改原值。比如void show(const int &a)只能读取不能修改。 - 返回引用:函数可以返回引用(比如
int& getValue(int arr[], int idx)),让调用者能直接修改数组元素。但要注意不要返回局部变量的引用。 - 指针(更高一级):也能达到类似效果,但语法更复杂。引用可以理解为“自动解引用的指针”,更安全易用。
现在你可以自信地说:“引用传递就是给变量取了一个小名,函数里叫小名,其实叫的就是你原来的名字!”
例题精讲
关于C++中的引用传递,以下说法正确的是?
在C++中,引用传递和指针传递都能在函数内部修改外部变量的值,但引用更安全且语法更简洁。
以下函数使用引用交换两个整数的值,请补全代码。
void swap(int &a, int &b) {
int temp = ___;
a = b;
b = ___,
}执行以下代码后,变量x的值是多少? void func(int &y) { y += 10; } int main() { int x = 5; func(x); return 0; }
下列代码中,函数返回一个引用,请补全函数定义,使其能够返回数组元素的引用并修改该元素。
int& getElement(int arr[], int index) {
return ___;
}
int main() {
int nums[] = {1,2,3};
getElement(nums, 1) = 100;
// 现在nums[1]的值为100
return 0;
}