用于查找数字的阶乘的递归函数

Posted

技术标签:

【中文标题】用于查找数字的阶乘的递归函数【英文标题】:Recursive function for finding factorial of a number 【发布时间】:2021-09-02 13:56:45 【问题描述】:

我得到 24 的输出,这是 4 的阶乘,但我应该得到 5 阶乘的输出,即 120

#include <stdio.h>
int factorial(int number)
    if(number==1)
        return number;
    
    return number*factorial(--number);

int main()
    int a=factorial(5);
    printf("%d",a);

【问题讨论】:

6 4!和24 5!和120 6!你看到模式了吗? warning: operation on ‘number’ may be undefined [-Wsequence-point] 是的,但我想知道为什么我的代码是错误的 number*factorial(--number) 中,first number 使用的值是多少? number 在前一个序列点和--number 之间的某个时间递减。但没有人知道确切的时间。 为什么number--number 执行之前就被--number 取代了 【参考方案1】:

您的程序存在未定义行为

在第一次致电factorial(5) 时,您有

return number * factorial(--number);

你想象这是要计算的

       5      * factorial(4);

但这并不能保证! 如果编译器以不同的顺序查看它会怎样? 如果先在右侧工作会怎样? 如果它首先做相当于:

temporary_result = factorial(--number);

然后做乘法:

return number * temporary_result;

如果编译器按此顺序执行,则temporary_result 将是factorial(4),它会返回 4 倍,而不是 5!。基本上,如果编译器按照这个顺序执行它——它可能会! -- 然后number 会“过早”递减。

您可能没有想到编译器可以这样做。 您可能已经想到,表达式总是“从左到右解析”。 但那些想象是不正确的。 (有关评估顺序的更多讨论,另请参阅this answer。)

我说过表达式会导致“未定义的行为”,而这个表达式就是一个经典的例子。使这个表达式未定义的原因是它内部发生了太多事情。

表达式的问题

return number * factorial(--number);

是变量number 在其中使用了它的值,并且同一变量number 也在其中被修改。这种模式基本上是毒药。

让我们标记number出现的两个位置,以便我们可以非常清楚地谈论它们:

return number * factorial(--number);
       /* A */             /* B */

在点 A,我们获取变量 number 的值。 在点 B,我们修改变量 number 的值。 但问题是,在点 A,我们得到 number 的“旧”值还是“新”值? 我们是在 Spot B 修改之前还是之后得到它?

正如我已经说过的,答案是:我们不知道。 C 中没有规则可以告诉我们。

同样,您可能认为有一条关于从左到右求值的规则,但实际上并没有。因为没有规定应该如何解析这样的表达式,所以编译器可以做任何它想做的事情。它可以以“正确”的方式或“错误”的方式解析它,或者它可以做一些更奇怪和意想不到的事情。 (而且,实际上,首先没有“正确”或“错误”的方式来解析像这样的未定义表达式。)

解决这个问题的方法是:不要那样做! 不要编写同时使用和修改一个变量(如number)的表达式。 在这种情况下,正如您已经发现的那样,有一个简单的解决方法:

return number * factorial(number - 1);

现在,我们实际上并没有尝试修改变量number 的值(就像表达式--number 所做的那样),我们只是在将较小的值传递给递归调用之前从中减去1。 所以现在,我们没有违反规则,我们没有在同一个表达式中使用和修改number。 我们只是使用了它的值两次,这很好。

有关此类表达式中未定义行为的更多(更多!)主题,请参阅Why are these constructs using pre and post-increment undefined behavior?

【讨论】:

但是如果我们使用 number - 1 而不是 --number 就可以了 @IcanCode number - 1 不会更改 number 的值,但 --number 会。 我正在使用 vscode 但我看不到任何警告或未定义的行为警报 @ash54321 不幸的是,关于未定义行为的明确警告非常罕见。你必须知道自己避免它。编译器不会警告你——事实上在某些情况下它不能警告你。 @jo-art 未定义。请参阅linked question 的引用。

以上是关于用于查找数字的阶乘的递归函数的主要内容,如果未能解决你的问题,请参考以下文章

Python3基础 用 函数递归求解 一个数字的阶乘

递归检测数字是不是为阶乘

递归实例详解

递归函数和二分查找

JS函数递归

python递归函数(10)