Существует ли ограничение на количество вложенных циклов "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....
      }
    }
  }
}
429   4  

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

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

ошибка CS8078: выражение слишком долго или сложно компилировать

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


обновление

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

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

в обоих случаях, ответ: Да, это возможно. При заполнении метода 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: выражение слишком длинное или сложное для компиляции

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

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

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

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

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

как указал Марко, текущее порог больше в компиляторе чем в спецификации языка или во время выполнения. Как только это будет перекодировано, вы можете получить еще несколько итераций. если вы, например, используете 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().

    Ничего не найдено.

Добавить ответ:
Отменить.