函数¶
方法速览¶
| 任务 | 方法 | 口诀 |
|---|---|---|
| 判断实参是否被改 | 值传递拷贝改副本;引用/指针直达本体 | 值传拷、引用直达 |
| 数组作参数 | 退化为指针,sizeof 变指针大小,但能改原数组 | 数组进门变指针 |
| 重载判定 | 同名 + 参数个数或类型不同;仅返回值不同不算 | 重载看参数,不看返回 |
| 默认参数 | 从右往左连续给出,调用从右往左省略 | 默认靠右 |
什么是参数列表¶
函数定义由返回类型、函数名、参数列表、函数体组成:
- 形参:定义里占位的变量(
a、b),是函数自己的局部变量,函数结束就销毁; - 实参:调用时真正传进去的值(
add(3, 4)的 3、4); - 参数列表可以为空:
int f()或int f(void)。
四种参数传递方式¶
| 方式 | 写法 | 函数内修改影响外部? | 说明 |
|---|---|---|---|
| 值传递 | void f(int a) |
✗ | 拷贝一份实参值 |
| 引用传递 | void f(int& a) |
✓ | a 是实参的别名 |
| 指针传递 | void f(int* a) |
✓(需解引用) | 传地址,*a 才是本体 |
| 数组作参数 | void f(int a[]) |
✓ | 退化为指针,a[i] 直接改原数组 |
例(三种改法对照):
数组退化两点:
void f(int a[10])里的 10 被忽略,a 就是指针:sizeof(a) = 8(64 位);- 正因为传的是地址,
a[i] = 5;改的就是原数组——与值传递语义相反。
函数重载¶
重载:同一个函数名,参数列表不同(个数不同 / 类型不同),完成相似功能。
三条判定:
- 仅返回类型不同不构成重载:
int f(int)与double f(int)并存 → 编译错误; - 调用时按实参类型精确匹配;两个版本都“半匹配”会产生二义性编译错误;
int f(int)与int f(int&)同时存在,用变量调用也有二义性(初赛记住结论即可)。
默认参数:int pow(int b, int e = 2);必须从右往左连续(f(int a, int b = 1) 合法,f(int a = 1, int b) 非法)。
练习题目¶
练习 1 · 值传递¶
答案与解析
答案:A
值传递:形参 a 是副本,改副本不影响实参。
练习 2 · 引用传递¶
答案与解析
答案:B
int& a:a 是 x 的别名,动 a 就是动 x。
练习 3 · 指针传递¶
题目
调用后 x 的值( )- A. \(5\)
- B. \(5\)(地址值加一)
- C. \(6\)
- D. 编译错误
答案与解析
答案:C
传的是 x 的地址,*a 解引用直达本体。
干扰项思路:B 把“指针自身 +1”(那会指向下一个 int,不是改 x)。
练习 4 · 指针传参的调用写法¶
题目
void f(int* p) 要作用于变量 x,正确调用是( )
- A.
f(x); - B.
f(&x); - C.
f(*x); - D.
f(x);且函数内写p = 9;
答案与解析
答案:B
指针参数要配取地址 &x;p = 9 改的是指针本身(让 p 指向地址 9),必须 *p = 9。
练习 5 · 失效的 swap¶
题目
调用后 x、y 分别是( )- A. \(2, 2\)
- B. \(2, 1\)
- C. \(1, 1\)
- D. \(1, 2\)
答案与解析
答案:D
值传递只交换了两个副本,实参纹丝不动——这是“为什么需要引用/指针”的经典反例。
练习 6 · 生效的 swap¶
题目
把上一题的函数头改为( )后,交换能真正生效。
- A.
void swap(int& a, int& b) - B.
void swap(int a, int b)加t前面加 static - C.
void swap(double a, double b) - D.
void swap(int a[2])
答案与解析
答案:A
引用直达本体。指针版 swap(int* a, int* b) + f(&x, &y) 也可以。
练习 7 · 数组参数能改原数组¶
题目
调用后arr[0] 的值( )
- A. \(1\)
- B. 未定义
- C. \(99\)
- D. 编译错误
答案与解析
答案:C
数组名传的是首地址,a[0] 就是 arr[0]——数组参数天然“引用式”。
练习 8 · 数组参数的 sizeof¶
题目
64 位环境下:void f(int a[10]) { cout << sizeof(a); } 输出( )
- A. \(40\)
- B. \(10\)
- C. \(8\)
- D. \(4\)
答案与解析
答案:C
数组参数退化为指针,方括号里的 10 被忽略;64 位指针 8 字节。
干扰项思路:A 以为整个数组拷贝进来了。
练习 9 · 形参是局部变量¶
题目
关于形参,正确的是( )
- A. 形参在 main 里定义
- B. 形参是函数的局部变量,函数返回后销毁
- C. 形参与实参共用同一存储,始终同地址
- D. 形参不能在函数内修改
答案与解析
答案:B
形参随函数调用而生、返回而灭;值传形参是副本(地址与实参不同),引用形参才是别名。
练习 10 · 混合传递追踪¶
题目
调用后 x、y 分别是( )- A. \(1, 2\)
- B. \(2, 1\)
- C. \(2, 2\)
- D. \(1, 1\)
答案与解析
答案:A
a 是 x 的副本(x 不变),b 是 y 的别名(y 变 2)。一参一性,逐个判断。
练习 11 · 引用必须初始化¶
题目
下列写法错误的是( )
- A.
int x = 1; int& r = x; - B.
void f(int& a) { } - C.
int x = 1; int& r = x; r = 5; - D.
int& r;
答案与解析
答案:D
引用必须在定义时绑定一个变量,不能先声明后绑。
练习 12 · 三种头文件改写¶
题目
函数体为 t = a; a = b; b = t;(t 为局部变量)要实现真正的交换,函数头不能是( )
- A.
void swap(int& a, int& b) - B.
void swap(int* a, int* b)(配*t版实现) - C.
void swap(int a, int b) - D.
void swap(int& a, int& b)与 B 均可
答案与解析
答案:C
值传递交换的是副本;A/B 都能直达本体(B 的实现需相应写 *a)。
练习 13 · 重载:类型不同¶
题目
与 int add(int a, int b) 构成合法重载的是( )
- A.
double add(double a, double b) - B.
double add(int a, int b) - C.
int add(int a, int b) - D.
int Add(int a, int b)(与原函数的关系)
答案与解析
答案:A
参数类型不同 → 合法重载。B 仅返回类型不同 → 编译错误;C 完全重复 → 重复定义;D 是另一个函数(名字不同),不叫重载。
练习 14 · 重载:个数不同¶
题目
与 int f(int a) 构成合法重载的是( )
- A.
int f(int a, int b) - B.
void f(int a) - C.
int f(int a)换个文件写 - D.
double f(int a)
答案与解析
答案:A
参数个数不同 → 合法。B/D 与原函数参数列表完全相同、仅返回类型不同,都不构成重载。
练习 15 · 重载匹配¶
题目
已有 int f(int) 与 double f(double),调用 f(3) 的结果是( )
- A. 未定义
- B. 匹配
f(double) - C. 二义性错误
- D. 匹配
f(int)
答案与解析
答案:D
3 是 int 字面值,精确匹配优先;要调 double 版需写 f(3.0)。
练习 16 · 二义性¶
题目
已有 void g(int) 与 void g(double),调用 g(3.5) 与 g('a') 分别( )
- A. 前者匹配 double 版;后者匹配 int 版
- B. 前者匹配 double 版;后者二义性报错
- C. 都匹配 int 版
- D. 都匹配 double 版
答案与解析
答案:A
3.5 是 double 字面值,精确匹配 g(double);'a' 是 char,char→int 是整型提升、char→double 是转换,提升的优先级更高,编译器选 g(int)。只有两条路径同级才谈得上二义性(例如同时有 f(int) 与 f(long) 时调 f(3.5):double→int 与 double→long 都是转换,才会报二义性)。
练习 17 · 默认参数的位置¶
题目
下列默认参数写法合法的是( )
- A.
int f(int a = 1, int b) - B.
int f(int a, int b = 2) - C.
int f(int a = 1, int b = 2, int c) - D.
int f(int a, int b, int c = 3, int d)
答案与解析
答案:B
默认参数必须从右往左连续:一旦某个参数有默认值,它右边所有参数都得有。
练习 18 · 默认参数的调用¶
题目
int f(int a, int b = 2) { return a * b; } 则 f(5) 的值( )
- A. 编译错误
- B. \(10\)
- C. \(5\)
- D. \(7\)
答案与解析
答案:B
缺的 b 用默认值 2:\(5\times2 = 10\)。
练习 19 · const 引用参数¶
题目
void f(const int& a) { a = 5; } 的说法正确的是( )
- A. 合法,修改实参
- B. 合法,修改副本
- C. 编译错误:const 引用是只读的
- D. 运行时崩溃
答案与解析
答案:C
const& 只读:既能避免拷贝(引用)、又禁止修改(const)——大对象传参的标准写法。
练习 20 · 传大对象¶
题目
结构体 Big 占 1 KB,函数只读取它,最优传参方式( )
- A.
void f(Big b) - B. 复制一份全局变量
- C.
void f(Big* b)且函数内改它 - D.
void f(const Big& b)
答案与解析
答案:D
值传递每次拷贝 1 KB;const& 零拷贝且防误改。
练习 21 · 用引用带出多个结果¶
题目
要让函数同时“返回”商和余数,典型写法( )
- A.
void divmod(int a, int b, int& q, int& r) - B. 返回一个 int,分两次调用
- C. 用全局变量并递归
- D. 函数只能返回一个值,做不到
答案与解析
答案:A
引用出参是标准做法:divmod(17, 5, q, r) 后 q=3, r=2。
练习 22 · 原型声明¶
题目
函数先使用后定义时,应在前面给出( )
- A. 完整定义
- B.
#include - C. 原型声明:
int add(int a, int b); - D.
typedef
答案与解析
答案:C
原型只写“返回类型 + 名字 + 参数列表”,分号结尾;让编译器知道怎么调用。
练习 23 · void 返回¶
题目
void f() { return; } 的说法正确的是( )
- A. 语法错误
- B. 返回 void 指针
- C. 返回 0
- D. 合法:裸 return 立即结束函数
答案与解析
答案:D
void 函数可以 return; 提前退出,但不能 return 值;。
练习 24 · 实参求值不影响结果(本例)¶
题目
int f(int a, int b) { return a - b; } 则 f(10, 4) 与 f(4, 10) 分别是( )
- A. \(6\) 和 \(-6\)
- B. \(6\) 和 \(6\)
- C. \(-6\) 和 \(6\)
- D. 都不确定
答案与解析
答案:A
参数按位置对应:第一个实参给 a。减法不交换,位置写反就变号。
练习 25 · 递归函数的参数¶
题目
f(4) 的值( )
- A. \(10\)
- B. \(16\)
- C. \(4\)
- D. \(24\)
答案与解析
答案:D
\(4! = 4\times3\times2\times1 = 24\):每层调用的 n 是各自的形参副本,互不干扰——这正是值传递让递归安全的原因。
练习 26 · 综合追踪¶
题目
- A. \(100, 200, 300\)
- B. \(1, 200, 300\)
- C. \(1, 2, 300\)
- D. \(100, 2, 300\)
答案与解析
答案:B
一参一性:a 是 x 的副本(x 不变);p 指向 y,*p = 200 改 y;r 是 z 的别名,改 z。
历年真题¶
2025 年 · 第 10 题¶
题目
考虑以下 C++ 函数:
在 \(main\) 函数调用 \(solve\) 后,\(x\) 和 \(y\) 的值分别是?( )
- A. \(5, 10\)
- B. \(10, 10\)
- C. \(10, 5\)
- D. \(5, 5\)
答案与解析
答案:B
逐行追踪(a 是 x 的别名,b 是 y 的副本):
| 行 | 语句 | a(=x) | b(副本) |
|---|---|---|---|
| 初值 | — | \(5\) | \(10\) |
| 2 | a = a + b |
\(15\) | \(10\) |
| 3 | b = a - b |
\(15\) | \(5\) |
| 4 | a = a - b |
\(10\) | \(5\) |
x = a = 10;y 从头到尾没被碰过,仍是 10。
干扰项思路:C 是“以为 b 也是引用、三行交换真的完成”的错觉——交换只发生在 a 与副本 b 之间,y 永远是 10。
易错小结¶
- 值传递改副本、引用/指针改本体;数组参数例外:退化为指针,天生能改原数组;
- 指针传参:调用给
&x,函数内*p才是本体;p = 9是改指向不是改值; - 仅返回类型不同不构成重载(编译错误);重载看参数个数与类型;
- 实参 char 传给
f(int)/f(double)双版本会二义性; - 默认参数从右往左连续,中间不能断;
- 递归每层的形参是独立副本——值传递保证互不干扰。