嵌套 'for' 循环的数量是否有限制?

Is there a limit to the number of nested 'for' loops?

既然凡事都有个限度,我就想知道是不是嵌套的for循环有限制,或者只要有内存就可以加,Visual Studio可以吗编译器创建这样的程序?

当然,64 个或更多的嵌套 for 循环不便于调试,但它可行吗?

private void TestForLoop()
{
  for (int a = 0; a < 4; a++)
  {
    for (int b = 0; b < 56; b++)
    {
      for (int c = 0; c < 196; c++)
      {
        //etc....
      }
    }
  }
}

C# 语言规范或 CLR 中没有硬性限制。您的代码将是迭代的,而不是递归的,这可能会很快导致堆栈溢出。

有一些东西可以算作阈值,例如(通常)您将使用的 int 计数器,它会为每个循环(以及之前)在内存中分配一个 int你已经用整数分配了整个堆栈......)。请注意,使用 int 是必需的,您可以重复使用相同的变量。

作为默认使用旧编译器的 , the current threshold is more in the compiler than in the actual language specification or runtime. Once that is recoded, you might up with quite some more iterations. If you for example use Ideone,您可以轻松获得超过 1200 for 个循环。

不过,这么多的 for 循环表明设计不当。我希望这个问题纯属假设。

我发布这个是冒险的,但我认为答案是:

550 到 575 之间

使用 Visual Studio 2015 年的默认设置

我创建了一个生成嵌套 for 循环的小程序...

for (int i0=0; i0<10; i0++)
{
    for (int i1=0; i1<10; i1++)
    {
        ...
        ...
        for (int i573=0; i573<10; i573++)
        {
            for (int i574=0; i574<10; i574++)
            {
                Console.WriteLine(i574);
            }
        }
        ...
        ...
    }
}

对于500个嵌套循环,程序仍然可以编译。使用 575 个循环,编译器退出:

Warning AD0001 Analyzer 'Microsoft.CodeAnalysis.CSharp.Diagnostics.SimplifyTypeNames.CSharpSimplifyTypeNamesDiagnosticAnalyzer' threw an exception of type 'System.InsufficientExecutionStackException' with message 'Insufficient stack to continue executing the program safely. This can happen from having too many functions on the call stack or function on the stack using too much stack space.'.

使用底层编译器消息

error CS8078: An expression is too long or complex to compile

当然,这是纯属假设的结果。如果最内层循环执行的操作多于 Console.WriteLine,则在超过堆栈大小之前嵌套循环可能会更少。此外,这可能不是严格的技术限制,因为可能存在隐藏设置以增加错误消息中提到的 "Analyzer" 或(如有必要)生成的可执行文件的最大堆栈大小.然而,这部分答案留给深入了解 C# 的人。


更新

回应

I would be interested to see this answer expanded to "prove" experimentally whether you can put 575 local variables on the stack if they're not used in for-loops, and/or whether you can put 575 non-nested for-loops in a single function

对于这两种情况,答案都是:是的,有可能。当用 575 个自动生成的语句填充方法时

int i0=0;
Console.WriteLine(i0);
int i1=0;
Console.WriteLine(i1);
...
int i574=0;
Console.WriteLine(i574);

它仍然可以编译。其他一切都会让我感到惊讶。 int 变量所需的堆栈大小仅为 2.3 KB。但我很好奇,为了测试更多的限制,我增加了这个数字。最终,它没有编译,导致错误

error CS0204: Only 65534 locals, including those generated by the compiler, are allowed

这是一个有趣的观点,但已在其他地方观察到:Maximum number of variables in method

类似地,575 非嵌套 for 循环,如

for (int i0=0; i0<10; i0++)
{
    Console.WriteLine(i0);
}
for (int i1=0; i1<10; i1++)
{
    Console.WriteLine(i1);
}
...
for (int i574=0; i574<10; i574++)
{
    Console.WriteLine(i574);
}

也可以编译。在这里,我也试图找到极限,并创建了更多这样的循环。特别是,我不确定这种情况下的循环变量是否也算作 "locals",因为它们在它们自己的 { block } 中。但是,仍然不可能超过 65534。最后,我添加了一个包含 40000 个模式循环的测试

for (int i39999 = 0; i39999 < 10; i39999++)
{
    int j = 0;
    Console.WriteLine(j + i39999);
}

在循环中包含一个额外的变量,但这些似乎也算作"locals",并且无法编译它。


所以总结一下:~550的限制确实是由循环的嵌套深度引起的。错误消息也表明了这一点

error CS8078: An expression is too long or complex to compile

不幸的是(但可以理解)documentation of error CS1647没有指定"measurement"的复杂性,而只是给出了务实的建议

There was a stack overflow in the compiler processing your code. To resolve this error, simplify your code.

再次强调这一点:对于深度嵌套 for 循环的特殊情况,所有这些都是相当学术和假设的。但是网络搜索 CS1647 的错误消息揭示了几种情况,这些错误出现的代码很可能不是故意复杂的,而是在现实场景中创建的。

所有编译为 MSIL 的 C# 都有一个限制。 MSIL 只能支持 65535 个局部变量。如果您的 for 循环与您在示例中显示的循环类似,则每个循环都需要一个变量。

您的编译器可能会在堆上分配对象作为局部变量的存储,从而绕过此限制。但是,我不确定这会产生什么样的奇怪结果。反射可能会出现一些问题,使这种方法成为非法。

for(;;) 循环介于 800 和 900 之间。

镜像 Marco13 的方法,除了尝试 for(;;) 循环:

for (;;)  // 0
for (;;)  // 1
for (;;)  // 2
// ...
for (;;)  // n_max
{
  // empty body
}

它适用于 800 次嵌套 for(;;),但它给出了 Marco13 在尝试 900 次循环时遇到的相同错误。

当它编译时,for(;;) 似乎在没有最大化 CPU 的情况下阻塞线程;从表面上看,它似乎像 Thread.Sleep().