int Factorial(int n)
{
if (n == 0)
{
return 1;
}
return n * Factorial(n - 1);
}
因此该函数一直调用自己直到n == 0
。然后它继续在每次调用时返回值。现在这是我不明白的:当它从基本条件返回时,它返回一个值,并且它不断添加每个调用的值。
这些值存储在哪里最终能够返回总和?
答案 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的递归函数的函数调用(在左侧)和堆栈(在右侧)的描述:
您的函数int Factorial(int n);
正在使用递归。第一个返回称为基本情况,它充当终止条件,第二个返回是递归步骤,这导致调用修改了与其中一个参数(在基本情况if
条件语句中使用)相同的函数。累积结果并在达到基本条件后返回。
Here是递归的绝佳解释,使用 factorial 作为示例。
1。在计算机体系结构级别,结果存储在特定的寄存器中,累积最终结果。