c++ 函数参数传递
C++ 中的值传递和地址传递
在 C++ 中,函数参数传递主要有两种方式:值传递和地址传递(指针传递和引用传递都属于地址传递的变体)。
1. 值传递
特点
- 函数接收的是实参的副本
- 对形参的修改不会影响原始变量
- 适用于小型数据(基本类型、小型结构体)
void increment(int x) {x++; // 只修改局部副本
}int main() {int a = 5;increment(a);cout << a; // 输出 5,原始值未改变return 0;
}
值传递优缺点
优点:
- 安全,原始数据不会被意外修改
- 实现简单直观
缺点:
- 对于大型对象,复制开销大
- 无法通过参数返回修改结果
2. 地址传递
2.1 指针传递
void increment(int* x) {(*x)++; // 解引用并修改原始值
}int main() {int a = 5;increment(&a); // 传递地址cout << a; // 输出 6,原始值被修改return 0;
}
2.2 引用传递
void increment(int& x) {x++; // 直接修改原始值
}int main() {int a = 5;increment(a); // 看起来像值传递,实际是引用传递cout << a; // 输出 6return 0;
}
地址传递的优缺点
优点:
- 避免大型对象的复制开销(减少内存拷贝,提升性能)
- 可以在函数中修改实参的值
#include <iostream>
using namespace std;void func(int a, int b, int c, int* max, int* min)
{// 先假设a是最大值,b是最小值*max = a;*min = b;if (b > a) {*max = b;*min = a;}// 检查c是否比当前最大值大if (c > *max) {*max = c;}// 检查c是否比当前最小值小else if (c < *min) {*min = c;}
}int main()
{int a = 5;int b = 8;int c = 1;int max, min; // 声明max和min变量func(a, b, c, &max, &min); // 传递max和min的地址cout << "max = " << max << ", " << "min = " << min << endl;return 0;
}
缺点:
- 可能意外修改原始数据(指针传递)
- 指针可能为 nullptr(需要检查)
- 引用不能重新绑定(有时是优点)
3. 对比表格
特性 | 值传递 | 指针传递 | 引用传递 |
---|---|---|---|
传递内容 | 值的副本 | 内存地址 | 别名(隐式指针) |
语法 | func(int x) | func(int* x) | func(int& x) |
调用方式 | func(a) | func(&a) | func(a) |
修改原始值 | 不能 | 能 | 能 |
空值检查 | 不需要 | 需要 | 不需要 |
性能 | 小型数据高效 | 大型数据高效 | 大型数据高效 |
安全性 | 高 | 中(可能空指针) | 高 |
可读性 | 直观 | 需要理解指针 | 直观 |
4. 现代C++最佳实践:
- 优先使用const引用传递大型只读对象
void printBigObject(const BigObject& obj);
- 使用智能指针代替裸指针进行资源管理
void process(std::shared_ptr<Resource> res);
5. 示例:三种方式实现swap函数
// 值传递(无法实现swap)
void swap_fail(int a, int b) {int temp = a;a = b;b = temp;
}// 指针传递
void swap_ptr(int* a, int* b) {int temp = *a;*a = *b;*b = temp;
}// 引用传递(推荐)
void swap_ref(int& a, int& b) {int temp = a;a = b;b = temp;
}int main() {int x = 1, y = 2;swap_fail(x, y);cout << x << " " << y; // 1 2(未交换)swap_ptr(&x, &y);cout << x << " " << y; // 2 1(已交换)swap_ref(x, y);cout << x << " " << y; // 1 2(再次交换)return 0;
}
为什么函数内部不能直接修改指针本身
在 C++ 中,函数参数传递默认是值传递,包括指针参数。
1. 指针参数的本质
当你传递指针给函数时:
void func(int* ptr) {ptr = new int(10); // 这只会修改局部副本
}int main() {int* p = nullptr;func(p);// p 仍然是 nullptr!
}
这是因为:
- 函数接收到的是指针的副本(指针值的拷贝)
- 修改这个副本(
ptr = ...
)不会影响原始指针 - 这就像值传递一个
int
参数一样
2. 可以修改指针指向的内容
虽然不能直接修改指针本身,但可以修改指针指向的内容:
void modifyContent(int* ptr) {*ptr = 10; // 这会修改指针指向的内存
}int main() {int x = 5;int* p = &x;modifyContent(p);// x 现在是 10
}
3. 如何真正修改指针本身
如果需要修改指针本身(而不仅是指向的内容),你需要:
方法1:使用指针的指针(二级指针)
void modifyPointer(int** ptrPtr) {*ptrPtr = new int(20); // 修改原始指针
}int main() {int* p = nullptr;modifyPointer(&p);// p 现在指向新分配的 intdelete p; // 记得释放内存
}
方法2:使用指针的引用(更推荐)
void modifyPointer(int*& ptrRef) {ptrRef = new int(30); // 直接修改原始指针
}int main() {int* p = nullptr;modifyPointer(p);// p 现在指向新分配的 intdelete p;
}
4. 原始代码问题分析
void func(..., int* max, int* min) {max = &a; // 这只修改了局部副本min = &b; // 这只修改了局部副本
}
这些赋值操作只修改了函数内部的指针副本,不会影响 main()
中的原始指针。
5. 解决方案对比
方法 | 示例 | 优点 | 缺点 |
---|---|---|---|
返回值 | int* func() | 简单 | 只能返回一个值 |
二级指针 | func(int** max) | 可以修改多个指针 | 语法复杂 |
指针引用 | func(int*& max) | 语法简洁 | 需要C++引用支持 |
修改指向的内容 | func(int* max) | 最简单 | 需要预先分配内存 |
6. 最佳实践建议
- 如果只需要修改指针指向的内容,使用一级指针
- 如果需要修改指针本身,优先使用指针引用 (
int*&
) - 在C代码中或需要兼容C时,使用二级指针 (
int**
) - 考虑使用智能指针 (
std::unique_ptr&
) 来避免内存管理问题