代码示例:1.3.3 函数与递归
全部示例均标注编译命令、适用环境与实测状态;完整验证记录见《03.5-代码编译运行验证记录》。
examples/ex1-swap-value-address.c · 传值 vs 传址对照(swap 成功与失败)
/* ex1-swap-value-address.c —— 传值 vs 传址对照(1.3.3) * 适用环境:Linux / Windows(MinGW) 标准:C11 * 编译:gcc ex1-swap-value-address.c -o ex1 -std=c11 -Wall -Wextra -Wpedantic * 运行:./ex1(Windows: ex1.exe) */ #include <stdio.h> static void swap_by_value(int a, int b) { int t = a; a = b; b = t; /* 只交换了形参副本 */ } static void swap_by_address(int *a, int *b) { int t = *a; *a = *b; *b = t; /* 通过地址间接修改调用者的变量 */ } int main(void) { int x = 10, y = 20; swap_by_value(x, y); printf("after swap_by_value: x=%d y=%d(未交换)\n", x, y); swap_by_address(&x, &y); printf("after swap_by_address: x=%d y=%d(已交换)\n", x, y); return 0; }examples/ex2-factorial.c · 阶乘:递归 vs 迭代对照(0..10 全等)
/* ex2-factorial.c —— 阶乘:递归 vs 迭代对照(1.3.3) * 适用环境:Linux / Windows(MinGW) 标准:C11 * 编译:gcc ex2-factorial.c -o ex2 -std=c11 -Wall -Wextra -Wpedantic * 运行:./ex2(Windows: ex2.exe) */ #include <stdio.h> static long long factorial_rec(int n) { if (n <= 1) return 1; /* 基准情形 */ return (long long)n * factorial_rec(n - 1); /* 递归步骤:规模递减 */ } static long long factorial_iter(int n) { long long r = 1; for (int i = 2; i <= n; i++) r *= i; return r; } int main(void) { for (int n = 0; n <= 10; n++) { printf("factorial(%2d): rec=%10lld iter=%10lld\n", n, factorial_rec(n), factorial_iter(n)); } return 0; }examples/ex3-hanoi.c · 汉诺塔(3 盘,7 步;2^n - 1 验证)
/* ex3-hanoi.c —— 汉诺塔(3 盘,1.3.3) * 适用环境:Linux / Windows(MinGW) 标准:C11 * 编译:gcc ex3-hanoi.c -o ex3 -std=c11 -Wall -Wextra -Wpedantic * 运行:./ex3(Windows: ex3.exe) */ #include <stdio.h> static int move_count = 0; static void hanoi(int n, char from, char via, char to) { if (n == 1) { /* 基准情形 */ printf("move disk 1: %c -> %c\n", from, to); move_count++; return; } hanoi(n - 1, from, to, via); /* 递归步骤 */ printf("move disk %d: %c -> %c\n", n, from, to); move_count++; hanoi(n - 1, via, from, to); } int main(void) { hanoi(3, 'A', 'B', 'C'); printf("total moves = %d(2^3 - 1 = 7)\n", move_count); return 0; }examples/ex4-infinite-recursion.txt · 无限递归与栈溢出(conceptual:静态讲解,不运行)
# 1.3.3 无限递归与栈溢出(conceptual 讲解,不编译不运行) # 本文件是"概念讲解型"示例(example_kind=conceptual):只讲机制,不运行无限递归。 # # 危险写法(只讲解,绝不执行): # static void bad(int n) { bad(n); } /* 无基准情形、参数不递减 */ # # 为什么必然栈溢出? # 1. 每次函数调用都在调用栈上分配一个栈帧(局部变量、返回地址、保存的寄存器等); # 2. 递归三要素缺一(无基准情形/参数不递减)时,调用永不返回,栈帧只增不减; # 3. 栈空间有限(Linux 默认 8MB;嵌入式任务栈通常仅 1-16KB),耗尽即 stack overflow, # 常见表现为段错误(SIGSEGV)或栈保护(stack smashing)崩溃。 # # 正确写法(三要素齐全): # static long long factorial_rec(int n) { # if (n <= 1) return 1; /* ① 基准情形 */ # return (long long)n * factorial_rec(n - 1); /* ② 递归步骤 ③ n-1 规模递减 */ # } # # 工程建议: # - 递归前先估算最大深度 × 每层栈帧大小,确认远小于可用栈; # - 嵌入式(栈小)或深度不可预估时改迭代(ex2 的 iter 版); # - 不依赖编译器尾调用优化(优化行为,标准不保证)。 # # 一句可背的结论:递归三要素缺一不可;栈溢出不是"报错"而是资源耗尽,防患于设计。