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

时间:2016-12-27 14:30:27

标签: c# for-loop compilation limit

由于一切都有限制,我想知道嵌套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....
      }
    }
  }
}

4 个答案:

答案 0 :(得分:119)

我通过张贴这个问题出去了,但我认为答案是:

介于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个循环,编译器就会退出:

  

警告AD0001分析器'Microsoft.CodeAnalysis.CSharp.Diagnostics.SimplifyTypeNames.CSharpSimplifyTypeNamesDiagnosticAnalyzer'抛出类型为'System.InsufficientExecutionStackException'的异常,并显示消息'堆栈不足以安全地继续执行程序。这可能是因为调用堆栈上的函数太多或堆栈上的函数占用太多堆栈空间。'。

使用底层编译器消息

  

错误CS8078:表达式太长或太复杂而无法编译

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

更新

回应question in the comments

  

我很有兴趣看到这个答案扩展到“证明”是否可以在堆栈中放置575个局部变量,如果它们在for循环中,和/或是否可以在单个函数中放入575个非嵌套 for循环

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

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

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

  

错误CS0204:仅允许65534个本地,包括编译器生成的本地

这是一个有趣的观点,但已在其他地方观察过: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);
}

也可以编译。在这里,我也尝试找到限制,并创建了更多这些循环。特别是,我不确定这种情况下的循环变量是否也算“本地人”,因为它们在他们自己的{ block }中。但是,超过65534是不可能的。最后,我添加了一个由40000个模式循环组成的测试

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

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

总结一下:~550的极限确实是由循环的嵌套深度引起的。错误消息

也表明了这一点
  

错误CS8078:表达式太长或太复杂而无法编译

不幸的是documentation of error CS1647(但可以理解)并未指定复杂性的“度量”,而只是提供了实用的建议

  

编译器处理代码时出现堆栈溢出。要解决此错误,请简化代码。

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

答案 1 :(得分:40)

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

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

作为pointed out by Marco,当前阈值在编译器中比在实际语言规范或运行时中更多。一旦重新编码,您可能会进行更多的迭代。 If you for example use Ideone,默认情况下使用旧编译器,您可以轻松获得超过1200个for循环。

尽管如此,循环次数仍然是糟糕设计的一个指标。我希望这个问题纯粹是假设的。

答案 2 :(得分:13)

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

您的编译器可能会在堆上分配对象以充当本地变量的存储,从而绕过此限制。但是,我不确定会有什么样的奇怪结果。反思可能会产生一些问题,使这种做法变得非法。

答案 3 :(得分:7)

for(;;)次循环在800到900之间。

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

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

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

编译时,for(;;)似乎会阻塞线程而不会超出CPU;从表面上看,它似乎就像Thread.Sleep()