一、函数参数传递的概念
在函数未被调用时,函数的形参不会占用内存空间,也没有实际的值。只有在函数被调用时,才为形参分配存储单元。每个实参都是一个表达式,其类型必须与形参的类型相符。
C++ 中参数传递有两种方式:
- 值传递
- 引用传递
本节先讲解值传递。
二、值传递的原理
值传递是指:当发生函数调用时,为形参分配内存空间,并用实参的值来初始化形参(直接将实参的值复制给形参)。这一过程是参数值的单向传递。
一旦形参获得值后,形参和实参便脱离关系。此后无论形参发生了怎样的改变,都不会影响到实参。
三、值传递的特点
| 特性 | 说明 |
|---|---|
| 传递方向 | 单向传递(实参 → 形参) |
| 内存关系 | 形参和实参占用不同的内存空间 |
| 形参修改 | 只修改形参自身的值,不影响实参 |
| 初始化方式 | 实参的值复制给形参,等价于 形参 = 实参 |
| 适用场景 | 不需要修改实参、数据量较小的场合 |
四、示例:交换两个整数(值传递方式)
以下代码尝试通过值传递交换两个整数,但无法成功:
#include <iostream>
using namespace std;
void swap(int a, int b)
{
int z = a;
a = b;
b = z;
// cout << "函数内 a,b " << a << " " << b << "\n";
}
int main()
{
int a = 2;
int b = 4;
cout << "调用前 a,b " << a << " " << b << "\n"; // 输出 2 4
swap(a, b);
cout << "调用后 a,b " << a << " " << b << "\n"; // 仍然输出 2 4
return 0;
}Code language: PHP (php)
分析:
- main 中 a = 2, b = 4。
- 调用 swap(a, b) 时,为形参 a、b 分配内存,并将实参的值复制过去。此时形参 a = 2, 形参 b = 4。
- swap 函数内部交换了形参 a 和 b 的值,形参变为 a = 4, b = 2。
- swap 执行完毕,形参所占内存被释放。
- main 中的实参 a 和 b 自始至终没有被修改,仍然是 a = 2, b = 4。
五、代码示例
以下代码进一步演示值传递的各种情况:
#include <iostream>
using namespace std;
// 示例1:基本类型的值传递
void modifyInt(int x)
{
x = 100;
cout << "modifyInt 内 x = " << x << endl; // 输出 100
}
// 示例2:值传递不影响实参
void trySwap(int a, int b)
{
int temp = a;
a = b;
b = temp;
cout << "trySwap 内 a = " << a << ", b = " << b << endl;
}
// 示例3:值传递用于计算(不修改实参)
int square(int x)
{
x = x * x; // 修改的是形参 x
return x;
}
// 示例4:结构体的值传递
struct Point
{
int x;
int y;
};
void movePoint(Point p)
{
p.x += 10;
p.y += 10;
cout << "movePoint 内 p.x = " << p.x << ", p.y = " << p.y << endl;
}
// 示例5:数组作为参数(注意:数组名退化为指针,不属于值传递)
void printArray(int arr[5])
{
arr[0] = 999; // 这会改变原数组!
for (int i = 0; i < 5; i++)
cout << arr[i] << " ";
cout << endl;
}
// 示例6:指针的值传递(传递的是地址值本身)
void modifyPointer(int* p)
{
p = NULL; // 只修改了形参 p,不影响实参
}
int main()
{
int a = 5;
modifyInt(a);
cout << "main 中 a = " << a << endl << endl; // 输出 5
int x = 2, y = 4;
cout << "交换前 x = " << x << ", y = " << y << endl;
trySwap(x, y);
cout << "交换后 x = " << x << ", y = " << y << endl << endl; // 未交换
int num = 3;
int result = square(num);
cout << "square(" << num << ") = " << result << endl;
cout << "num 仍为 " << num << endl << endl;
Point pt = { 1, 2 };
cout << "移动前 pt.x = " << pt.x << ", pt.y = " << pt.y << endl;
movePoint(pt);
cout << "移动后 pt.x = " << pt.x << ", pt.y = " << pt.y << endl << endl;
int arr[5] = { 1, 2, 3, 4, 5 };
cout << "原数组: ";
printArray(arr);
cout << "调用后 arr[0] = " << arr[0] << endl << endl; // arr[0] 变为 999
int val = 10;
int* ptr = &val;
modifyPointer(ptr);
if (ptr != NULL)
cout << "ptr 仍指向 val,值为 " << *ptr << endl;
return 0;
}Code language: C++ (cpp)
六、注意事项
- 值传递是 C++ 默认的实参-形参结合方式。
- 值传递时,实参的值被复制给形参。对于基本类型(int、double、char 等),复制开销很小;对于大型结构体或类对象,复制开销较大,此时应使用引用传递或指针传递。
- 值传递是单向的:形参的改变不会影响实参。
- 如果需要在函数中修改实参的值,不能使用值传递,应使用引用传递或指针传递(下一节介绍)。
- 数组作为函数参数时,数组名会退化为指针,传递的是数组首地址的副本,函数内通过指针可以修改数组元素。这不属于值传递,而是地址传递。
- 指针作为形参时,传递的是指针变量的值(地址),形参获得地址的副本。修改形参指向的内容(如
*p = 10)会影响实参指向的内容,但修改形参本身(如p = NULL)不影响实参。
七、常见错误
1. 误以为值传递能交换实参
void swap(int a, int b)
{
int t = a; a = b; b = t;
}
int main()
{
int x = 1, y = 2;
swap(x, y);
// 期望 x=2, y=1,实际 x=1, y=2
}Code language: JavaScript (javascript)
解决:使用引用传递 void swap(int& a, int& b) 或指针传递 void swap(int* a, int* b)。
2. 大型对象值传递导致性能问题
struct BigData { int data[10000]; };
void process(BigData d) { ... } // 每次调用复制 40000 字节Code language: JavaScript (javascript)
解决:使用 const BigData& d 避免复制。
3. 混淆指针传递和值传递
void foo(int* p)
{
p = NULL; // 只修改了形参 p
}
int main()
{
int x = 10;
int* q = &x;
foo(q);
// q 仍然指向 x,没有被修改
}Code language: JavaScript (javascript)
Previous: 递归调用
Next: 函数的参数传递-引用传递