C++中函数参数传递是程序开发的基础操作,不同的传递方式会影响函数内部对参数的修改是否能反馈到外部,也会涉及不同的内存开销。常见的参数传递方式包括值传递、引用传递和指针参数传递,三者在实现逻辑和使用效果上存在明显差异。

三种参数传递方式的基本概念
值传递
值传递是C++默认的传递方式,函数调用时会将实参的值复制一份给形参,形参和实参是两个独立的变量,占用不同的内存空间。函数内部对形参的修改不会影响外部的实参。
引用传递
引用传递是给实参起一个别名,形参和实参指向同一块内存空间,函数内部对形参的修改会直接作用于实参。引用在定义时必须初始化,且之后不能再引用其他变量。
指针参数传递
指针参数传递是将实参的地址传递给形参,形参是指针变量,存储的是实参的内存地址。通过解引用指针可以访问和修改实参的值,本质上也是通过地址操作实参,但和引用传递的实现形式不同。
三种传递方式的代码示例对比
下面通过一个修改整型变量值的示例,直观展示三种传递方式的差异:
#include <iostream>
using namespace std;
// 值传递函数
void value_pass(int a) {
a = 20; // 修改的是形参a,不影响外部实参
cout << "值传递函数内a的值: " << a << endl;
}
// 引用传递函数
void reference_pass(int &a) {
a = 30; // 修改的是实参的别名,直接影响外部实参
cout << "引用传递函数内a的值: " << a << endl;
}
// 指针参数传递函数
void pointer_pass(int *a) {
*a = 40; // 解引用指针修改指向的内存值,影响外部实参
cout << "指针参数传递函数内a的值: " << *a << endl;
}
int main() {
int num = 10;
cout << "初始num的值: " << num << endl;
value_pass(num);
cout << "值传递调用后num的值: " << num << endl;
reference_pass(num);
cout << "引用传递调用后num的值: " << num << endl;
pointer_pass(&num);
cout << "指针参数传递调用后num的值: " << num << endl;
return 0;
}
上述代码的运行结果为:
初始num的值: 10 值传递函数内a的值: 20 值传递调用后num的值: 10 引用传递函数内a的值: 30 引用传递调用后num的值: 30 指针参数传递函数内a的值: 40 指针参数传递调用后num的值: 40
从结果可以看出,值传递没有修改外部实参,而引用传递和指针参数传递都成功修改了外部的num变量。
三种传递方式的核心差异对比
我们可以通过以下维度对三种传递方式进行详细对比:
| 对比维度 | 值传递 | 引用传递 | 指针参数传递 |
|---|---|---|---|
| 内存开销 | 复制实参的完整值,开销随实参类型大小变化 | 无额外内存开销,只是别名 | 复制地址值,固定为一个指针大小的开销 |
| 是否影响实参 | 不影响 | 影响 | 影响(需解引用操作) |
| 是否可以为空 | 无此概念 | 不可以,引用必须初始化绑定有效变量 | 可以,指针可以赋值为nullptr |
| 是否需要解引用 | 不需要 | 不需要,直接使用形参即可 | 需要,通过*操作符解引用才能访问实参 |
| 重新赋值能力 | 形参可以随意修改,不影响实参 | 修改形参等于修改实参,不能重新绑定其他变量 | 指针本身可以重新指向其他地址 |
适用场景分析
- 值传递适合传递小型内置类型,比如int、char等,或者不需要修改实参、只需要使用实参值的场景,逻辑简单清晰。
- 引用传递适合需要修改实参,且确定实参一定有效的场景,比如函数需要返回多个结果时,用引用作为输出参数,代码更简洁。
- 指针参数传递适合需要修改实参,且实参可能为空、或者需要在函数内切换指向不同变量的场景,比如操作动态分配的内存、链表节点修改等场景。
注意事项
使用指针参数传递时,需要注意判断指针是否为空,避免解引用空指针导致程序崩溃,示例代码如下:
void safe_pointer_pass(int *a) {
if (a == nullptr) { // 先判断指针是否为空
cout << "指针为空,无法修改" << endl;
return;
}
*a = 50;
cout << "安全指针传递修改后的值: " << *a << endl;
}
而引用传递因为必须绑定有效变量,不需要做空值判断,代码更简洁,但灵活性不如指针参数传递。