【问题标题】:Can anyone explain this output to me?谁能向我解释这个输出?
【发布时间】:2011-06-19 00:26:42
【问题描述】:

所以我在玩一些思想实验,我想象当两个函数相互递归时会发生什么。如果两个函数都可能陷入无限循环,其中之一就是这样。

为此,我想到了这个简单的例子:

#include <iostream>
#include <cstdlib>

int foo(int x);
int bar(int x);


int foo(int x)
{
    return bar(x + 1);
}

int bar(int x)
{
    return foo(x - 1);
}

int main(int argc, char **argv)
{
    if (argc > 1)
        std::cout << "The value is: " << foo(atoi(argv[1])) << std::endl;

    return 0;
}

有趣的是,如果你用 g++ 编译它,它实际上不会打印任何东西。使用任何-O开关编译它,它就会陷入无限循环。

想法?这可能是编译器错误,还是可以预料到的?我认为在 -O 优化的情况下,它会意识到 foo(x) 和 bar(x) 只返回 x。

我没想到它实际上会“优化”调用并完全忽略将“值是”打印到标准输入。

编辑:我在 Cygwin 下使用 GCC 4.5.0 将它编译为 g++ source.cpp (-O1/2/3)。 -OX 版本实际上无限循环而不会溢出堆栈。

【问题讨论】:

  • 如果它没有永远循环,我会相当惊讶。
  • @delnan:然而,这正是他所说的,没有优化。
  • 它出现段错误,因为计算机没有无限堆栈。如果不进行优化,此递归将溢出堆栈。优化器无法确定此递归的头或尾除了,它可以看到如何将这些函数调用更改为跳转。在这种情况下没有堆栈溢出,因为优化器已将这些递归函数更改为递归协程。无论哪种方式,它都是未定义的行为。
  • “-OX 版本实际上无限循环,堆栈不会溢出。” # 你测试过它吗?
  • @Tomalak:我查看了程序集(i686-apple-darwin10-g++-4.2.1)。编译器将这些函数调用更改为带有 -O2 或更高版本的跳转。如果栈上没有操作,栈怎么会溢出?

标签: c++ recursion mutual-recursion


【解决方案1】:

由于您没有指定您使用的是哪个 C++,所以我将给出 C++0x 答案:

This is 实际上是undefined behaviour in C++0x

如果您使用的是 C++03,我无法完全解释该行为,但由于您的堆栈最终会溢出,因此您仍然处于依赖于实现的行为的范围内。期望在这里发生任何理智的事情是愚蠢的差事。

【讨论】:

    【解决方案2】:

    数学的角度来看,f(x) = b(x + 1)b(x) = f(x - 1) 不足以定义 fb。一种可能的解决方案是 f(x) = xb(x) = x - 1,而另一种可能的解决方案是 f(x) = 0g(x) = 0 (还有无数种方法)。所以在数学上,函数应该返回什么的问题没有答案。从计算的角度来看,情况更糟,因为毕竟,您不是在定义数学函数,而是在指定如何执行计算。 foo(x - 1) 表示“当我们将x - 1 的值作为参数传递时,执行函数foo() 中指定的计算”,bar(x + 1) 也是如此。因此,当每个函数无条件地说要调用另一个函数时,您已指定希望发生无限递归。正如其他人所提到的,一些语言(或语言版本)将此视为明确定义的情况(期望的结果是程序挂起),而另一些则将其视为未定义的情况(即编译器可以做任何它喜欢的事情)。

    【讨论】:

      【解决方案3】:

      由于您的递归没有停止条件,因此您很自然会陷入无限循环。您假设这些函数返回“just x”是错误的。他们注定永远不会回来。

      编译器不能仅仅优化递归。

      【讨论】:

      • 是的,我认为它不会优化递归。让我感到困惑的是,没有 -O 开关它什么也不打印,当我使用 -O 开关时它会循环。
      • 如果代码不调用未定义的行为,编译器必须遵守代码的意图。这并不意味着编译器必须实现程序员所表达的代码。例如,编译器经常将尾递归转换为循环。在这种情况下,编译器将相互递归的函数转换为一对递归协同程序。没有调用,没有堆栈。所以不是堆栈溢出,而是无限循环。
      • @David:这仍然没有真正解决他的第一个问题,即如果不进行优化,程序将在没有输出的情况下终止。
      • 我没有看到未优化的行为。我得到一个段错误,这导致我使用的每台机器上都有输出(不需要的输出)。也许这就是 OP 所说的“什么都不打印”的意思。
      猜你喜欢
      • 2017-03-11
      • 1970-01-01
      • 1970-01-01
      • 2017-09-12
      • 2021-02-27
      • 1970-01-01
      • 2011-02-11
      • 1970-01-01
      相关资源
      最近更新 更多