跳转至

函数

方法速览

任务 方法 口诀
判断实参是否被改 值传递拷贝改副本;引用/指针直达本体 值传拷、引用直达
数组作参数 退化为指针,sizeof 变指针大小,但能改原数组 数组进门变指针
重载判定 同名 + 参数个数或类型不同;仅返回值不同不算 重载看参数,不看返回
默认参数 右往左连续给出,调用从右往左省略 默认靠右

什么是参数列表

函数定义由返回类型、函数名、参数列表、函数体组成:

1
2
3
返回类型 函数名(参数列表) { 函数体 }
int add(int a, int b) { return a + b; }
//     ^^^^^^^^^^^^^ 参数列表:类型 + 形参名,逗号分隔
  • 形参:定义里占位的变量(ab),是函数自己的局部变量,函数结束就销毁;
  • 实参:调用时真正传进去的值(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] 直接改原数组

(三种改法对照):

1
2
3
4
5
6
7
8
void byVal(int a)  { a = 10; }   // 改副本
void byRef(int& a) { a = 10; }   // 改本体
void byPtr(int* a) { *a = 10; }  // 改本体(要解引用)

int x = 1;
byVal(x);  // x = 1(没变)
byRef(x);  // x = 10
byPtr(&x); // x = 10(调用时要取地址)

数组退化两点:

  • void f(int a[10]) 里的 10 被忽略,a 就是指针:sizeof(a) = 8(64 位);
  • 正因为传的是地址,a[i] = 5; 改的就是原数组——与值传递语义相反。

函数重载

重载:同一个函数名,参数列表不同(个数不同 / 类型不同),完成相似功能。

1
2
3
int  add(int a, int b)        { return a + b; }
double add(double a, double b) { return a + b; }   // 类型不同 → 合法重载
int    add(int a, int b, int c) { return a + b + c; } // 个数不同 → 合法重载

三条判定:

  • 仅返回类型不同不构成重载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 · 值传递

题目

void f(int a) { a = a + 1; }
int x = 5; f(x);
调用后 x 的值( )

  • A. \(5\)
  • B. \(6\)
  • C. \(0\)
  • D. 未定义
答案与解析

答案:A

值传递:形参 a 是副本,改副本不影响实参。

练习 2 · 引用传递

题目

void f(int& a) { a = a + 1; }
int x = 5; f(x);
调用后 x 的值( )

  • A. \(5\)
  • B. \(6\)
  • C. 编译错误
  • D. 未定义
答案与解析

答案:B

int& a:a 是 x 的别名,动 a 就是动 x。

练习 3 · 指针传递

题目

void f(int* a) { *a = *a + 1; }
int x = 5; f(&x);
调用后 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

指针参数要配取地址 &xp = 9 改的是指针本身(让 p 指向地址 9),必须 *p = 9

练习 5 · 失效的 swap

题目

void swap(int a, int b) { int t = a; a = b; b = t; }
int x = 1, y = 2; swap(x, y);
调用后 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 · 数组参数能改原数组

题目

void f(int a[]) { a[0] = 99; }
int arr[3] = {1, 2, 3}; f(arr);
调用后 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 · 混合传递追踪

题目

void f(int a, int& b) { a++; b++; }
int x = 1, y = 1; f(x, y);
调用后 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 · 递归函数的参数

题目

1
2
3
4
int f(int n) {
    if (n <= 1) return 1;
    return n * f(n - 1);
}
f(4) 的值( )

  • A. \(10\)
  • B. \(16\)
  • C. \(4\)
  • D. \(24\)
答案与解析

答案:D

\(4! = 4\times3\times2\times1 = 24\):每层调用的 n 是各自的形参副本,互不干扰——这正是值传递让递归安全的原因。

练习 26 · 综合追踪

题目

1
2
3
void h(int a, int* p, int& r) { a = 100; *p = 200; r = 300; }
int x = 1, y = 2, z = 3;
h(x, &y, z);
调用后 x、y、z 分别是( )

  • 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++ 函数:

1
2
3
4
5
6
7
8
9
 1  void solve(int &a, int b) {
 2      a = a + b;
 3      b = a - b;
 4      a = a - b;
 5  }
 6  int main() {
 7      int x = 5, y = 10;
 8      solve(x, y);
 9  }

\(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) 双版本会二义性
  • 默认参数从右往左连续,中间不能断;
  • 递归每层的形参是独立副本——值传递保证互不干扰。