C 语言学习:递归、宏定义、预处理、汉诺塔、Fibonacci 等
1. 函数递归
什么是递归?
函数调用自己本身的行为称为递归。
递归本质上也是一种循环,但实现方式不同,特点是:
✔ 优点
- 逻辑清晰、代码简短
- 适合解决回溯、树形结构、数学归纳类问题(如汉诺塔、斐波那契)
❗ 必须满足两个条件
- 必须要有结束条件,否则会导致无限递归 → 栈溢出 Segmentation fault
- 避免递归层次过深
2. 字符串打印与 strlen()
通过递归前先学习字符串处理:
c
void show_str(char a[])
{
int len = strlen(a); // 字符串真实长度(不含'\0')
for(int i=0; i<len; i++)
{
putchar(a[i]);
}
printf("\nstrlen %lu\n", strlen(a));
}
注意事项:
sizeof(str)得到的是数组容量,例如char str[100]→ 100 字节strlen(str)得到的是字符串长度,例如"hello"→ 5
这是 C 语言中 字符串与字符数组的关键区别。
3. 闰年判断函数
闰年规则:
- 能被 4 整除但不能被 100 整除
- 或能被 400 整除
c
int isLeapYear(int year)
{
return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
}
用于遍历打印 1900 ~ 2024 之间的所有闰年:
c
for(int i=1900; i<2025; i++)
{
if(isLeapYear(i))
printf("year is %d\n", i);
}
4. 递归的典型应用
4.1 阶乘计算
c
int fun(int n)
{
if(n == 1) return 1;
return n * fun(n - 1);
}
递归非常适合数学归纳结构的函数表达式。
4.2 汉诺塔问题
问题描述 :
三根柱子 A、B、C,将 n 个盘从 A 移动到 C,每次只能移动一个盘,大盘不能压在小盘上。
c
void haonuota(int n, char src, char tmp, char dst)
{
if (n == 1)
{
printf("%c -> %c\n", src, dst);
}
else
{
haonuota(n - 1, src, dst, tmp);
printf("%c -> %c\n", src, dst);
haonuota(n - 1, tmp, src, dst);
}
}
汉诺塔体现了递归的 "逆向思维":
- 先把 n-1 个盘移动到中间柱子
- 把最大盘移动到目标柱子
- 再把 n-1 个盘移到目标柱子
4.3 Fibonacci 数列
递归版:
c
int fib(int n)
{
if(n == 1 || n == 2)
return 1;
return fib(n-1) + fib(n-2);
}
但这种写法效率非常低(指数复杂度)。
推荐:循环版 Fibonacci(性能好)
c
int fib_loop(int n)
{
if (n == 1 || n == 2)
return 1;
int a = 1, b = 1, c = 0;
for(int i = 3; i <= n; i++)
{
c = a + b;
a = b;
b = c;
}
return c;
}
5. 宏定义(
宏属于预处理阶段,特点是:
- 代码替换,不会执行运算
- 无类型检查
- 效率高,没有函数调用开销
示例:
c
#define PI 3.14
#define GETMAX(x,y) ((x)>(y)?(x):(y))
#define M 50
#define N (M+M)
带参宏建议:
所有参数加括号避免优先级错误,例如:
c
#define GETMAX(x,y) ((x)>(y)?(x):(y))
5.1 多行宏
c
#define PRINT(a,b,c) do{ \
printf("%d\n", a); \
printf("%d\n", b); \
printf("%d\n", c); \
}while(0)
6. C 语言预处理
编译步骤:
- 预处理 (处理
#include,#define,宏展开) - 编译(变成汇编)
- 汇编(生成 .o 目标文件)
- 链接(生成最终 a.out 或可执行程序)
7. 头文件
#include <...>:从系统目录中查找,比如 /usr/include
#include "...":先从当前目录查找,再去系统目录查找
头文件一般包含:
- 宏定义
- typedef
- 函数声明
- extern 声明
防止头文件被重复包含
c
#ifndef _FUNC_H_
#define _FUNC_H_
extern int add(int a, int b);
#endif
8. 条件编译
常见语法:
c
#if
#elif
#else
#endif
判断宏是否存在:
c
#ifdef DEBUG
#endif
#ifndef DEBUG
#endif
非常适合调试或不同平台切换。