简体   繁体   English

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

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

Since everything has a limit, I was wondering if there is a limit to the number of nested for loops or as long as I have memory, I can add them, can the Visual Studio compiler create such a program? 由于一切都有限制,我想知道嵌套for循环的数量是否有限制,或者只要我有内存,我可以添加它们,Visual Studio编译器是否可以创建这样的程序?

Of course a 64 or more nested for loops would be not handy to debug, but is it doable? 当然,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....
      }
    }
  }
}

I'm going out on a limb by posting this, but I think that the answer is: 我发布了这篇文章,但我认为答案是:

Between 550 and 575 550到575之间

with default settings in Visual Studio 2015 使用Visual Studio 2015中的默认设置

I created a small program that generates nested for loops... 我创建了一个生成嵌套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);
            }
        }
        ...
        ...
    }
}

For 500 nested loops, the program can still be compiled. 对于500个嵌套循环,仍然可以编译该程序。 With 575 loops, the compiler bails out: 通过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. 警告AD0001分析器'Microsoft.CodeAnalysis.CSharp.Diagnostics.SimplifyTypeNames.CSharpSimplifyTypeNamesDiagnosticAnalyzer'抛出类型为'System.InsufficientExecutionStackException'的异常,并显示消息'堆栈不足以安全地继续执行程序。 This can happen from having too many functions on the call stack or function on the stack using too much stack space.'. 这可能是因为调用堆栈上的函数太多或堆栈上的函数使用了太多的堆栈空间。

with the underlying compiler message 使用底层编译器消息

error CS8078: An expression is too long or complex to compile 错误CS8078:表达式太长或太复杂而无法编译

Of course, this is a purely hypothetical result. 当然,这是纯粹的假设结果。 If the innermost loop does more than a Console.WriteLine , then fewer nested loops may be possible before the stack size is exceeded. 如果最内层循环不仅仅是Console.WriteLine ,那么在超出堆栈大小之前,可能会有更少的嵌套循环。 Also, this may not be a strictly technical limit, in the sense that there may be hidden settings to increase the maximum stack size for the "Analyzer" that is mentioned in the error message, or (if necessary) for the resulting executable. 此外,这可能不是严格的技术限制,因为可能存在隐藏设置以增加错误消息中提到的“Analyzer”的最大堆栈大小,或者(如果需要)生成的可执行文件。 This part of the answer, however, is left to the people who know C# in depth. 然而,这部分答案留给了深入了解C#的人。


Update 更新

In response to the question in the comments : 回答评论中问题

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个局部变量(如果它们用于for循环),和/或是否可以放入575个非嵌套 for循环单一功能

For both cases, the answer is: Yes, it is possible. 对于这两种情况,答案是:是的,这是可能的。 When filling the method with 575 auto-generated statements 使用575自动生成的语句填充方法时

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

it can still be compiled. 它仍然可以编译。 Everything else would have surprised me. 其他一切都会让我感到惊讶。 The stack size that is required for the int variables is just 2.3 KB. int变量所需的堆栈大小仅为2.3 KB。 But I was curious, and in order to test for further limits, I increased this number. 但我很好奇,为了测试进一步的限制,我增加了这个数字。 Eventually, it did not compile, causing the error 最终,它并没有编译,导致错误

error CS0204: Only 65534 locals, including those generated by the compiler, are allowed 错误CS0204:仅允许65534个本地,包括编译器生成的本地

which is an interesting point, but has already been observed elsewhere: Maximum number of variables in method 这是一个有趣的观点,但已在别处观察到: 方法中的最大变量数

Similarly, 575 non-nested for -loops, as in 同样,575个非嵌套 for -loops,如

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);
}

can be compiled as well. 也可以编译。 Here, I also tried to find the limit, and created more of these loops. 在这里,我也尝试找到限制,并创建了更多这些循环。 Particularly, I wasn't sure whether the loop variables in this case also count als "locals", because they are in their own { block } . 特别是,我不确定在这种情况下循环变量是否也算“本地”,因为它们在他们自己的{ block } But still, more than 65534 is not possible. 但是,超过65534是不可能的。 Finally, I added a test consisting of 40000 loops of the pattern 最后,我添加了一个由40000个模式循环组成的测试

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

that contained an additional variable in the loop, but these seem to count as "locals" as well, and it was not possible to compile this. 循环中包含一个额外的变量,但这些似乎也算作“本地”,并且无法编译它。


So to summarize: The limit of ~550 is indeed caused by the nesting depth of the loops. 总结一下:~550的极限确实是由环的嵌套深度引起的。 This also was indicated by the error message 错误消息也表明了这一点

error CS8078: An expression is too long or complex to compile 错误CS8078:表达式太长或太复杂而无法编译

The documentation of error CS1647 unfortunately (but understandably) does not specify a "measurement" of complexity, but only gives the pragmatic advice 遗憾的是,错误CS1647文档 (但可以理解)并未指定复杂性的“度量”,而只是提供了实用的建议

There was a stack overflow in the compiler processing your code. 处理代码的编译器中存在堆栈溢出。 To resolve this error, simplify your code. 要解决此错误,请简化代码。

To emphasize this again: For the particular case of deeply nested for -loops, all this is rather academic and hypothetical . 再次强调这一点: 对于深度嵌套for -loops的特殊情况,所有这些都是学术性和假设性的 But websearching for the error message of CS1647 reveals several cases where this error appeared for code that was most likely not intentionally complex, but created in realistic scenarios. 但是针对CS1647的错误消息的网络搜索揭示了一些情况,这些错误出现在很可能不是故意复杂但在现实场景中创建的代码中。

There is no hard limit in the C# language specification or the CLR. C#语言规范或CLR没有硬性限制。 Your code would be iterative, rather than recursive, which could lead to a stack overflow quite fast. 您的代码将是迭代的,而不是递归的,这可能导致堆栈溢出非常快。

There are a few things that could count as a threshold, for example the (generally) int counter you would use, which would allocate an int in memory for each loop (and before you have allocated your entire stack with ints...). 有一些东西可以算作一个阈值,例如你将使用的(通常) int计数器,它会为每个循环在内存中分配一个int (在你用整个堆栈分配整个堆栈之前......)。 Note that the use of that int is required and you may reuse the same variable. 请注意,需要使用该int ,您可以重用相同的变量。

As pointed out by Marco , the current threshold is more in the compiler than in the actual language specification or runtime. 正如Marco指出的 ,当前阈值在编译器中比在实际语言规范或运行时中更多。 Once that is recoded, you might up with quite some more iterations. 一旦重新编码,您可能会进行更多的迭代。 If you for example use Ideone , which by default uses the old compiler, you can get over 1200 for loops easily. 例如如果您使用Ideone ,它默认使用旧的编译器,您可以轻松地获得超过1200个for循环。

That much for loops are an indicator of bad design though. 尽管如此,循环次数仍然是糟糕设计的一个指标。 I hope this question is purely hypothetical. 我希望这个问题纯粹是假设的。

There is a limit for all C# compiled down to MSIL. 编译到MSIL的所有C#都有限制。 MSIL can only support 65535 local variables. MSIL只能支持65535个局部变量。 If your for loops are like the ones you showed in the example, each one requires a variable. 如果你的for循环就像你在例子中展示的那样,每个循环都需要一个变量。

It's possible that your compiler could allocate objects on the heap to act as storage for local variables, circumventing this limit. 您的编译器可能会在堆上分配对象以充当本地变量的存储,从而绕过此限制。 However, I'm not sure what sorts of odd results would come from that. 但是,我不确定会有什么样的奇怪结果。 There may be issues that arise with reflection which make such an approach illegal. 反思可能会产生一些问题,使这种做法非法。

Between 800 and 900 for empty for(;;) loops. 800到900之间为空for(;;)循环。

Mirrored Marco13's approach, except tried for(;;) loops: 镜像Marco13的方法,除了尝试for(;;)循环:

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

It worked for 800 nested for(;;) , but it gave the same error that Marco13 encountered when trying 900 loops. 它适用于800嵌套for(;;) ,但它给出了与Marco13在尝试900次循环时遇到的相同的错误。

When it does compile, the for(;;) appears to block the thread without maxing out the CPU; 当它编译时, for(;;)似乎阻塞线程而不会超出CPU; superficially, it seems to act like a Thread.Sleep() . 从表面上看,它似乎就像一个Thread.Sleep()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM