我使用尾递归编写factorial,我在这里有一个问题。我原来的功能看起来像这样
代码段A
#include <stdio.h>
int main(void)
{
int n = 0;
printf("Enter number to find factorial of : ");
scanf("%d",&n);
printf("fact == %d\n",fun(n,1));
return 0;
}
int fun(int n, int sofar)
{
int ret = 0;
if(n == 0)
return sofar;
ret = fun(n-1,sofar*n);
return ret;
}
但是,即使我不使用return,它仍然有效。这不是很有意义,因为我只在基本情况下返回值。假如n == 5,那么将在基本情况下返回120。但是,从第4次调用返回到第3次调用返回的内容无法预测,因为我们没有明确指定任何返回,这与代码片段A不同。
代码段B
int fun(int n, int sofar)
{
int ret = 0;
if(n == 0)
return sofar;
ret = fun(n-1,sofar*n);
}
我认为上述工作是因为某种编译器优化?因为如果我将一个printf语句添加到代码片段B,它就不再起作用了。
代码段C
int fun(int n, int sofar)
{
int ret = 0;
if(n == 0)
return sofar;
ret = fun(n-1,sofar*n);
printf("now it should not work\n");
}
可能printf会导致从堆栈中删除某些内容?请帮我理解这一点。
答案 0 :(得分:3)
不返回应该返回值的函数的值为undefined behavior。
如果运气好的话,那么它不是优化,而只是存储这些自动分配值的方式和位置的巧合。
答案 1 :(得分:2)
有人可以推测这可行的原因,但没有办法给出明确的答案:在没有return
语句的情况下到达值返回函数的末尾并且使用返回值是未定义的行为,有无优化。
这个&#34;工作的原因&#34;在您的编译器中,编译器使用的返回机制恰好在到达函数结束时具有正确的值。例如,如果您的编译器在代码中的最后一次计算所使用的同一寄存器中返回整数(即ret = fun(n-1,sofar*n)
),那么正确的值将被意外加载到返回寄存器中,掩盖未定义的行为。 / p>
答案 2 :(得分:1)
它的工作原理是因为返回值几乎总是存储在特定的CPU寄存器中(x86为eax
)。这意味着如果您没有显式返回值,则不会显式设置返回寄存器。因此,它的值可以是任何值,但它通常是最后一个被调用函数的返回值。因此,使用myfunc()
结束函数几乎可以保证与return myfunc();
具有相同的行为(但它仍然是未定义的行为)。
答案 3 :(得分:1)
以下是'某事'的计算原因。
对printf()的调用有一个返回值(很少使用)打印的字符数(包括制表符,换行符等)
在“C”代码段中,printf()返回23。
'int'返回的值总是在同一个寄存器中返回。
printf()设置注册为23。
因此,返回一些内容,没有任何内容从堆栈中删除。
答案 4 :(得分:0)
它似乎在B中工作的原因是因为返回值很可能在您的体系结构的寄存器中传递。因此,返回所有递归层(或者如果编译器将整个事物优化为迭代),没有任何东西触及该寄存器,并且您的代码似乎有效。
不同的编译器可能不允许这种情况发生,或者编译器的下一个版本可能会以不同方式对其进行优化。事实上,编译器可以删除大部分函数,因为它允许假设由于未定义的行为不会发生,它必须意味着将永远不会达到未定义行为似乎发生的函数部分,并且可以安全地删除。