Jako programista z pewnością znasz błąd przepełnienia stosu z powodu oczywistej rekurencji. Ale z pewnością istnieje wiele dziwnych i niezwykłych sposobów, aby Twój ulubiony język wypluł ten błąd.
Cele:
- Musi spowodować przepełnienie stosu, które jest wyraźnie widoczne na wyjściu błędu.
- Nie wolno używać oczywistej rekurencji.
Przykłady nieprawidłowych programów:
// Invalid, direct obvious recursion.
methodA(){ methodA(); }
// Invalid, indirect, but obvious recursion.
methodA(){ methodB(); }
methodB(){ methodA(); }
Najbardziej kreatywne sposoby są najlepsze, ponieważ jest to konkurs popularności . Tj. Unikaj nudnych oczywistych odpowiedzi takich jak to:
throw new StackOverflowError(); // Valid, but very boring and downvote-deserving.
Mimo że teraz zaakceptowałem odpowiedź, dodawanie kolejnych odpowiedzi jest nadal w porządku :)
popularity-contest
stack
masterX244
źródło
źródło
Odpowiedzi:
Pyton
Spowoduje to natychmiastową awarię tłumacza:
Zamiast używać rekurencji, po prostu zmniejsza stos, aby natychmiast się przepełnił.
źródło
Pyton
źródło
C / Linux 32bit
Działa poprzez nadpisanie adresu zwrotnego, więc
g
wraca do punktumain
przed wezwaniemf
. Działa na każdej platformie, na której stos zwrotny znajduje się na stosie, ale może wymagać poprawek.Oczywiście pisanie poza tablicą jest nieokreślonym zachowaniem i nie masz żadnej gwarancji, że spowoduje to przepełnienie stosu, zamiast, powiedzmy, pomalować wąsy na niebiesko. Duże znaczenie mogą mieć szczegóły flag platformy, kompilatora i kompilacji.
źródło
JavaScript / DOM
Jeśli chcesz zabić swoją przeglądarkę, wypróbuj to w konsoli.
źródło
with (document.body) { addEventListener('DOMSubtreeModified', function() { appendChild(firstChild); }, false); title = 'Kill me!'; } 15:43:43.642 TypeError: can't convert undefined to object
Jawa
Widziałem coś takiego gdzieś tutaj:
Edycja: Znaleziono tam, gdzie go widziałem: odpowiedź Joe K na najkrótszy program, który generuje błąd StackOverflow
To może mylić niektórych początkujących w Javie. Po prostu ukrywa połączenie rekurencyjne.
val + this
staje się,val + this.toString()
ponieważval
jest ciągiem.Zobacz, jak działa tutaj: http://ideone.com/Z0sXiD
źródło
new StringBuilder().append(val).append("").append(this).toString()
, a ostatni append wywołuje String.valueOf (...), który z kolei wywołuje metodęString. To sprawia, że śledzenie stosu jest nieco zróżnicowane (są tam trzy metody)."" + this.toString()
.+ "" +
może to podpowiedzieć ludziom, ponieważ na pierwszy rzut oka wydaje się bezużyteczne.String val;
ireturn val + this;
może być nieco podstępny""
konstrukcji -String jest+ ""
do
Raczej latwo:
źródło
ulimit -s unlimited
w powłoce rozwiązuje to w Linuksie)~0u
jest dość dużą liczbą w C.Przepełnienie stosu nierekurencyjnego w C
Niezgodność konwencji wywoływania.
Kompiluj z
gcc -O0
.__cdecl
funkcje oczekują od wywołującego wyczyszczenia stosu i__stdcall
oczekują od odbierającego, więc przez wywołanie wskaźnika funkcji typecast czyszczenie nigdy sięmain
nie kończy - wypycha parametr na stos dla każdego wywołania, ale nic go nie wyrywa i ostatecznie wypełnienia stosu.źródło
JavaScript
źródło
+this
zrobić?+{toString:"".toLocaleString}
:-)Byłem sfrustrowany faktem, że java 7 i java 8 są odporne na mój zły kod w mojej poprzedniej odpowiedzi . Uznałem więc, że łatka do tego jest konieczna.
Powodzenie! Zrobiłem
printStackTrace()
rzutStackOverflowError
.printStackTrace()
jest powszechnie używany do debugowania i logowania i nikt nie podejrzewa, że może to być niebezpieczne. Nietrudno zauważyć, że ten kod może zostać wykorzystany do stworzenia poważnych problemów bezpieczeństwa:Niektórzy ludzie mogą myśleć, że jest to oczywista rekurencja. Nie jest.
EvilException
Konstruktor nie wywołujegetCause()
metodę, tak że wyjątek może być faktycznie rzucony bezpiecznie po wszystkim. WywołaniegetCause()
metody nie spowoduje żadnego zStackOverflowError
nich. Rekurencja znajduje się w normalnie nieoczekiwanymprintStackTrace()
zachowaniu JDK i dowolnej bibliotece innej firmy do debugowania i rejestrowania, która służy do sprawdzania wyjątku. Ponadto prawdopodobne jest, że miejsce, w którym wyjątek jest zgłaszany, znajduje się bardzo daleko od miejsca, w którym jest przetwarzany.W każdym razie, oto kod, który generuje a
StackOverflowError
i nie zawiera w końcu żadnych rekurencyjnych wywołań metod.StackOverflowError
Dzieje pozamain
metody, w JDK naUncaughtExceptionHandler
:źródło
getCause()
metody nie powodujeStackOverflowError
. Polega on na tym, że istnieje kod JDK, który rekurencyjnie wywołujegetCause()
metodę.getCause
na tylko,return this;
ale najwyraźniej Java jest na to zbyt sprytna. Zauważa, że jest to „CIRCULAR REFERENCE
”.StackOverflowError
ponieważ pakiet OpenBSD 5.5 jdk-1.7.0.21p2v0 ma błąd. Nie rzuca sięStackOverflowError
. UderzaSIGSEGV
i zrzuca rdzeń.Linux NASM x86
Spojler:
źródło
ret
C ++ w czasie kompilacji
Ten plik źródłowy nie ma rekurencji, żadna z klas nie ma się jako klasa podstawowa, nawet pośrednio. (W C ++, w takiej klasie szablonów
S<1>
iS<2>
są to zupełnie odrębne klasy.) Błąd segmentacji wynika z przepełnienia stosu po rekurencji w kompilatorze.źródło
template <typename T> auto foo(T t) -> decltype(foo(t)); decltype(foo(0)) x;
jest nieco krótszy.Bash (Danger Alert)
Ściśle mówiąc, nie spowoduje to bezpośredniego przepełnienia stosu, ale generuje coś, co może być oznaczone jako „ trwała sytuacja generowania przepełnienia stosu ”: gdy uruchomisz to, aż dysk będzie pełny i chcesz usunąć bałagan za pomocą „rm -rf x ”, ten został trafiony.
Jednak nie dzieje się tak na wszystkich systemach. Niektóre są bardziej wytrzymałe niż inne.
Duże niebezpieczeństwo OSTRZEŻENIE:
niektóre systemy radzą sobie z tym bardzo źle i możesz mieć bardzo trudny czas do czyszczenia (ponieważ sam „rm -rf” napotka problem z reusion). Być może będziesz musiał napisać podobny skrypt do czyszczenia.
Lepiej spróbuj tego na maszynie wirtualnej typu scratch, jeśli nie jesteś pewien.
PS: to samo dotyczy oczywiście, jeśli jest zaprogramowane lub wykonane w skrypcie wsadowym.
PPS: uzyskanie komentarza od ciebie może być interesujące, jak zachowuje się twój system ...
źródło
while cd x; do :; done; cd ..; while rmdir x; cd ..; done;
powinno zająć się tym.Jawa
Fajny od Java Puzzlers . Co to drukuje?
W rzeczywistości kończy się niepowodzeniem w przypadku StackOverflowError.
Wyjątkiem w konstruktorze jest tylko czerwony śledź. Oto co mówi o tym książka:
źródło
Lateks
Stos wejściowy przepełnia się, ponieważ
\end
wielokrotnie rozwija się w nieskończonej pętli, jak wyjaśniono tutaj .TeX nie działa z
TeX capacity exceeded, sorry [input stack size=5000]
podobnym lub podobnym.źródło
BF
W końcu przepełni stos, zależy tylko od tego, jak długo interpreter utworzy stos ...
źródło
C #, w czasie kompilacji
Istnieje wiele sposobów, aby kompilator Microsoft C # wysadził stos; za każdym razem, gdy zobaczysz błąd „wyrażenie jest zbyt skomplikowane, aby go skompilować” z kompilatora C #, który prawie na pewno jest spowodowany przepaleniem stosu.
Analizator składni jest zejściem rekurencyjnym, więc każda wystarczająco głęboko zagnieżdżona struktura języka zniszczy stos:
Parser wyrażeń jest dość sprytny w eliminowaniu rekurencji po stronie, która jest często powtarzana. Zazwyczaj:
który buduje niezwykle głębokie drzewo parsowania, nie wysadzi stosu. Ale jeśli wymusisz rekurencję po drugiej stronie:
wtedy stos może zostać zdmuchnięty.
Mają one nieelegacyjną właściwość, że program jest bardzo duży. Możliwe jest również włączenie analizatora semantycznego w nieograniczoną rekurencję za pomocą małego programu, ponieważ nie jest on wystarczająco inteligentny, aby usunąć pewne nieparzyste cykle w systemie typów. (Roslyn może to poprawić.)
W tym miejscu opisuję, dlaczego ta analiza przechodzi w nieskończoną rekurencję:
http://blogs.msdn.com/b/ericlippert/archive/2008/05/07/covariance-and-contravariance-part-twelve-to-infinity-but-not-beyond.aspx
i dla wielu bardziej interesujących przykładów powinieneś przeczytać ten artykuł:
http://research.microsoft.com/en-us/um/people/akenn/generics/FOOL2007.pdf
źródło
fatal error CS1647: An expression is too long or complex to compile near (code)
. Dokumentacja tego komunikatu o błędzie znajduje się tutaj i jest dokładnie taka, jak mówisz: „W kompilatorze przetwarzającym kod wystąpił przepełnienie stosu”.W Internecie (z którego korzysta miliard osób dziennie)
Na przykład w witrynie pomocy technicznej firmy Dell (bez obrazy, przepraszam, Dell):
Jeśli usuniesz TAG obsługi z adresu URL, przejdzie on w nieskończone przekierowania . W następującym adresie URL ###### jest dowolnym tagiem pomocniczym.
http://www.dell.com/support/drivers/uk/en/ukdhs1/ServiceTag/######?s=BSD&~ck=mn
Uważam, że jest to równoważne z przepełnieniem stosu.
źródło
/Errors/
znaków do adresu URL, a następnie zatrzymuje się po otrzymaniu nieprawidłowego żądania HTTP 400. Ale to prawdopodobnie zapewnia lepsze przepełnienie stosu niż nieskończone przekierowanie.http://www.dell.com/support/drivers/uk/en/ukdhs1/ServiceTag/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/...
PHP
Przepełnienie stosu wykonane tylko z zapętlonymi elementami.
Objaśnienie (najedź kursorem, aby zobaczyć spoiler):
źródło
while (1) { $a = array(&$a); }
coś podobnego właśnieJawa
Zrobiłem dokładnie odwrotnie - program, który oczywiście powinien zgłaszać błąd przepełnienia stosu, ale tego nie robi.
Wskazówka: program działa w czasie O (2 n ), a n jest rozmiarem stosu (zwykle 1024).
Z Java Puzzlers # 45:
źródło
finally
zamiastcatch
, a czas działania wynosi O (2 ^ n). Odpowiedź zaktualizowana.DO#
Pierwszy post, więc spokojnie.
To po prostu tworzy ślad stosu, chwyta górną ramkę (która będzie naszym ostatnim wywołaniem
Main()
), pobiera metodę i wywołuje ją.źródło
Jawa
printStackTrace()
wchodzi w nieskończoną pętlę.printStackTrace()
rzucaStackOverflowError
.Szaloną rzeczą jest to, że w Javie 5 i 6 nie pochodzi ona od kodu użytkownika, dzieje się to w kodzie JDK. Nikt nie ma podejrzeń,
printStackTrace()
których wykonanie byłoby niebezpieczne.źródło
InnerException
właściwość jest tylko do odczytu i jest ustawiona w konstruktorze, więc odbicie jest konieczne, aby to spowodować.JavaScript: Nierekurencyjna, iteracyjna mutacja funkcji
W ogóle nie ma tu rekurencji.
f
będzie wielokrotnie curry z coraz większą liczbą argumentów, aż będzie mógł przepełnić stos w jednym wywołaniu funkcji. Taconsole.log
część jest opcjonalna, jeśli chcesz zobaczyć, ile argumentów potrzeba, aby to zrobić. Zapewnia również, że sprytne silniki JS nie zoptymalizują tego.Wersja Code-golf w CoffeeScript, 28 znaków:
źródło
C # z epicką porażką
Sposób, w jaki zawodzi, jest epicki, całkowicie zaskoczył mnie:
To tylko jedna klatka z pozornie nieskończonej serii dziwnych zdjęć.
To musi być najdziwniejsza rzecz na świecie. Czy ktoś może wyjaśnić? Najwyraźniej coraz większa liczba spacji wykorzystywanych do wcięć powoduje pojawienie się białych bloków. Zdarza się to na Win7 Enterprise x64 z .NET 4.5.
Właściwie nie widziałem jeszcze końca. Jeśli zastąpi
System.Console.Out
sięSystem.IO.Stream.Null
, że dość szybko umiera.Wyjaśnienie jest dość proste. Tworzę klasę, która ma jedną właściwość i zawsze zwraca nową instancję tego typu zawierającego. Jest to więc nieskończenie głęboka hierarchia obiektów. Teraz potrzebujemy czegoś, co próbuje to odczytać. Tam właśnie używam
XmlSerializer
, co właśnie to robi. I najwyraźniej wykorzystuje rekurencję.źródło
grzmotnąć
Choć wielu może uznać, że rekurencja jest oczywista , ale wydaje się ładna. Nie?
Po wykonaniu masz gwarancję, że zobaczysz:
źródło
_(){_|_;};_
{
poprawności składniowej?):(){:|:;}:
Haskell
(smutne, ale prawdziwe, przynajmniej dopóki
ghc-7.6
, chociaż zO1
lub więcej to zoptymalizuje problem)źródło
sum
jest realizowany w warunkachfoldl
, które nie używać połączeń ogon, ale dlatego, że nie ocenia akumulator ściśle jedynie produkuje stos łącznikami jak duże jak na pierwotnej liście. Problem znika po przełączeniu nafoldl' (+)
, który ocenia ściśle, a tym samym zwraca WHN w wywołaniu ogona. Lub, jak powiedziałem, jeśli włączysz optymalizacje GHC!Pogawędka
To tworzy nową metodę w locie, która
tworzy nową metodę w locie, która
tworzy nową metodę w locie, która
...
...
..
a następnie zostaje przeniesiona na nią.
Dodatkowa mała przyprawa pochodzi z jednoczesnego stresowania pamięci stosu i pamięci stosu, tworząc zarówno dłuższą i dłuższą nazwę metody, jak i ogromną liczbę jako odbiornik, gdy spadamy z dziury ... (ale rekurencja uderza nas pierwsza ).
skompiluj w liczbie całkowitej:
następnie skacz, oceniając
"2 downTheRabbitHole"
...... po chwili skończysz w debuggerze, pokazując wyjątek RecursionException.
Następnie musisz wyczyścić cały bałagan (zarówno SmallInteger, jak i LargeInteger mają teraz dużo kodu z krainy czarów):
albo spędzić trochę czasu w przeglądarce, usuwając krainę Alicji.
Oto niektóre z głowy śladu:
PS: dodano „withoutUpdatingChangesFile:”, aby uniknąć konieczności czyszczenia trwałego pliku dziennika zmian Smalltalk.
PPS: dzięki za wyzwanie: myślenie o czymś nowym i innowacyjnym było zabawą!
PPPS: Chciałbym zauważyć, że niektóre dialekty / wersje Smalltalk kopiują przepełnione ramki stosu na stos - więc mogą one zamiast tego wystąpić w sytuacji braku pamięci.
źródło
DO#
Naprawdę duży
struct
, bez rekurencji, czysty C #, niezabezpieczony kod.jako kicker powoduje awarię okien debugowania stwierdzając, że
{Cannot evaluate expression because the current thread is in a stack overflow state.}
I wersja ogólna (dzięki za sugestię NPSF3000)
źródło
DO#
Wadliwe wdrożenie nadpisanego
==
operatora:Można powiedzieć, że to oczywiste, że
operator==
wywołuje się za pomocą==
operatora, ale zwykle nie myślisz w ten sposób==
, więc łatwo wpaść w tę pułapkę.źródło
Uruchamianie odpowiedzi przy użyciu SnakeYAML
Edycja: odkopano go
Od czytelnika zależy, jak to działa: P (wskazówka: stackoverflow.com)
Nawiasem mówiąc: rekurencja jest wykonywana dynamicznie przez SnakeYAML (zauważysz, jeśli wiesz, jak wykrywa pola, które serializuje, i patrzy na
Point
kod źródłowy)Edycja: mówiąc, jak to działa:
SnakeYAML szuka pary
getXXX
isetXXX
metod o tej samej nazwie,XXX
a zwracany typ gettera jest taki sam jak parametr settera; i, co zaskakujące,Point
klasa maPoint getLocation()
a,void setLocation(Point P)
które zwraca się; SnakeYAML tego nie zauważa i powtarza się w tym dziwactwie i przepływach StackOver. Odkryłem to podczas pracy z nimi wHashMap
ai pytaniu na stackoverflow.com.źródło
DO#
niepoprawnie zaimplementowany moduł pobierania właściwości
źródło
static void Main() { Main(); }
Main()
przypadkowo rekurencyjnego . Ale dość łatwo jest przypadkowo napisać właściwość rekurencyjną, a następnie zostać zdezorientowanym przez przepełnienie stosu.