C++:参数传递方法(Parameter Passing Methods)
目录
1. 值传递(Pass by Value)
2. 地址传递(Pass by Address)
3. 引用传递(Pass by Reference)
数组作为函数参数(Array as Parameter)
数组作为函数返回值
什么是函数(Function)?
定义:函数是一段封装了特定功能的代码块(Code Block),接受输入(参数,Parameters),执行操作,并可选返回结果。函数提高代码的模块化(Modularity)和可复用性(Reusability)。
为什么需要函数?
-
核心需求:程序需要重复执行某些逻辑(如计算、数据处理),而不希望重复写代码。函数将逻辑封装,调用时只需提供输入。
什么是结构体(Structure)?
定义:结构体是一种用户定义的数据类型(User-Defined Data Type),将多个相关变量(成员,Members)组织成一个整体,方便管理和操作数据。
为什么需要结构体?
-
核心需求:程序需要处理一组相关数据(如学生的姓名、学号、成绩),单独定义变量会很零散,结构体将它们组合成一个逻辑单元。
1. 值传递(Pass by Value)
void swap(int a, int b) { // 值传递,a和b是x和y的副本int temp = a;a = b;b = temp;
}
int main() {int x = 10, y = 20;swap(x, y); // 传递x和y的副本std::cout << "x: " << x << ", y: " << y << std::endl; // 输出:x: 10, y: 20return 0;
}
原理:
-
函数接收到的是
a
和b
的拷贝副本。 -
这些副本在函数的栈区 stack里存在。
-
修改这些副本不会影响原始变量。
工作原理:a和b是x和y的副本,函数修改a和b,但x和y不变。
优点:
-
安全:函数无法修改原始数据,适合不需要改变输入的场景。
-
简单:代码直观,适合小数据(如int)。
缺点:
-
无法实现swap目标:原始变量x和y未交换。
-
效率低:对于大对象(如结构体、数组),复制副本耗时耗空间。
适用场景:只读操作或小数据传递。
2. 地址传递(Pass by Address)
指针传递将变量的内存地址(Memory Address)传递给函数,函数通过指针(Pointer)操作原始数据。
void swap(int *a, int *b) {int temp = *a;*a = *b;*b = temp;
}
原理:
-
传入的是变量的地址(内存位置)。
-
函数通过指针
*a
、*b
解引用,直接修改了内存中的值。 -
实际修改的是原始变量。
工作原理:a和b存储x和y的地址,通过*(解引用,Dereference)访问和修改原始数据。
-
优点:
-
可修改原始数据:成功实现swap目标,x和y值交换。
-
高效:只传递地址(通常4或8字节),适合大对象(如结构体、数组)。
-
支持空指针检查:可用nullptr表示无效数据。
-
缺点:
-
复杂性增加:需要使用*和&,容易出错(如忘记解引用)。
-
需检查空指针:否则可能导致崩溃(未定义行为,Undefined Behavior)
3. 引用传递(Pass by Reference)
引用传递将变量的**引用(Reference)**传递给函数,函数直接操作原始数据。引用是C++特有的机制,像是变量的“别名”(Alias)。
void swap(int &a, int &b) {int temp = a;a = b;b = temp;
}
原理:
-
&
并不是取地址,而是声明a 和 b 是引用(Reference)。 -
它们是原始变量的别名。
-
编译器在底层偷偷用指针处理,但你写的代码不需要
*
和&
。
工作原理:a和b是x和y的引用,操作a和b直接修改x和y。
优点:
-
可修改原始数据:成功实现swap目标。
-
语法简洁:无需*或&,代码像值传递一样直观。
-
高效:传递引用(底层是地址),不复制数据。
缺点:
-
可能意外修改:调用者可能不希望数据被改变,需明确意图。
-
无空值检查:引用必须绑定有效变量,无法像指针用nullptr。
数组作为函数参数(Array as Parameter)
void printArray(int arr[], int size) {for (int i = 0; i < size; i++)cout << arr[i] << " ";
}
在 C++ 中,数组作为函数参数传递时,实际上传递的是数组的首地址(指针)!
🔬 C++ 中数组作为参数时,会退化为指针
void printArray(int arr[], int size);
// 实际等价于:
void printArray(int* arr, int size);
原因:
在函数参数中,数组类型会退化(decay)为指向其首元素的指针。这是 C/C++ 语言设计的一部分,背后原因:
-
函数参数不能接收整个数组的“值”,因为数组不能整体赋值(不能复制整个内存块)。
-
所以只能传递一个地址,也就是数组第一个元素的地址
&arr[0]
。 -
这也是为什么你必须单独传一个
size
参数 —— 因为函数内无法知道数组长度。
数组作为函数返回值
C++中,函数不能直接返回局部数组(栈上分配,函数结束即销毁),需要通过指针(堆内存或外部数组)或现代C++容器(如std::vector)实现。
int[] getArray() { // ❌ 错误:不能返回数组类型int arr[5] = {1, 2, 3, 4, 5};return arr; // ❌ 错误:返回局部数组
}
-
它是一个在栈区(Stack)上分配的内存块。
-
函数执行完毕后,这块内存就会被自动销毁。
-
返回这个地址 → 悬空指针(Dangling Pointer) → 引发未定义行为。
✅ 正确的做法:用“能长期存在”的内存来返回数组
我们有几种安全的替代方案,分别基于不同内存管理方式:
方法 1:返回堆上数组
int* getArray() {int* arr = new int[5]{1, 2, 3, 4, 5}; // 分配在堆上return arr; // 返回地址
}
特点:
-
返回的是堆内存地址,函数执行完也不会释放。
-
必须手动
delete[]
来释放内存,否则内存泄漏。
方法 2:用 static
关键字返回静态数组
int* getArray() {static int arr[5] = {1, 2, 3, 4, 5};return arr; // 返回的是静态变量地址
}
特点:
-
static
数组会在程序整个生命周期中都存在。 -
不在栈上,不会被销毁。
-
不需要
delete
,但多次调用会共享同一个数组。