5
函数:把活儿打包,自己调自己也行
Functions · Recursion · Storage Classes
函数就是"给一串操作起个名字,以后喊名字就行"。这一章讲函数怎么定义、变量在哪生在哪灭(作用域与存储类),以及 C 的招牌戏——递归(函数调用它自己)。
定义、声明与原型
C 要求"先声明,后使用"。如果函数定义写在 main 后面,就得在开头写一行"函数原型"提前打招呼。
函数原型 + 定义
#include <stdio.h>
int square(int n); // 函数原型:告诉编译器有这么个函数(打招呼)
int main() {
printf("%d\n", square(5)); // 25
return 0;
}
int square(int n) { // 真正的定义在后面
return n * n;
}
作用域与存储类:变量的"户籍"
| 关键字 | 含义(大白话) |
|---|---|
| 局部变量 | 函数里定义的,函数一结束就消失。最常用。 |
| 全局变量 | 函数外面定义的,全程序都能访问。能少用就少用,谁都能改容易乱。 |
static(局部) | 函数里的静态变量:第一次进函数时初始化一次,之后一直活着、记住上回的值。 |
static(全局) | 限制这个全局变量/函数只能被本文件用,别的文件看不见。 |
extern | 声明"这个变量在别的文件里定义了",跨文件共享时用。 |
register | 建议编译器把这个变量放进寄存器(最快)。现在基本不写,编译器比你聪明。 |
inline(C99) | 建议把函数体"原地展开",省掉函数调用开销。 |
static 局部变量:记住上一次的值
#include <stdio.h>
void count() {
static int n = 0; // 只在第一次进来时初始化一次,之后一直活着
n++;
printf("这是第 %d 次调用\n", n);
}
int main() {
count(); // 第 1 次
count(); // 第 2 次
count(); // 第 3 次
}
运行结果(普通局部变量每次都会归零,static 不会)
这是第 1 次调用
这是第 2 次调用
这是第 3 次调用
递归:函数调用它自己
递归就是"大事化小":算 n 的阶乘,先算 n-1 的阶乘再乘 n。关键是要有终止条件(base case),否则会无限调用下去直到栈崩。
阶乘 + 斐波那契
// 阶乘:5! = 5*4*3*2*1 = 120
long fact(int n) {
if (n <= 1) return 1; // 终止条件:这是"大事化小"的终点
return n * fact(n - 1); // 自己调自己
}
// 斐波那契:1,1,2,3,5,8,13...
int fib(int n) {
if (n <= 2) return 1;
return fib(n - 1) + fib(n - 2);
}
printf("%ld\n", fact(5)); // 120
printf("%d\n", fib(7)); // 13
递归的代价
递归虽然优雅,但每次调用都要在栈上压一层。斐波那契递归写起来爽,算到 40 就明显变慢(重复算了无数次),数字再大就栈溢出直接崩。工程上能用循环就别硬递归,除非问题本身就是树/分治这种递归结构(比如后面的二叉树遍历)。
经典递归:汉诺塔(n 个盘子从 A 移到 C)
// 把 n 个盘子,从 from 借助 via,移到 to
void hanoi(int n, char from, char via, char to) {
if (n == 1) { // 终止条件:只剩一个,直接搬
printf("%c -> %c\n", from, to);
return;
}
hanoi(n - 1, from, to, via); // ① 先把上面 n-1 个,从 from 搬到 via
printf("%c -> %c\n", from, to); // ② 把最底下那个,从 from 搬到 to
hanoi(n - 1, via, from, to); // ③ 再把 n-1 个,从 via 搬到 to
}
// hanoi(3, 'A', 'B', 'C'); // 三个盘子,7 步搬完
运行结果(n=3)
A -> C
A -> B
C -> B
A -> C
B -> A
B -> C
A -> C
味递归的正确"味觉"
写递归别在脑子里一步步模拟(会晕),就信三件事:① 假设"搬 n-1 个"这个子问题已经能正确解决;② 你只负责把最难的那一步(搬最底下那个)做了;③ 定好终止条件。能这样想清楚,递归就不再是玄学。汉诺塔、目录树遍历、表达式求值,都是这个套路。