避免栈溢出
限制递归的深度。
避免栈溢出 是 CoddyKit 上的免费 C Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C Academy 课程共包含 4 节课。
什么是栈溢出
调用栈的大小是有限的。每次函数调用都会为参数和局部变量占用其中一部分空间。
如果递归过深,栈就会被填满,程序会因栈溢出而崩溃。
缺少基本情况
最常见的原因是基本情况永远无法达到。这会导致无限循环,最终栈溢出。
不要运行这类函数;请研究它失败的原因。
int broken(int n) {
/* no base case: never stops */
return broken(n + 1);
}参数没有缩小
即使存在基本情况,参数也必须朝着它变化。在这里 n 不断增大,因此永远无法达到 0。
请始终检查每次调用是否都更接近停止条件。
int oops(int n) {
if (n == 0) return 0;
return oops(n + 1); /* wrong direction */
}正确的版本
修正变化方向后,函数就会终止。现在 n 会递减并趋向基本情况 0。
#include <stdio.h>
int good(int n) {
if (n == 0) return 0;
return n + good(n - 1);
}
int main(void) {
printf("%d\n", good(10));
return 0;
}深度限制确实存在
即使递归逻辑正确,递归过深也可能导致栈溢出。调用一个数百万层深的函数可能超过栈的容量,而栈通常只有几兆字节。
对于非常大的深度,请优先使用迭代。
将深度递归转换为循环
如果递归深度会随着输入规模增长,请改用循环。这样可以避免堆叠数千个栈帧。
下面的循环使用固定大小的内存,安全地计算从 1 加到很大的 n。
#include <stdio.h>
int main(void) {
long total = 0;
for (int i = 1; i <= 1000000; i++)
total += i;
printf("%ld\n", total);
return 0;
}使用分治减少深度
将工作量分成两半可以保持较小的深度。通过不断二分范围来求和时,深度的增长速度类似于规模的对数,而不是线性增长。
long range_sum(int lo, int hi) {
if (lo == hi) return lo;
int mid = (lo + hi) / 2;
return range_sum(lo, mid) + range_sum(mid + 1, hi);
}注意大型局部数组
Big 局部变量会让每个栈帧都更 heavy,因此栈会更快填满。
请避免在递归函数内部声明大型数组;改为传递指针,或使用堆。
void heavy(int n) {
int buffer[10000]; /* big frame each call */
if (n == 0) return;
heavy(n - 1);
}使用累加器
将当前总和作为累加器传递,可以使每个栈帧保持较小,并让递归呈现尾递归结构。
这样一来,某些编译器就可以重用同一个栈帧。
#include <stdio.h>
long sum_acc(int n, long acc) {
if (n == 0) return acc;
return sum_acc(n - 1, acc + n);
}
int main(void) {
printf("%ld\n", sum_acc(100, 0));
return 0;
}安全检查清单
在信任一个递归函数之前,请检查:
1. 是否存在基本情况?
2. 每次调用是否都会朝着基本情况变化?
3. 对于大规模输入,深度是否可能非常大?
如果深度可能急剧增长,请改用循环。
使用小规模输入进行测试
请始终先使用可以手动验证的极小输入测试递归。
如果小规模情况能够正常工作,并且深度保持有界,您就可以放心扩大规模。
快速检查
找出最安全的修复方法。
回顾
递归过深或永不停止时,就会发生栈溢出。请始终提供一个可以达到的基本情况,让参数在每次调用中缩小,保持栈帧轻量,并在深度可能随输入规模增长时切换到迭代。
常见问题解答
「避免栈溢出」课时是免费的吗?
是的 — 「避免栈溢出」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C Academy 课程的其余内容,请升级到 CoddyKit PRO。 C Academy 课程共包含 4 节课。
「避免栈溢出」这节课中我会学到什么?
限制递归的深度。 你通过在浏览器中直接运行的动手代码来练习 C Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「避免栈溢出」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C Academy 课中编写并运行代码吗?
能。每节 C Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。