Поскольку у всего есть предел, мне было интересно, есть ли ограничение на количество вложенных 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#
for-loop
compilation
limit
Фред Смит
источник
источник
Ответы:
Я рискну, опубликовав это, но думаю, что ответ таков:
Между 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 циклами компилятор выручает:
с базовым сообщением компилятора
Конечно, это чисто гипотетический результат. Если самый внутренний цикл выполняет больше, чем a
Console.WriteLine
, то может быть возможно меньше вложенных циклов до того, как размер стека будет превышен. Кроме того, это не может быть строго техническим ограничением в том смысле, что могут быть скрытые настройки для увеличения максимального размера стека для «Анализатора», упомянутого в сообщении об ошибке, или (при необходимости) для результирующего исполняемого файла. Однако эта часть ответа остается на усмотрение людей, глубоко знающих C #.Обновить
В ответ на вопрос в комментариях :
В обоих случаях ответ: да, это возможно. При заполнении метода 575 автоматически сгенерированными операторами
int i0=0; Console.WriteLine(i0); int i1=0; Console.WriteLine(i1); ... int i574=0; Console.WriteLine(i574);
его еще можно скомпилировать. Все остальное меня бы удивило. Размер стека, который требуется для
int
переменных, составляет всего 2,3 КБ. Но мне было любопытно, и я увеличил это число, чтобы проверить дальнейшие пределы. В конце концов, он не скомпилировался, что привело к ошибке.что является интересным моментом, но уже наблюдалось в другом месте: максимальное количество переменных в методе
Аналогичным образом , 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); }
также могут быть скомпилированы. Здесь я также попытался найти предел и создал больше таких циклов. В частности, я не был уверен, считаются ли переменные цикла в этом случае также «локальными», потому что они сами по себе
{ block }
. Но все равно больше 65534 невозможно. Напоследок добавил тест, состоящий из 40000 петель узораfor (int i39999 = 0; i39999 < 10; i39999++) { int j = 0; Console.WriteLine(j + i39999); }
которые содержат дополнительную переменную в цикле, но они, кажется, тоже считаются «локальными», и это было невозможно скомпилировать.
Итак, подведем итог: ограничение ~ 550 действительно вызвано глубиной вложенности петель. На это также указывает сообщение об ошибке
К сожалению, в документации по ошибке CS1647 (но по понятным причинам) не указывается «измерение» сложности, а дается только практический совет.
Еще раз подчеркнем: для частного случая глубоко вложенных
for
циклов все это скорее академическое и гипотетическое . Но веб-поиск сообщения об ошибке CS1647 показывает несколько случаев, когда эта ошибка появлялась для кода, который, скорее всего, не был намеренно сложным, а был создан в реалистичных сценариях.источник
for
циклов. У Microsoft BASIC был лимит вложенности 8.42
как ответ.В спецификации языка C # или в среде CLR нет жесткого ограничения. Ваш код будет итеративным, а не рекурсивным, что может довольно быстро привести к переполнению стека.
Есть несколько вещей, которые могут считаться пороговыми, например (обычно)
int
счетчик, который вы использовали бы, который выделял быint
в памяти для каждого цикла (и до того, как вы распределили весь свой стек с помощью int ...). Обратите внимание, чтоint
это необходимо, и вы можете повторно использовать одну и ту же переменную.Как указал Марко , текущий порог больше в компиляторе, чем в фактической спецификации языка или во время выполнения. Как только это будет перекодировано, вы можете сделать еще несколько итераций. Если вы, например, используете Ideone , который по умолчанию использует старый компилятор, вы можете
for
легко получить более 1200 циклов.Однако такое количество циклов for указывает на плохой дизайн. Надеюсь, этот вопрос чисто гипотетический.
источник
int
). Если бы это были циклы без переменной цикла, все было бы иначе.Существует предел для всего C #, скомпилированного до MSIL. MSIL может поддерживать только 65535 локальных переменных. Если ваши
for
циклы похожи на те, которые вы показали в примере, для каждого из них требуется переменная.Возможно, что ваш компилятор может выделить объекты в куче, чтобы они действовали как хранилище для локальных переменных, в обход этого ограничения. Однако я не уверен, какие странные результаты из этого могут возникнуть. При рефлексии могут возникнуть проблемы, которые сделают такой подход незаконным.
источник
while(true)
который формирует бесконечный цикл. Если мы отредактируем вопрос на «Есть ли ограничение на количество вложенных циклов for в завершающейся программе?» тогда можем ли мы использовать трюк с локальной переменной для создания жесткого ограничения?break
или сделать так, чтобы условие цикла проверяло что-то внешнее, например( ; DateTime.now() < ... ; )
От 800 до 900 для пустых
for(;;)
петель.Отраженный подход Marco13, кроме проверенных
for(;;)
циклов:for (;;) // 0 for (;;) // 1 for (;;) // 2 // ... for (;;) // n_max { // empty body }
Он работал для 800 вложенных
for(;;)
циклов, но выдавал ту же ошибку, что и Marco13 при попытке 900 циклов.Когда он компилируется,
for(;;)
кажется, что поток блокируется, не перегружая ЦП; на первый взгляд кажется, что он действует какThread.Sleep()
.источник
for(;;)
это будет бесконечный цикл в C # ?! (Я не могу попробовать прямо сейчас, но если окажется, что это не так, я удалю этот комментарий)for(;;)
блокируется без использования процессорного времени.for(;;)
Циклы вложены, поэтому все они начинаются до самого вложенного цикла, который бесконечен. Чтобыfor(;;)
заблокировать первый , это должно бытьfor(;;){}
. Что касается того, зачем это нужно, это был просто тест, чтобы увидеть, какие ограничения имеет текущая реализация .NET / C #; очевидно, он не может перейти на 900for(;;)
, хотя, как вы заметили, ничего не делает.