Ponieważ wszystko ma swój limit, zastanawiałem się, czy istnieje ograniczenie liczby zagnieżdżonych for
pętli, czy jeśli mam pamięć, mogę je dodać, czy kompilator Visual Studio może stworzyć taki program?
Oczywiście 64 lub więcej zagnieżdżonych for
pętli nie byłoby przydatne do debugowania, ale czy jest to wykonalne?
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
Fred Smith
źródło
źródło
Odpowiedzi:
Wychodzę na skraj, publikując to, ale myślę, że odpowiedź brzmi:
Między 550 a 575
z ustawieniami domyślnymi w programie Visual Studio 2015
Stworzyłem mały program, który generuje zagnieżdżone
for
pętle ...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); } } ... ... } }
W przypadku 500 zagnieżdżonych pętli program można nadal skompilować. W przypadku pętli 575 kompilator wyskakuje:
z podstawowym komunikatem kompilatora
Oczywiście jest to czysto hipotetyczny wynik. Jeśli najbardziej wewnętrzna pętla robi więcej niż a
Console.WriteLine
, to mniej zagnieżdżonych pętli może być możliwych, zanim rozmiar stosu zostanie przekroczony. Może to również nie być ściśle techniczne ograniczenie w tym sensie, że mogą istnieć ukryte ustawienia zwiększające maksymalny rozmiar stosu dla „Analizatora”, o którym mowa w komunikacie o błędzie, lub (jeśli to konieczne) dla wynikowego pliku wykonywalnego. Jednak ta część odpowiedzi jest pozostawiona osobom, które dogłębnie znają język C #.Aktualizacja
W odpowiedzi na pytanie w komentarzach :
W obu przypadkach odpowiedź brzmi: tak, jest to możliwe. Podczas wypełniania metody 575 automatycznie wygenerowanymi wyciągami
int i0=0; Console.WriteLine(i0); int i1=0; Console.WriteLine(i1); ... int i574=0; Console.WriteLine(i574);
nadal można go skompilować. Wszystko inne by mnie zaskoczyło. Rozmiar stosu wymagany dla
int
zmiennych to zaledwie 2,3 KB. Ale byłem zaciekawiony i aby sprawdzić dalsze ograniczenia, zwiększyłem tę liczbę. Ostatecznie nie skompilował się, powodując błądco jest interesujące, ale zostało już zaobserwowane gdzie indziej: Maksymalna liczba zmiennych w metodzie
Podobnie, 575 zagnieżdżane
for
-loops, jak wfor (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); }
można również skompilować. Tutaj również próbowałem znaleźć limit i utworzyłem więcej tych pętli. W szczególności nie byłem pewien, czy zmienne pętli w tym przypadku również liczą się jako „lokalne”, ponieważ są one same w sobie
{ block }
. Ale nadal więcej niż 65534 nie jest możliwe. Na koniec dodałem test składający się z 40000 pętli wzorufor (int i39999 = 0; i39999 < 10; i39999++) { int j = 0; Console.WriteLine(j + i39999); }
który zawierał dodatkową zmienną w pętli, ale wydaje się, że są one również liczone jako „lokalne” i nie można było tego skompilować.
Podsumowując: granica ~ 550 jest rzeczywiście spowodowana głębokością zagnieżdżenia pętli. Wskazuje na to również komunikat o błędzie
Dokumentacja błędów CS1647 niestety (ale ze zrozumiałych względów) nie określa „pomiar” złożoności, ale tylko daje pragmatyczne porady
Aby jeszcze raz to podkreślić: w szczególnym przypadku głęboko zagnieżdżonych
for
pętli wszystko to jest raczej akademickie i hipotetyczne . Jednak wyszukiwanie w Internecie komunikatu o błędzie CS1647 ujawnia kilka przypadków, w których ten błąd pojawił się dla kodu, który najprawdopodobniej nie był celowo złożony, ale został utworzony w realistycznych scenariuszach.źródło
for
pętli. Microsoft BASIC miał limit zagnieżdżenia wynoszący 8.42
to odpowiedź.Nie ma sztywnego limitu w specyfikacji języka C # ani w środowisku CLR. Twój kod byłby iteracyjny, a nie rekurencyjny, co mogłoby doprowadzić do dość szybkiego przepełnienia stosu.
Jest kilka rzeczy, które mogą być liczone jako próg, na przykład (ogólnie)
int
licznik, którego używałbyś, który przydzielałbyint
w pamięci dla każdej pętli (i zanim przydzielisz cały stos intami ...). Pamiętaj, że użycie tegoint
jest wymagane i możesz ponownie użyć tej samej zmiennej.Jak zauważył Marco , bieżący próg występuje bardziej w kompilatorze niż w rzeczywistej specyfikacji języka lub w czasie wykonywania. Po przekodowaniu możesz uzyskać kilka kolejnych iteracji. Jeśli na przykład używasz Ideone , który domyślnie używa starego kompilatora, możesz łatwo uzyskać ponad 1200
for
pętli.Tyle w przypadku pętli wskazuje jednak na zły projekt. Mam nadzieję, że to pytanie jest czysto hipotetyczne.
źródło
int
). Gdyby były to pętle bez zmiennej pętli, byłoby inaczej.Istnieje limit dla wszystkich C # skompilowanych do MSIL. MSIL może obsługiwać tylko 65535 zmiennych lokalnych. Jeśli twoje
for
pętle są takie jak te, które pokazałeś w przykładzie, każda z nich wymaga zmiennej.Możliwe, że Twój kompilator może przydzielić obiekty na stercie, aby działały jako magazyn dla zmiennych lokalnych, omijając ten limit. Nie jestem jednak pewien, jakie dziwne rezultaty wynikną z tego. Podczas refleksji mogą pojawić się kwestie, które czynią takie podejście nielegalnym.
źródło
while(true)
który utworzyłby nieskończoną pętlę. Jeśli zredagowaliśmy pytanie „Czy istnieje ograniczenie liczby zagnieżdżonych pętli for w programie kończącym?” czy moglibyśmy użyć sztuczki ze zmienną lokalną, aby utworzyć sztywny limit?break
lub sprawić, by warunek pętli sprawdził coś zewnętrznego, na przykład( ; DateTime.now() < ... ; )
Od 800 do 900 dla pustych
for(;;)
pętli.Odzwierciedlono podejście Marco13, z wyjątkiem wypróbowanych
for(;;)
pętli:for (;;) // 0 for (;;) // 1 for (;;) // 2 // ... for (;;) // n_max { // empty body }
for(;;)
Działało dla 800 zagnieżdżonych , ale dawało ten sam błąd, który napotkał Marco13 podczas próbowania 900 pętli.Kiedy się kompiluje,
for(;;)
wydaje się, że blokuje wątek bez maksymalnego wykorzystania procesora; powierzchownie wydaje się działać jak plikThread.Sleep()
.źródło
for(;;)
to będzie nieskończona pętla w C # ?! (Nie mogę tego teraz wypróbować, ale jeśli okaże się, że jest źle, usunę ten komentarz)for(;;)
blokuje się bez zużywania czasu procesora.for(;;)
Pętle są zagnieżdżone, więc wszystkie zaczynają się od najbardziej zagnieżdżonej pętli, która jest nieskończona.for(;;)
Musiałby to być pierwszy blokfor(;;){}
. Jeśli chodzi o to, dlaczego to robi, był to tylko test, aby zobaczyć, jakie ograniczenia ma bieżąca implementacja .NET / C #; najwyraźniej nie może dojść do 900for(;;)
, chociaż, jak zauważyłeś, nic nie robi.