C++: 函数参数传递
最后更新:2026-08-26
第10课我们学了函数的基础,知道函数可以接收参数。
但参数到底是怎么传给函数的?函数里改了参数,外面的变量会变吗?
这就是这一课要讲的——参数传递方式。
1. 传值(Pass by Value)
(1) 1.1 基本用法
传值是 C++ 的默认参数传递方式——实参的值会复制一份给形参。
#include <iostream>
void modifyValue(int x) { // x 是形参(副本)
x = 100; // 修改的是副本,不影响原变量
std::cout << "函数里的 x = " << x << std::endl;
}
int main() {
int a = 5;
modifyValue(a); // 传值:a 的值复制给 x
std::cout << "main 里的 a = " << a << std::endl; // a 还是 5
return 0;
}
💡 重点: 传值时,函数里修改形参,不会影响实参。
(2) 1.2 传值的优缺点
| 优点 | 缺点 |
|---|---|
| 安全(函数不能修改实参) | 大对象复制开销大(如大数组、大结构体) |
| 思路清晰 | 无法让函数"返回"多个值 |
2. 传地址(Pass by Pointer)
如果你想让函数修改外部的变量,可以传地址(指针)。
(1) 2.1 基本用法
#include <iostream>
void modifyValue(int* x) { // x 是指针
*x = 100; // 解引用,修改指针指向的值
std::cout << "函数里的 *x = " << *x << std::endl;
}
int main() {
int a = 5;
modifyValue(&a); // 传地址:把 a 的地址传给函数
std::cout << "main 里的 a = " << a << std::endl; // a 变成了 100
return 0;
}
💡 重点: 传地址时,函数通过指针可以修改实参的值。
(2) 2.2 为什么能修改?
| 步骤 | 说明 |
|---|---|
| 1 | int a = 5; — 在内存中开辟一块空间,存 5 |
| 2 | modifyValue(&a); — 把 a 的地址传给函数 |
| 3 | int* x 接收地址,指向 a 的内存空间 |
| 4 | *x = 100; — 通过指针修改 a 的值 |
3. 传引用(Pass by Reference)
C++ 引入了引用(reference),它是变量的"别名",比指针更安全、更易用。
(1) 3.1 基本用法
#include <iostream>
void modifyValue(int& x) { // x 是引用(别名)
x = 100; // 修改 x 就是修改原变量
std::cout << "函数里的 x = " << x << std::endl;
}
int main() {
int a = 5;
modifyValue(a); // 传引用:不用写 &,看起来像传值
std::cout << "main 里的 a = " << a << std::endl; // a 变成了 100
return 0;
}
💡 重点: 传引用时,形参是实参的别名,修改形参就是修改实参。
(2) 3.2 引用 vs 指针
| 对比 | 引用 | 指针 |
|---|---|---|
| 语法 | int& x = a; |
int* x = &a; |
| 是否能为空 | ❌ 不能(必须初始化) | ✅ 能(可以是 nullptr) |
| 是否要解引用 | ❌ 不用(直接用) | ✅ 要(用 *x) |
| 能否重新指向 | ❌ 不能(一旦绑定,不能改) | ✅ 能(可以指向别的变量) |
| 推荐程度 | ⭐⭐⭐⭐⭐(优先用) | ⭐⭐⭐(必要时用) |
4. 三种传递方式对比
▶ 示例 1:交换两个变量的值(难度⭐⭐)
方法一:传值(失败)
#include <iostream>
void swapFailed(int a, int b) { // ❌ 传值:修改的是副本
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 3, y = 5;
swapFailed(x, y);
std::cout << "x = " << x << ", y = " << y << std::endl; // x=3, y=5(没交换)
return 0;
}
输出:
x = 5, y = 3
方法二:传指针(成功)
#include <iostream>
void swapPointer(int* a, int* b) { // ✅ 传指针
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 3, y = 5;
swapPointer(&x, &y);
std::cout << "x = " << x << ", y = " << y << std::endl; // x=5, y=3(交换了)
return 0;
}
方法三:传引用(成功,推荐)
#include <iostream>
void swapReference(int& a, int& b) { // ✅ 传引用(推荐)
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 3, y = 5;
swapReference(x, y); // 看起来像传值,其实是传引用
std::cout << "x = " << x << ", y = " << y << std::endl; // x=5, y=3(交换了)
return 0;
}
5. 什么时候用哪种传递方式?
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 函数不需要修改参数 | 传值 或 传 const 引用 | 安全、清晰 |
| 函数需要修改参数 | 传引用 | 语法简洁、安全 |
| 需要"返回"多个值 | 传引用 | 函数可以修改多个引用参数 |
| 参数是大对象(数组、结构体) | 传 const 引用 | 避免复制开销 |
| 需要表示"没有"(如查找失败) | 传指针 | 指针可以是 nullptr |
💡 黄金法则: 优先用传引用,必要时用传指针,小对象可以用传值。
6. const 引用
如果你不想让函数修改参数,但又想避免复制开销,可以用 const 引用。
#include <iostream>
#include <string>
void printName(const std::string& name) { // const 引用:不能修改 name
std::cout << "名字:" << name << std::endl;
// name = "Alice"; // ❌ 错误:不能修改 const 引用
}
int main() {
std::string myName = "MOTO";
printName(myName); // 不用复制整个字符串,效率高
return 0;
}
💡 重点: const 引用 是 C++ 中最常用的参数传递方式之一,兼顾了安全性和效率。
7. 数组作为参数
数组作为参数时,会退化成指针(后面会详细讲)。
#include <iostream>
// 数组作为参数,需要传数组长度
void printArray(int arr, int length) {
for (int i = 0; i < length; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}
int main() {
int myArray[5] = {1, 2, 3, 4, 5};
printArray(myArray, 5); // 传数组名(就是首元素地址)
return 0;
}
💡 提示: 数组作为参数时,函数里不知道数组的长度,需要额外传一个长度参数。
8. 实战:多个返回值
▶ 示例 2:计算商和余数(难度⭐⭐)
#include <iostream>
// 用引用参数"返回"多个值
void divide(int a, int b, int& quotient, int& remainder) {
quotient = a / b;
remainder = a % b;
}
int main() {
int a = 17, b = 5;
int q, r;
divide(a, b, q, r); // q 和 r 会被修改
std::cout << a << " / " << b << " = " << q << " ... " << r << std::endl;
// 输出:17 / 5 = 3 ... 2
return 0;
}
输出:
17 / 5 = 3 ... 2
▶ 示例 3:默认参数使用(难度⭐)
#include <iostream>
#include <string>
// 带默认参数的函数
void greet(const std::string& name, const std::string& greeting = "你好") {
std::cout << greeting << "," << name << "!" << std::endl;
}
int main() {
greet("张三"); // 使用默认参数
greet("李四", "早上好"); // 指定参数
greet("王五", "晚上好"); // 指定参数
return 0;
}
输出:
你好,张三!
早上好,李四!
晚上好,王五!
预期输出:
你好,张三!
早上好,李四!
晚上好,王五!
❓ 常见问题
Q:引用和指针到底有什么区别? A:> - 引用是变量的别名,指针是存储地址的变量 > - 引用必须初始化,指针可以是
nullptr> - 引用不能重新指向,指针可以 > - 优先用引用,除非需要表示"没有"(这时用指针) Q:为什么数组作为参数时,不需要写长度? A:因为数组会退化成指针。 > > void foo(int arr) { // 等价于 void foo(int* arr) > // arr 实际上是指针,不是数组 > } > Q:传值、传引用、传指针,哪个效率高? A:> - 小对象(如int):传值最高(复制开销小) > - 大对象(如大数组、大结构体):传 const 引用最高(避免复制) > - 需要修改参数:传引用最高(语法简洁)
Q:关于function parameters最重要的是什么? A:先理解核心概念,再通过实践例子来巩固。
Q:如何高效练习function parameters? A:从简单例子开始,逐步增加难度,务必测试你的代码。
📖 小节
- 传值:复制一份,函数里修改不影响外面
- 传指针:传地址,函数里可以通过指针修改外面
- 传引用:传别名,函数里修改就是修改外面(推荐)
- const 引用:不能修改,但避免复制开销(最常用)
- 数组作为参数时会退化成指针,需要额外传长度
📝 作业
-
基础题 (Difficulty ⭐): 写一个函数
void addTen(int& x),给传入的变量加 10。在main里测试。 -
进阶题 (Difficulty ⭐⭐): 写一个函数
void minMax(int arr, int length, int& min, int& max),找出数组的最小值和最大值(用引用参数"返回")。 -
挑战题 (Difficulty ⭐⭐⭐): 写一个程序,实现"三个数排序":
-
写一个函数
void sortThree(int& a, int& b, int& c),把三个数按从小到大排序 -
在
main里让用户输入三个数,调用sortThree,然后输出排序后的结果
- 传值:拷贝参数,函数内修改不影响外部
- 传引用:共享内存,函数内修改影响外部
- 默认参数从右向左,调用时可省略
- 函数重载:同名不同参数,编译时确定
- const 引用保护参数不被修改,同时避免拷贝
9. 🚀 下一步
学会了参数传递,接下来我们学习 递归(第12课)—— 函数调用自己的神奇技巧!