递归函数必须有明确终止条件,否则会导致栈溢出;以阶乘为例,必须定义0!=1或1!=1作为边界,错误写法如if(n==0)return 0会引发逻辑错误和无限递归。

递归函数必须有明确的终止条件
没有终止条件的递归会无限调用自身,最终导致栈溢出(Segmentation fault 或 stack overflow)。C++ 中每次函数调用都会在栈上压入帧,递归深度过大时栈空间耗尽。
以阶乘为例:n! = n × (n−1)!,数学定义本身是递归的,但必须约定 0! = 1 或 1! = 1 作为边界。这个边界就是终止条件,**不能省略,也不能写错**。
- 错误写法:
if (n == 0) return 0;→ 导致0!算成 0,且1!会进入n==1分支继续递归(若没覆盖) - 正确写法:
if (n (兼容n=0和n=1) - 注意:输入为负数时未定义,建议加校验,如
if (n
阶乘递归实现要小心整型溢出
int 类型在大多数平台最多表示到 12!(479001600),13! 就溢出;long long 也只撑到 20!(约 2.4e18)。递归本身不解决数值范围问题,只是逻辑表达方式。
示例代码:
立即学习“C++免费学习笔记(深入)”;
long long factorial(int n) {
if (n < 0) throw std::invalid_argument("n must be non-negative");
if (n <= 1) return 1;
return n * factorial(n - 1);
}- 返回类型用
long long比int更安全(但仍有限) - 参数用
int即可,因为超过 20 的输入已超出long long表达能力 - 不要试图用
unsigned int规避负数——它会让n-1在n==0时变成极大正数,直接进入死递归
递归 vs 迭代:不是所有场景都该用递归
阶乘看起来天然适合递归,但实际工程中更倾向迭代——无栈开销、无深度限制、更容易优化。
递归真正有价值的地方是结构天然分治的问题:比如树遍历、快速排序的划分逻辑、表达式解析等。对阶乘这种线性可展平的操作,递归只是教学示例。
- 迭代版阶乘更鲁棒:
for (int i = 2; i - 编译器通常无法对递归阶乘做尾调用优化(即使写成尾递归形式),因为乘法操作在递归调用之后,不是严格尾调用
- 调试递归函数时,调用栈深,单步跟踪成本高;而循环变量状态一目了然
写递归函数前先手写几层展开
别急着敲代码。拿纸笔模拟 factorial(4) 的展开过程:4 * factorial(3) → 4 * 3 * factorial(2) → 4 * 3 * 2 * factorial(1) → 4 * 3 * 2 * 1。确认每一步参数如何变化、何时触达 base case。
- 这能帮你发现参数递减方向是否正确(比如误写成
factorial(n + 1)) - 能验证终止条件是否覆盖所有可能入口(例如传入
0、1、2是否都停得下来) - 对复杂递归(如汉诺塔、DFS 回溯),这步省不得——逻辑错一点,整个调用链就崩了
递归的简洁性容易掩盖其内在代价。哪怕是最简单的阶乘,也要意识到:每次调用都在消耗栈空间、每次返回都要回溯计算、每个中间结果都依赖上一层的栈帧存活。这些不是理论风险,是真实跑起来就会撞上的墙。











