楼层: 首页/ 软件技术/ C 语言/ 函数:把活儿打包,自己调自己也行
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 个"这个子问题已经能正确解决;② 你只负责把最难的那一步(搬最底下那个)做了;③ 定好终止条件。能这样想清楚,递归就不再是玄学。汉诺塔、目录树遍历、表达式求值,都是这个套路。