10

这段代码有什么问题:

using System;
namespace app1
{
    static class Program
    {
        static int x = 0;
        static void Main()
        {
            fn1();
        }
        static void fn1()
        {
            Console.WriteLine(x++);
            fn1();
        }
    }
}

我使用以下命令编译这段代码:

csc /warn:0 /out:app4noex.exe app4.cs

当我双击 exe 时,它​​似乎没有抛出异常(StackOverFlowException),并且永远运行。

使用visual studio命令提示符2010,但我在系统上也安装了vs 2012,都是最新的。

4

4 回答 4

10

因为优化器将尾递归调用展开为:

    static void fn1()
    {
      START:

        Console.WriteLine(x++);
        GOTO START;
    }

重写以获得如下异常:

   static int y;

   static void fn1()
   {
       Console.WriteLine(x++);
       fn1();
       Console.WriteLine(y++);
   }
于 2012-12-29T20:51:15.410 回答
3

x64 抖动将其检测为尾调用并将其优化掉,而 x86 抖动则不这样做。x64 抖动对这些优化更具侵略性。请参阅 Bart de Smet 的分析和 CLR 团队的博客文章

于 2012-12-29T21:07:05.577 回答
1

有一种东西叫做尾递归优化

从堆栈的角度来看,基本上它意味着如果一个方法做的最后一件事是调用另一个方法,那么新的调用可以获取调用方法的堆栈帧。例如在:

static void Main()
{
  fn(0);
}

static void fn(int value)
{
   fn(value+1);
}

而不是调用堆栈变得Main->fn(0)->fn(1)->...令人作呕,它将恰好有两个链接长,第一个Main->fn(0)Main->fn(1),直到Main->fn(int.MaxValue)它会爆炸或溢出的地方。

现在,问题是,C# 编译器真的这样做了吗?
AFAIK,使用 4.0 和更高版本的 C# 编译器,在 x86 环境中编译时,它使用尾调用优化,而在编译 x64 应用程序时,它确实使用尾调用优化(显然,从其他评论/答案我'是正确的)。例如,在我的系统上,使用 LINQPad,您提供的代码会立即以StackOverflowException.

于 2012-12-29T21:12:54.723 回答
-1

当程序在 Visual Studio 环境中运行时,它将使用有限的堆栈深度。我的意思是,当您在 VS2012 中通过按键盘上的 F5 和 F6 来编译程序时,它会向 csc.exe 程序发送一些参数以限制程序并导致堆栈溢出来了解您程序中的错误源代码和算法。实际上,没有 Stack-over-flow 错误,程序的进程将使用真实存储和虚拟存储,操作系统会为您完成。

注意:这也和你的操作系统有关,有些操作系统在内存管理和cpu调度方面的弱点会报错。

于 2012-12-29T21:10:59.173 回答