递归函数必须有明确终止条件,否则会无限调用导致栈溢出;C++ 不检查递归深度,需程序员控制;常见错误是终止条件遗漏 n≤0 的情况。

递归函数必须有明确的终止条件
没有终止条件的递归会无限调用自身,最终导致栈溢出(stack overflow),程序崩溃。C++ 不做递归深度检查,全靠程序员自己控制。
- 常见错误是把终止条件写成
if (n == 1)却忘了处理n 的情况,比如传入负数或 0 时直接跳过终止逻辑 - 递归深度受栈空间限制,通常默认栈大小在 1MB–8MB 之间,深度超过几千层就容易崩,别指望它处理
n = 100000的阶乘 - 终止条件要放在递归调用前,且逻辑必须覆盖所有可能输入路径,例如计算斐波那契时,
fib(0)和fib(1)都得单独返回,不能只写if (n == 1)
递归调用要朝终止条件收敛
每次递归调用的参数必须更接近终止条件,否则即使写了终止判断,也永远进不去——比如用 n + 1 代替 n - 1,那就不是“递”而是“增”,根本停不下来。
- 阶乘函数里,
factorial(n)必须调用factorial(n - 1),而不是factorial(n)或factorial(n / 2)(后者虽收敛但逻辑错) - 二分查找递归版中,若不更新
left或right就直接重调,区间不缩小,等于白写递归 - 注意整数除法和边界:比如
mid = (left + right) / 2在大数时可能溢出,应改用left + (right - left) / 2,但这不是递归专属问题,而是数值安全习惯
避免重复计算,必要时加记忆化
朴素递归如 fib(n) 时间复杂度是指数级 O(2^n),因为同一子问题(如 fib(3))被反复算几十次。C++ 没内置记忆化,得手动缓存。
- 用
std::unordered_map或std::vector存已算结果,首次计算后存入,后续直接查表 - 注意缓存生命周期:局部
static变量可跨递归调用共享,但多线程下不安全;更稳妥的是把缓存作为参数传入或封装进类 - 不是所有递归都适合记忆化——比如树遍历、DFS 路径搜索,状态本身就不重叠,加哈希反而拖慢
尾递归在 C++ 中基本没用
C++ 标准不强制要求编译器优化尾递归,GCC/Clang 在 -O2 下可能优化简单情形,但一旦有临时对象析构、异常处理或内联失败,优化就失效。别依赖它来防栈溢出。
立即学习“C++免费学习笔记(深入)”;
- 写成尾递归形式(把计算逻辑移到参数里,最后一步只是调用自身)只是风格变化,不保证栈空间节省
- 想真正规避深递归?改迭代:用
std::stack模拟调用栈,或重写为循环+状态变量 - 有些场景(如解析嵌套括号、AST 遍历)天然适合递归,这时优先保证逻辑清晰,再考虑是否需要手动转栈
递归看起来简洁,但每层调用都在压栈,变量作用域、对象构造/析构顺序、异常传播路径都会比迭代复杂得多。写完一定要用小数据测终止,再用稍大一点的数据看是否卡顿或崩溃。


















