Существует ли ограничение на количество вложенных циклов "for"?

поскольку все имеет предел, мне было интересно, есть ли предел количеству вложенных 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 ответов


Я иду на риск, публикуя это, но я думаю, что ответ:

между 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 'Майкрософт.CodeAnalysis.Используется CSharp.Диагностика.SimplifyTypeNames.CSharpSimplifyTypeNamesDiagnosticanalyzer "бросил исключение типа" системы.InsufficientExecutionStackException "с сообщением" недостаточный стек для продолжения безопасного выполнения программы. Это может произойти из-за слишком большого количества функций в стеке вызовов или функции в стеке, используя слишком много пространства стека.'.

с базовым сообщением компилятора

ошибка CS8078: выражение слишком длинный или сложный для компиляции

конечно, это чисто гипотетически результат. Если внутренний цикл делает больше, чем Console.WriteLine, тогда возможно меньше вложенных циклов до превышения размера стека. Кроме того, это не может быть строго техническим ограничением, в том смысле, что могут быть скрытые настройки для увеличения максимального размера стека для "анализатора", упомянутого в сообщении об ошибке, или (при необходимости) для результирующего исполняемого файла. Этот часть ответа, однако, оставлена людям, которые знают C# в глубине.


обновление

в ответ на вопрос в комментариях :

мне было бы интересно увидеть этот ответ расширенным, чтобы "доказать" экспериментально, можете ли вы поместить 575 локальных переменных в стек, если они не используется в for-loops и / или можно ли поставить 575 невложенной for-loops в одном функция

в обоих случаях, ответ: Да, это возможно. При заполнении метода 575 автоматически сгенерированными операторами

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

он все еще может быть скомпилирован. Все остальное удивило бы меня. Размер стека, необходимый для int переменные-всего 2,3 КБ. Но мне было любопытно, и, чтобы проверить дальнейшие пределы, я увеличил это число. В конце концов, это произошло не компиляции вызывает ошибку

ошибка CS0204: разрешено только 65534 локальных, в том числе созданных компилятором

это интересный момент, но уже было замечено в другом месте: максимальное количество переменных в методе

аналогично, 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);
}

также можно скомпилировать. Здесь я также попытался найти предел и создал больше этих циклов. В частности, я не был уверен будут ли переменные цикла в этом случае также считать als "локальными", потому что они находятся в их собственном { block }. Но все же, больше 65534 невозможно. Наконец, я добавил тест, состоящий из 40000 циклов шаблона

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

который содержал дополнительную переменную на цикл, но они, похоже, также считаются "местными", и это было невозможно скомпилировать.


Итак, чтобы суммировать: предел ~550 действительно вызван глубина вложенности из петель. Это также было указано сообщением об ошибке

ошибка CS8078: выражение слишком длинное или сложное для компиляции

на документация об ошибке CS1647 к сожалению (но понятно) не указывает "измерение" сложности, а только дает прагматический совет

в компиляторе, обрабатывающем ваш код, было переполнение стека. Для решения этой ошибка, упростить код.

чтобы подчеркнуть это еще раз: для частного случая глубоко вложенных for-loops, все это довольно академично и гипотетически. Но websearching для сообщения об ошибке CS1647 показывает несколько случаев, когда эта ошибка появилась для кода, который, скорее всего, не был намеренно сложным, но создан в реалистичных сценариях.


в спецификации языка C# или CLR нет жесткого ограничения. Ваш код будет итеративным, а не рекурсивным, что может привести к переполнению стека довольно быстро.

есть несколько вещей, которые могут считаться порогом, например (обычно) int счетчик можно использовать, что бы выделить int в памяти для каждого цикла (и до того, как вы выделили весь стек с помощью ints...). Обратите внимание, что использование этого и вы можете повторно использовать та же переменная.

As указал Марко, текущее порог больше в компиляторе чем в спецификации языка или во время выполнения. Как только это будет перекодировано, вы можете получить еще несколько итераций. если вы, например, используете Ideone, который по умолчанию используется старый компилятор, вы можете получить более 1200 for легко петли.

это много для петель является показателем плохого дизайна, хотя. Я надеюсь, что этот вопрос чисто гипотетический.


существует ограничение для всех c#, скомпилированных до MSIL. MSIL может поддерживать только 65535 локальных переменных. Если for петли похожи на те, которые вы показали в примере, каждый из них требует переменной.

возможно, что ваш компилятор может выделять объекты в куче в качестве хранилища для локальных переменных, обходя это ограничение. Однако, я не уверен, какие странные результаты от этого. Могут быть вопросы, которые возникают с отражением, которые делают такой подход незаконным.


между 800 и 900 для пустых for(;;) петли.

зеркальный подход Marco13, кроме пробовал for(;;) петли:

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

он работал для 800 вложенных for(;;), но он дал ту же ошибку, с которой столкнулся Marco13 при попытке 900 циклов.

когда он компилируется,for(;;) Кажется, блокирует поток без максимизации процессора; внешне, похоже, он действует как Thread.Sleep().