返回如何在Factorial中完全正常工作?

时间:2015-09-27 09:43:00

标签: c recursion

自从我在过去挣扎以来,我已经从基础知识中获得了关于递归的信息。我正在看这个Factorial解决方案:

int Factorial(int n)
{
    if (n == 0)
    {
        return 1;
    }
    return n * Factorial(n - 1);
}

因此该函数一直调用自己直到n == 0。然后它继续在每次调用时返回值。现在这是我不明白的:当它从基本条件返回时,它返回一个值,并且它不断添加每个调用的值。

这些值存储在哪里最终能够返回总和?

3 个答案:

答案 0 :(得分:3)

正如您正确解释的那样,函数Factorial会调用自身,除非参数为0。由于它使用n - 1调用自身,最终递归调用序列将在n步之后停止。每次调用都是函数的新实例调用,计算暂停直到新实例返回。给定实例的状态保存在称为堆栈的区域的内存中(在大多数当前环境中)。

当使用值0调用的最终实例返回时,最后一个调用实例可以完成其计算并将1 * 1返回给其调用者,依此类推,直到初始实例可以完成其乘法并返回其参数的阶乘。

-------除非您对实施细节感兴趣,否则请在此处阅读--------

上面代码的第一个问题,如果n为负或非常大,程序可能会调用未定义的行为,因为递归调用太多,每个都占用一些堆栈空间,资源有限。

第二个问题:int类型的可能值范围有限。在大多数临时系统中,int存储为32位。因子增长非常快,因此Factorial(12)产生479001600,当乘以13时会导致算术溢出,因为结果6227020800不能适合32位。算术溢出调用未定义的行为。

您可以尝试在此网站上编译Factorial功能:http://gcc.godbolt.org/#。它有一个交互式编译器并显示汇编代码。尝试不同的优化器选项:

  • -m32 -O1将提供一个递归版本,不太难理解
  • -m32 -O2表明编译器足够精明,可以将递归C实现转换为迭代汇编版本,比-O1版本更短,更快。
  • -m32 -O3会产生更复杂的代码,涉及MMX寄存器上的SIMD指令......可能比-O2版本更快,但完全过度设计,因为一个简单的小查找表没有单次测试就足够了:

    int Factorial(int n) {
        static int f32[16] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320,
                               362880, 3628800, 39916800, 479001600,
                               0, 0, 0 };
        return f32[n & 15];
    }
    

注意如何利用未定义的行为语义(任何事情)来简化实现并删除测试。允许编译器这样做,其中一些确实这样做。

答案 1 :(得分:0)

  

以递归方式递归计算给定输入的结果   函数调用(副本)本身具有不同的("较小的"在某些中   方式)输入并使用此调用的结果来构造其结果。   除非基本情况已经发生,否则递归调用会做同样的事情   到达。因此,在该过程中开发了调用栈。例如,到   计算因子(3),这递归调用因子(2),因子(1),因子(0)   ("清理"堆栈),此时递归终止于   因子(0)= 1,然后堆栈以相反的顺序展开并且   结果是在沿着调用栈返回的路上计算的   初始调用框架因子(3),其中最终结果计算为3 * 2   =:6并最终返回。在此示例中,函数返回单个值。

答案 2 :(得分:0)

快速回答:

  

所有内容都存储在相应激活记录 1 的堆栈中。

下面是实现factorial的递归函数的函数调用(在左侧)和堆栈(在右侧)的描述: enter image description here

您的函数int Factorial(int n);正在使用递归。第一个返回称为基本情况,它充当终止条件,第二个返回是递归步骤,这导致调用修改了与其中一个参数(在基本情况if 条件语句中使用)相同的函数。累积结果并在达到基本条件后返回。

Here是递归的绝佳解释,使用 factorial 作为示例。

1。在计算机体系结构级别,结果存储在特定的寄存器中,累积最终结果。