在C++中,函数参数传递主要有值传递、引用传递和指针传递三种方式,三者的底层实现逻辑和实际表现存在明显差异,理解这些差异是写出高效正确代码的基础。

值传递
值传递是最基础的参数传递方式,调用函数时会将实参的值复制一份传递给形参,形参和实参是两个独立的变量,占用不同的内存空间。
在函数内部修改形参的值,不会影响到外部的实参,因为操作的是复制出来的副本。值传递适用于传递小型内置类型数据,或者不需要修改外部变量的场景。
#include <iostream>
using namespace std;
// 值传递函数,修改形参不会影响实参
void valuePass(int num) {
num = 20;
cout << "函数内形参值: " << num << endl;
}
int main() {
int a = 10;
valuePass(a);
cout << "函数外实参值: " << a << endl;
return 0;
}
上述代码执行后,函数内输出20,函数外输出10,说明形参的修改没有影响到实参。
引用传递
引用传递是给实参起一个别名,形参和实参指向同一块内存空间,对形参的修改本质上就是对实参的修改。引用传递不需要复制实参的值,效率更高,适合传递大型对象或者需要修改外部变量的场景。
引用必须在定义的时候初始化,且之后不能再引用其他变量,这是引用传递的重要特性。
#include <iostream>
using namespace std;
// 引用传递函数,修改形参会同步影响实参
void referencePass(int &num) {
num = 20;
cout << "函数内引用值: " << num << endl;
}
int main() {
int a = 10;
referencePass(a);
cout << "函数外实参值: " << a << endl;
return 0;
}
上述代码执行后,函数内和函数外都会输出20,说明形参的修改直接作用到了实参上。
指针传递
指针传递是将实参的地址传递给形参,形参是一个指针变量,存储的是实参的内存地址。通过解引用指针可以访问和修改实参的值,本质上也是操作实参所在的内存空间。
指针传递和引用传递都能实现修改外部变量的效果,但指针本身是一个独立的变量,可以为空,也可以修改指向的地址,这是和引用传递的核心差异之一。
#include <iostream>
using namespace std;
// 指针传递函数,通过解引用指针修改实参
void pointerPass(int *num) {
if (num != nullptr) { // 避免空指针解引用
*num = 20;
cout << "函数内指针解引用值: " << *num << endl;
}
}
int main() {
int a = 10;
pointerPass(&a);
cout << "函数外实参值: " << a << endl;
return 0;
}
上述代码执行后,函数内和函数外都会输出20,和引用传递的效果类似,但实现方式是通过地址操作完成的。
三种传递方式的区别对比
为了更清晰地理解三者的差异,我们可以从多个维度进行对比:
| 对比维度 | 值传递 | 引用传递 | 指针传递 |
|---|---|---|---|
| 是否复制实参 | 是,复制完整值 | 否,是实参的别名 | 否,复制的是实参地址 |
| 是否影响外部实参 | 否 | 是 | 是(解引用后) |
| 是否可以为空 | 无此概念 | 不可以,必须初始化 | 可以,指针可以为nullptr |
| 传递效率 | 低(复制开销大时) | 高 | 高 |
| 适用场景 | 小型内置类型、无需修改实参 | 大型对象、需要修改实参、避免复制开销 | 需要动态判断地址有效性、兼容C语言接口 |
场景选择建议
在实际开发中,我们可以根据需求选择合适的传递方式:
- 如果传递的是小型内置类型,且不需要修改外部变量,优先选择值传递,代码更直观。
- 如果传递的是大型对象,或者需要修改外部变量,优先选择引用传递,效率更高且语法更简洁。
- 如果需要兼容C语言接口,或者需要判断传递的地址是否有效(允许空值),可以选择指针传递。
需要注意的是,如果不想在函数内修改实参,即使使用引用传递或指针传递,也可以加上const修饰,避免误操作修改外部变量,比如void func(const int &num)或者void func(const int *num)。