在函数入口加 if ($n < 0) return 0; 可防止负数输入导致的错误,确保递归或计算逻辑只处理合法非负整数。

直接补:在函数入口加 if ($n 或抛异常,不能只靠 <code>$n == 0 或 $n 。
为什么 $n 不足以覆盖负数?
看似合理,但若传入 $n = -5,if ($n 为 true,直接返回 1 —— 这不是“终止”,是错误返回。阶乘数学上未定义负数,但代码没拒绝,反而继续算 <code>-5 * factorial(-6),触发无限递归。
- 终止条件必须区分「该停」和「不该算」:负数属于非法输入,不是基准情形
-
$n == 0或$n 是为处理合法边界(0! = 1, 1! = 1),不是兜底校验 - PHP 不会自动拦截负数参数,全靠你手动挡在递归开始前
怎么写才安全?两个位置必须检查
入口校验 + 递归调用前再确认,双保险。
- 函数第一行就做类型和范围检查:
if (!is_int($n) || $n - 每次递归调用前,确保参数已收缩:
return $n * factorial($n - 1);这行没问题,但前提是上层已保证$n >= 0;如果中间有分支计算了新值(比如$next = $n - $offset),就得在调用前再判一次if ($next - 不要依赖“用户不会传负数”——CLI 脚本、API 参数、数据库读出的脏数据都可能绕过前端校验
补完后还要防什么?
光堵负数不够,还得防整数溢出和深度失控。
立即学习“PHP免费学习笔记(深入)”;
- 大数如
factorial(10000)会卡死或超时,建议加深度限制参数:function factorial($n, $depth = 0) { if ($depth > 200) { throw new RuntimeException('Recursion too deep'); } - PHP 整数溢出不报错,
999999999999999999 * ...可能变 float 再变 INF,结果失真但无提示 - 用
error_log()记日志时别打完整参数(如大数组),只记关键标识:error_log("factorial({$n}) at depth {$depth}");
负数只是最典型的漏判点,真正难的是所有分支路径都收敛到合法终止态——比如树遍历里某个子节点为空,你忘了判 if (!$node) 就直接 traverse($node->left),一样栈爆。每处递归调用,都是一个需要独立验证的“小入口”。



















