【问题标题】:Why does int main() { return main(); } cause stackoverflow and not tail recursion?为什么 int main() { return main();导致stackoverflow而不是尾递归?
【发布时间】:2016-09-15 09:54:50
【问题描述】:

使用 GCC 在 Windows 上编译。它立即与Exception Code: c00000fd 崩溃。

编辑:尝试编译以下代码(用于可见输出),它会导致堆栈溢出。

#include<stdio.h>
int main(void)
{
printf("Hello World\n");
return main();
}

输出 -

>gcc trailORoverflow.c -o trailORoverflow.exe

Hello World
Hello World
Hello World
Hello World
Hello World
Hello World

它会持续打印 Hello World 一段时间并崩溃。

编辑:O2O3-O1 -foptimize-sibling-calls 优化没有崩溃。

【问题讨论】:

  • 也许正确答案与尾调用递归有关。 iirc GCC 将实现尾调用递归,即尽可能优化递归调用
  • 因为 C 标准不需要特定的优化。 (它甚至不需要为局部变量使用堆栈)。并使用正确的原型风格函数声明器:main(void)
  • 用不同的优化标志尝试 gcc 绝对是有意义的,可能一些组合会给你你想要的无限循环
  • 使用-O2-O3-O1 -foptimize-sibling-calls
  • gcc 默认不执行尾递归优化,可能是因为它使程序在异常后更难调试和展开。因此,您未优化的程序每次调用时都会占用一些堆栈空间(callq main 后跟push %rbp),直到达到操作系统施加的限制。优化的程序会执行类似jmp main 的操作。我在 Windows 上没有 gcc,因此无法提供全面的答案,但其他人会这样做。

标签: c gcc stack-overflow tail-recursion


【解决方案1】:

您显示的代码将无限调用main,因此会导致堆栈溢出。在任何函数的情况下都是如此,而不是特定于 main。每个函数调用都会在内存中创建一个堆栈帧,并且随着递归的深入,会创建无限个这样的帧,你会得到一个堆栈溢出。

但是,如果您像下面的示例那样进行适当的基本终止,对于main 中的递归调用,那么会有一件有趣的事情。

int main (void)
{
  static int x = 100;

  if (x == 0)
  {
    return 0;
  }
  x--;
  printf ("%d\n", x);
  main ();
  return 0;
}

在 C 和 C++ 语言中递归调用 main 是有区别的,我认为指出这一点很有趣。这是我写的post,我正在做一些解释。

C++ 标准在

中谈到了这些

第 3.6.1 节第 3 段

Recursive calls are permitted, except to the function named main. 

和第 5.2.2 节第 9 段

The function main shall not be used within the program. … … … 

我在 C 标准中没有发现任何此类限制。我在第 6.5.2.2 节第 11 节中发现的关于 C99 标准中递归调用的内容如下

Recursive function calls shall be permitted, both directly and indirectly through any chain of other functions. 

因此,在 C 语言中递归调用 main 是确定性的。但根据 C++ 标准,不允许从任何函数调用 main 或递归调用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-04
    • 2010-10-01
    • 2011-09-24
    • 2015-10-02
    • 2018-03-16
    • 2010-09-06
    • 1970-01-01
    相关资源
    最近更新 更多