递归实现阶乘的栈溢出风险在于调用深度等于n,每层递归需压入返回地址、局部变量等,当n超过几百(如1000)时易耗尽默认1–8MB栈空间,引发Segmentation fault或stack overflow。
递归写 factorial(n) 看似简洁,但实际调用深度等于 n。当 n 超过几百(比如 n = 1000),多数编译器默认栈空间(通常 1–8 MB)就会被压垮,触发 Segmentation fault 或 stack overflow 异常。
关键不是“能不能算”,而是“谁来承担调用开销”。每层递归都得压入返回地址、局部变量、寄存器备份——这些加起来远超一个 int 占用。
n 使用纯递归if (n > 1000) throw std::runtime_error("n too large for recursion");
-O2)对简单阶乘无效,因为 return n * factorial(n-1) 不是尾调用(乘法在 return 前)用 for 循环算阶乘更可控,但 int 或 long long 很快就爆掉:13! = 6227020800 已超出 int(通常 32 位),21! 就超过 long long(64 位)最大值 9223372036854775807。
这意味着:即使你写对了循环逻辑,结果也大概率是错的——而且不会报错,只会静默溢出(未定义行为)。
unsigned long long,大值必须上 boost::multiprecision::cpp_int 或自己实现大数__builtin_mul_overflow(GCC/Clang)或手动比较:if (result > ULLONG_MAX / i) throw std::overflow_error("factorial overflow");
0! 和 1! 必须显式处理为 1,否则循环从 i=2 开始时会漏掉如果参数是字面量且不大(比如用于数组长度、模板参数),用 constexpr 递归模板能彻底避免运行时开销,并在编译时报错溢出。
templateconstexpr unsigned long long factorial() { static_assert(N < 21, "N too large for constexpr factorial"); return (N <= 1) ? 1 : N * factorial (); }
调用如 constexpr auto f5 = factorial();,编译器直接替换为 120;若写 factorial(),编译失败并提示 static_assert 错误。
int n; std::cin >> n; factorial(); 是非法的static_assert 检查的是模板实例化时的 N,不是运行时值,所以安全可靠
当需求明确要支持 n > 20 且结果需精确(比如密码学、组合数学验证),硬编码 unsigned long long 就是自欺欺人。此时应切换到真正能处理任意精度的方案。
最轻量的选择是 boost::multiprecision::cpp_int,它头文件即用,无链接依赖:
#includeusing namespace boost::multiprecision; cpp_int factorial(int n) { cpp_int result = 1; for (int i = 2; i <= n; ++i) result *= i; return result; }
cpp_int 运算比原生类型慢几个数量级,只在必要时启用 和未来可能的 std::bigint(尚未标准化)真正麻烦的从来不是“怎么写阶乘”,而是想清楚:你要的是编译期常量、运行时小值快速计算,还是任意精度下的正确性——选错路径,后面全是补丁。