在函数入口加 if ($n
直接补:在函数入口加
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 参数、数据库读出的脏数据都可能绕过前端校验
补完后还要防什么?
光堵负数不够,还得防整数溢出和深度失控。
- 大数如
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),一样栈爆。每处递归调用,都是一个需要独立验证的“小入口”。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!












