【问题标题】:is this compiler optimization in context of recursion?这是递归上下文中的编译器优化吗?
【发布时间】:2015-09-20 06:48:04
【问题描述】:

我正在使用尾递归编写阶乘,我在这里有一个问题。我原来的功能是这样的

代码 sn-p 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 次调用的返回值,因为我们没有像代码 sn-p A 中那样明确指定任何返回值。

代码 sn-p B

int fun(int n, int sofar)
{
 int ret = 0;

 if(n == 0)
  return sofar;

 ret = fun(n-1,sofar*n);
}

我认为上述工作是因为某种编译器优化?因为如果我在 Code sn -p B 中添加一个 printf 语句,它就不再起作用了。

代码 sn-p 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 导致从堆栈中删除某些内容?请帮助我理解这一点。

【问题讨论】:

  • 你想了解UB吗?不要打扰。
  • @deviantfan 考虑到 UB 在生产代码中非常常见(我听说估计超过 90% 的程序都包含 UB),我想说人们应该费心去理解 UB,了解它以知道如何避免和修复它是非常关键的。
  • @Art 了解问题所在/如何解决问题并了解为什么在 UB 情况下会发生一些奇怪的事情是一个很大的不同......
  • 发布的代码在编译时会引发严重警告,即缺少“fun()”的原型。建议将该原型放在 main() 函数之前。

标签: c++ c recursion tail-recursion factorial


【解决方案1】:

这是计算“某物”的原因。

对 printf() 的调用有一个返回值(很少使用)打印的字符数(包括制表符、换行符等)

在“C”代码 sn-p 中,printf() 返回 23。

'int' 返回值总是在同一个寄存器中返回。

printf() 将该寄存器设置为 23。

所以,返回了一些东西,没有从堆栈中删除任何东西。

【讨论】:

    【解决方案2】:

    它似乎在 B 中工作的原因是因为返回值很可能是在您架构上的寄存器中传递的。因此,通过所有递归层返回(或者如果编译器将整个事物优化为迭代)没有任何东西触及该寄存器并且您的代码似乎可以工作。

    不同的编译器可能不允许这种情况发生,或者您的编译器的下一个版本可能会对此进行不同的优化。事实上,编译器可以删除大部分函数,​​因为它允许假设由于未定义行为不会发生,它必须意味着函数中似乎发生未定义行为的部分永远不会到达并且可以安全删除.

    【讨论】:

      【解决方案3】:

      之所以有效,是因为返回值几乎总是存储在特定的 CPU 寄存器中(x86 为eax)。这意味着如果您没有显式返回值,则不会显式设置返回寄存器。因此,它的值可以是任何值,但它通常是最后调用函数的返回值。因此,以myfunc() 结束函数几乎可以保证与return myfunc(); 具有相同的行为(但它仍然是未定义的行为)。

      【讨论】:

        【解决方案4】:

        人们可以推测它起作用的原因,但没有办法给出明确的答案:在没有return 语句的情况下到达返回值函数的末尾并且使用返回值是未定义的行为,带有或没有优化。

        这个在你的编译器中“起作用”的原因是你的编译器使用的返回机制恰好在到达函数末尾时具有正确的值。例如,如果您的编译器在用于代码中最后一次计算的同一寄存器中返回整数(即ret = fun(n-1,sofar*n)),那么正确的值将被意外加载到返回寄存器中,从而掩盖未定义的行为。

        【讨论】:

          【解决方案5】:

          没有从应该返回值的函数返回值是undefined behavior

          如果运气好的话,那不是优化,而只是这些自动分配值的存储方式和存储位置的巧合。

          【讨论】:

            猜你喜欢
            • 2017-06-19
            • 2012-04-07
            • 1970-01-01
            • 2011-10-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-08-31
            • 1970-01-01
            相关资源
            最近更新 更多