C++ 编程语法

八、函数

函数是将特定功能的代码打包封装、起名复用的机制。好的函数让程序结构清晰,有效避免重复代码。本章还将介绍递归这一强大技术。

函数的定义

函数是对实现某一功能的代码进行模块化封装。定义格式如下:

int   Add ( int a, int b { return a + b; }
返回值类型 — 函数返回什么类型的数据
函数名 — 调用时使用的名字
参数列表 — 传入的数据(类型 + 名称)
函数体 — 具体执行的代码
组成部分说明是否必须
返回值类型函数执行完毕后返回的数据类型,如 intdoublestring✅ 必需(无返回值写 void
函数名调用函数时使用的名字,命名规则同变量名✅ 必需
参数列表调用时传入的数据,需指定类型和名称,多个参数用逗号分隔可选(无参数时留空)
函数体花括号 {} 内要执行的具体代码✅ 必需
return 语句返回计算结果并结束函数;返回值类型为 void 时可省略视返回类型而定
C++ · 函数定义示例
1// 示例1:计算两数之和(有返回值)
2int Add(int a, int b) // 返回类型 int,两个 int 参数
3{
4 int result = a + b;
5 return result; // 返回计算结果
6}
7
8// 示例2:打印问候语(无返回值)
9void Greet(string name) // 返回类型 void,表示不返回值
10{
11 cout << "Hello, " << name << "!" << endl;
12 // 无需 return
13}
💡
命名建议:函数名应见名知义,用动词或动词短语描述其功能(如 AddGetMaxPrintResult)。多个参数即使类型相同也不能省略类型声明,必须写 int a, int b,不能写 int a, b

函数的调用

调用函数就是使用函数的功能来执行代码或获取返回值。在函数名后跟圆括号,括号内传入实参,多个参数用逗号分隔。

main() → 调用 → Add(3, 5) → 返回 → 8 // 用变量接收:int sum = Add(3, 5);
main() → 调用 → Add(3, 5) * 2 → 直接用于表达式 → 16
main() → 调用 → Greet("小明") // void 函数:作为独立语句调用

形参与实参

形参形式参数
函数定义时括号内的参数,规定调用者需要传入什么类型的数据。只是一个"占位符",没有实际的值。
实参实际参数
函数调用时实际传入的具体值或变量。实参的数量、顺序、类型必须与形参一一对应。
C++ · 形参 vs 实参
1int Add(int a, int b) // a、b 是形参(定义处)
2{ return a + b; }
3
4int main()
5{
6 int x = 3, y = 5;
7 int sum = Add(x, y); // x、y 是实参(变量)
8 Add(10, 20); // 10、20 也是实参(字面量)
9 return 0;
10}

函数的声明

C++ 要求函数必须先定义(或声明)后调用。如果函数定义在 main() 之后,需要在前面写函数声明(也叫函数原型),提前告诉编译器这个函数的存在。

概念格式作用
函数声明(原型)返回类型 函数名(参数列表);告诉编译器函数"长什么样",没有具体实现代码
函数定义(实现)返回类型 函数名(参数列表) { 函数体 }给出函数的完整实现代码
C++ · 函数声明示例
1#include <iostream>
2using namespace std;
3
4int Add(int a, int b); // ← 函数声明(只写函数头,不写函数体)
5 // 参数名可省略:int Add(int, int);
6
7int main()
8{
9 cout << Add(3, 5) << endl; // 正常调用
10 return 0;
11}
12
13int Add(int a, int b) // ← 函数定义(在 main 之后也没问题)
14{
15 return a + b;
16}

函数参数详解

C++ 中函数参数的传递方式主要有两种:值传递引用传递,区别在于函数内修改参数是否影响原变量。

📋值传递(默认)
函数接收的是原变量的副本,函数内对参数的修改不影响原变量。
写法:普通参数 int x
❌ 不影响原变量
🔗引用传递
函数接收的是原变量的引用(别名),函数内修改参数就是修改原变量本身。
写法:加 & 符号 int &x
✅ 直接修改原变量
C++ · 值传递 vs 引用传递
1// 值传递:修改的是副本
2void TryChange(int x) // x 是副本
3{ x = 100; } // 只改了副本,原变量不变
4
5// 引用传递:直接修改原变量
6void RealChange(int &x) // & 表示引用,x 就是原变量
7{ x = 100; } // 修改原变量本身
8
9int main()
10{
11 int a = 5;
12 TryChange(a);
13 cout << a; // 输出:5(未改变)
14 RealChange(a);
15 cout << a; // 输出:100(已改变)
16 return 0;
17}

选择建议

使用场景推荐方式
只需要读取参数的值,不修改值传递,或 const 引用 避免大对象的拷贝开销
需要在函数内修改原变量引用传递 int &x
参数是大型结构体或 stringconst 引用传递 const string &s,避免拷贝开销

递归函数

递归是函数调用自身的技术。一个正确的递归函数必须具备两个要素:

① 递归边界(终止条件)
让递归能够停下来,防止无限递归。没有终止条件 = 程序崩溃。
② 递归推进
每次调用时让问题规模向边界靠近(如 n 减小),最终达到终止条件。
C++ · 递归示例(阶乘 & 求和)
1// 计算 n!(n 的阶乘)
2int Factorial(int n)
3{
4 if (n == 1) return 1; // ① 边界:1! = 1
5 return n * Factorial(n - 1); // ② 推进:n! = n × (n-1)!
6}
7
8// 计算 1+2+...+n
9int Sum(int n)
10{
11 if (n == 1) return 1; // ① 边界
12 return n + Sum(n - 1); // ② 推进:Sum(n) = n + Sum(n-1)
13}
14
15cout << Factorial(5); // 120
16cout << Sum(10); // 55

Factorial(4) 的调用栈展开

⬇ 递推阶段(逐层调用)
Factorial(4) = 4 × Factorial(3)
Factorial(3) = 3 × Factorial(2)
Factorial(2) = 2 × Factorial(1)
Factorial(1) = 1 ← 触底!边界条件
触底
回溯
⬆ 回归阶段(逐层返回)
返回 1 1! = 1
返回 2×1 = 2 2! = 2
返回 3×2 = 6 3! = 6
返回 4×6 = 24 4! = 24 ✓
⚠️
三条递归注意事项:
① 必须有明确的终止条件,否则无限递归会导致栈溢出(程序崩溃)。
② 递归层数不能太深。竞赛中若递归深度超过约 10⁵~10⁶ 层,需改用循环或手动模拟栈。
③ 递归代码简洁,但每次函数调用都有额外开销。大规模问题优先考虑循环迭代替代递归。