Il modo più strano per produrre un overflow dello stack [chiuso]


146

Come programmatore conosci certamente l'errore di un overflow dello stack dovuto a un'ovvia ricorsione. Ma ci sono sicuramente molti modi strani e insoliti per far sputare la tua lingua preferita a quell'errore.

obiettivi:

  1. Deve causare un overflow dello stack chiaramente visibile sull'output dell'errore.
  2. Non è consentito utilizzare una ricorsione evidente.

Esempi di programmi non validi:

// Invalid, direct obvious recursion.
methodA(){ methodA(); }
// Invalid, indirect, but obvious recursion.
methodA(){ methodB(); }
methodB(){ methodA(); }

I modi più creativi sono i migliori in quanto questo è un . Vale a dire, evitare noiose risposte ovvie come questa:

throw new StackOverflowError(); // Valid, but very boring and downvote-deserving.

Anche se ho accettato una risposta ora, l'aggiunta di più risposte è ancora ok :)


14
Tendo a produrre navigando su stackoverflow.com, sebbene sia noto che eseguo query su "overflow dello stack" sul mio motore di ricerca preferito.
OJFord,

21
Usa Internet Explorer. Un modo sicuro per
prenderne

64
Il modo più strano per produrre un overflow dello stack è pubblicare un contest di popolarità su codegolf.stackexchange.com chiedendo alle persone di pubblicare il modo più strano per produrre un overflow dello stack. I rispondenti, nel testare le loro soluzioni alla domanda, produrranno un overflow dello stack. Non l'ho ancora testato, quindi non posso essere sicuro che funzioni (motivo per cui non l'ho pubblicato come risposta).
Tim Seguine,

3
Sono parziale di questo metodo: joelonsoftware.com/items/2008/09/15.html
robert

11
Guida una Toyota (Ehi, aspetta un minuto, la mia macchina è una Toyota ...)
ossifrage schifoso

Risposte:


224

Pitone

import sys
sys.setrecursionlimit(1)

Ciò farà fallire immediatamente l'interprete:

$ cat test.py
import sys
sys.setrecursionlimit(1)
$ python test.py
Exception RuntimeError: 'maximum recursion depth exceeded' in <function _remove at 0x10e947b18> ignored
Exception RuntimeError: 'maximum recursion depth exceeded' in <function _remove at 0x10e8f6050> ignored
$ 

Invece di usare la ricorsione, riduce semplicemente lo stack in modo da traboccare immediatamente.


12
A mio avviso carino, ma non del tutto a cui puntava la domanda originale.
Nit

12
@Non non vedo il problema. Che dire di questa soluzione non è soddisfacente?
SimonT,

17
@ masterX244 Sì, il punto centrale della domanda è "non farlo nel solito modo".
Plutor,

24
@Plutor di solito imposti uno StackOverFlow di proposito?
Kiwy,

15
Questo è stato solo leggermente intelligente la prima volta che è stato pubblicato .
primo


131

C / Linux 32 bit

void g(void *p){
        void *a[1];
        a[2]=p-5;
}
void f(){
        void *a[1];
        g(a[2]);
}
main(){
        f();
        return 0;
}

Funziona sovrascrivendo l'indirizzo di ritorno, quindi gtorna al punto mainprima di chiamare f. Funzionerà per qualsiasi piattaforma in cui gli indirizzi di ritorno sono nello stack, ma potrebbe richiedere modifiche.

Naturalmente, scrivere al di fuori di un array è un comportamento indefinito e non hai alcuna garanzia che causerà un overflow dello stack piuttosto che, diciamo, dipingi i baffi in blu. I dettagli dei flag di piattaforma, compilatore e compilation possono fare una grande differenza.


1
Questo non produrrebbe un segfault?
11684,

4
+1 per la strana manipolazione dello stack e assolutamente nessuna ricorsione!
RSFalcon7,

6
@ 11684, È un comportamento indefinito, quindi in generale potrebbe bloccarsi. Su Linux a 32 bit (su cui ho provato), scrive all'esterno dell'array, sovrascrivendo l'indirizzo di ritorno e non si arresta in modo anomalo fino a quando lo stack non trabocca.
ugoren,

46
"Dipingi i baffi blu" -> Quella linea mi aveva.
Aneesh Dogra,

2
Wow. Questo è incredibilmente offuscato.
MirroredFate

108

JavaScript / DOM

with (document.body) {
    addEventListener('DOMSubtreeModified', function() {
        appendChild(firstChild);
    }, false);

    title = 'Kill me!';
}

Se vuoi uccidere il tuo browser, provalo nella console.


53
Sono sicuro che meriti più +1 voti, ma purtroppo le persone hanno provato questo prima di votare .... lol
Reactgular,

5
Bene, è stato efficace. Non riuscivo nemmeno ad aprire il mio task manager per ucciderlo.
primo

1
Usa Chrome, chiudi la scheda. Problema risolto.
Cole Johnson,

1
with (document.body) { addEventListener('DOMSubtreeModified', function() { appendChild(firstChild); }, false); title = 'Kill me!'; } 15:43:43.642 TypeError: can't convert undefined to object
Brian Minton,

1
Accidenti a Firefox è stato impiccato
Farhad,

91

Giava

Ho visto qualcosa del genere da qualche parte qui intorno:

Modifica: trovato dove l'ho visto: la risposta di Joe K al programma più breve che genera l'errore StackOverflow

public class A {
    String val;
    public String toString() {
        return val + this;
    }

    public static void main(String[] args) {
        System.out.println(new A());
    }
}

Ciò può confondere alcuni principianti di Java. Nasconde semplicemente la chiamata ricorsiva. val + thisdiventa val + this.toString()perché valè una stringa.

Guardalo correre qui: http://ideone.com/Z0sXiD


30
In realtà, lo fa new StringBuilder().append(val).append("").append(this).toString(), e l'ultima append chiama String.valueOf (...), che a sua volta chiama toString. Questo rende la traccia dello Stack un po 'varia (tre metodi lì dentro).
Paŭlo Ebermann,

2
@ PaŭloEbermann Sì, è corretto. Tuttavia, è molto più facile dire che diventa "" + this.toString().
Giustino,

2
Penso che + "" +potrebbe dare la mancia alle persone, poiché a prima vista sembra inutile. String val;e return val + this;potrebbe essere leggermente più subdolo
Cruncher,

@ Cruncher affatto. Se sei un programmatore Java, sapresti che il modo semplice di concatenare un int a una stringa durante la ""costruzione di -String è con+ ""
Justin

5
In un'applicazione del mondo reale preferirei evitare ricorsioni infinite e errori di overflow dello stack
jon_darkstar

77

C

Abbastanza semplice:

int main()
{
    int large[10000000] = {0};
    return 0;
}

10
+1 per non ovvio! Nonostante sia molto dipendente dal sistema (un ulimit -s unlimitednella shell risolve questo in Linux)
RSFalcon7,

4
@ RSFalcon7, grazie per il +1, ma per me questo era in realtà il più ovvio !!
Shahbaz,

14
@Cruncher: non provoca ricorsione. Il problema è stato quello di far esplodere la pila. Su molti sistemi operativi lo stack è di dimensioni fisse e molto più piccolo di dieci milioni di pollici, quindi questo fa esplodere lo stack.
Eric Lippert,

2
@HannoBinder, in Linux dove ho provato, non si verifica un errore di overflow dello stack. Si ottiene un errore di segmentazione, poiché l'overflow dello stack comporta l'accesso a segmenti non proprietari. Non sono sicuro che in Linux esista un errore di overflow dello stack, poiché una chiamata di funzione ricorsiva infinita genera anche un errore di segmentazione.
Shahbaz,

3
~0uè un numero abbastanza grande in C.
Vortico,

63

Stack over non ricorsivo in C

Richiamo della mancata corrispondenza della convenzione.

typedef void __stdcall (* ptr) (int);

void __cdecl hello (int x) { }

void main () {
  ptr goodbye = (ptr)&hello;
  while (1) 
    goodbye(0);
}

Compila con gcc -O0.

__cdeclle funzioni si aspettano che il chiamante ripulisca lo stack e si __stdcallaspetta che il chiamante lo faccia, quindi chiamando il puntatore della funzione typecast, il cleanup non viene mai eseguito - maininserisce il parametro nello stack per ogni chiamata ma nulla lo apre e alla fine il riempimenti a pila.


2
bel modo di spammare lo stack: P
masterX244,

62

JavaScript

window.toString = String.toLocaleString;
+this;

5
Questo è sottovalutato.
Ry,

1
Cosa fa +this?
NobleUplift,

8
Unary + richiama l'operazione astratta ToNumber. Che utilizza l'operazione ToPrimitive con un tipo di suggerimento di numero. ToPrimitive su un oggetto utilizza il metodo interno [[DefaultValue]], che quando viene passato un suggerimento numerico, controlla innanzitutto se esiste il metodo valueOf () e restituisce una primitiva. window.valueOf () restituisce un oggetto, quindi [[DefaultValue]] restituisce il risultato della chiamata a String (). La prima cosa che fa window.toString (ora che è toLocaleString) è invocare toString su 'this'. Ripetere.
Ryan Cavanaugh,

3
Se Chrome genera un errore, "Troppa ricorsione", allora funziona su Chrome, giusto? Lo stack trabocca ed è così che Chrome offre un'eccezione di overflow dello stack.
David Conrad,

4
Dovresti usare +{toString:"".toLocaleString}:-)
Bergi

62

Ero frustrato dal fatto che java 7 e java 8 siano immuni al mio codice malvagio nella mia risposta precedente . Così ho deciso che era necessaria una patch per quello.

Successo! Ho fatto printStackTrace()lanciare a StackOverflowError. printStackTrace()viene comunemente utilizzato per il debug e la registrazione e nessuno sospetta ragionevolmente che possa essere pericoloso. Non è difficile vedere che questo codice potrebbe essere abusato per creare alcuni gravi problemi di sicurezza:

public class StillMoreEvilThanMyPreviousOne {
    public static void main(String[] args) {
        try {
            evilMethod();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void evilMethod() throws Exception {
        throw new EvilException();
    }

    public static class EvilException extends Exception {
        @Override
        public Throwable getCause() {
            return new EvilException();
        }
    }
}

Alcune persone potrebbero pensare che si tratti di una ovvia ricorsione. Non è. Il EvilExceptioncostruttore non chiama il getCause()metodo, quindi l'eccezione può effettivamente essere lanciata in modo sicuro dopo tutto. La chiamata al getCause()metodo non comporterà StackOverflowErrorneanche uno. La ricorsione è all'interno del printStackTrace()comportamento normalmente insospettato di JDK e di qualsiasi libreria di terze parti per il debug e la registrazione utilizzata per ispezionare l'eccezione. Inoltre, è probabile che il luogo in cui viene generata l'eccezione sia molto lontano dal luogo in cui viene gestita.

Ad ogni modo, ecco un codice che genera un StackOverflowErrore non contiene chiamate di metodo ricorsive dopo tutto. Il StackOverflowErroravviene al di fuori del mainmetodo, in JDK di UncaughtExceptionHandler:

public class StillMoreEvilThanMyPreviousOneVersion2 {
    public static void main(String[] args) {
        evilMethod();
    }

    private static void evilMethod() {
        throw new EvilException();
    }

    public static class EvilException extends RuntimeException {
        @Override
        public Throwable getCause() {
            return new EvilException();
        }
    }
}
Exception: java.lang.StackOverflowError thrown from the UncaughtExceptionHandler in thread "main"

2
: D e ora con un metodo conforme allo stackoverflow versione Java. Bastardo cattivo! : D
Kiwy,

9
Sono propenso a considerare ovvia la ricorsione in questo caso.
Taemyr,

1
@BlacklightShining La chiamata del getCause()metodo non provoca StackOverflowError. Si basa sul fatto che esiste un codice JDK che chiama in modo ricorsivo il getCause()metodo.
Victor Stafusa,

2
Ho pensato che potresti semplificarlo cambiando il corpo getCausein giusto return this;ma apparentemente Java è troppo intelligente per quello. Si accorge che è un " CIRCULAR REFERENCE".
David Conrad,

1
Non ho ricevuto un StackOverflowErrorperché il pacchetto OpenBSD 5.5 di jdk-1.7.0.21p2v0 ha un bug. Non getta StackOverflowError. Colpisce SIGSEGVe scarica il core.
kernigh,

57

Assemblaggio NASM Linux x86

section .data
    helloStr:     db 'Hello world!',10 ; Define our string
    helloStrLen:  equ $-helloStr       ; Define the length of our string

section .text
    global _start

    doExit:
        mov eax,1 ; Exit is syscall 1
        mov ebx,0 ; Exit status for success
        int 80h   ; Execute syscall

    printHello:
        mov eax,4           ; Write syscall is No. 4
        mov ebx,1           ; File descriptor 1, stdout
        mov ecx,helloStr    ; Our hello string
        mov edx,helloStrLen ; The length of our hello string
        int 80h             ; execute the syscall

    _start:
        call printHello ; Print "Hello World!" once
        call doExit     ; Exit afterwards

spoiler:

Dimentica di tornare da printHello, quindi saltiamo di nuovo a _start di nuovo.


78
In assemblea, nulla è ovvio.
11684,

21
@ 11684: Trovo che sia vero il contrario: nell'assemblaggio, tutto è ovvio perché nessuno può usare le astrazioni per nascondere ciò che il loro codice sta realmente facendo.
Mason Wheeler,

3
Questo è geniale per la sua semplicità ed eleganza.
Haneefmubarak,

11
@MasonWheeler: Preferisco dire che tutto è visibile anziché ovvio ... Per un bel modo di vedere la differenza tra visibile e ovvio, adoro fare riferimento a underhanded.xcott.com
Olivier Dulac,

2
Ricordo i miei frequenti errori di dimenticareret
Ruslan,

48

C ++ al momento della compilazione

template <unsigned N>
struct S : S<N-1> {};

template <>
struct S<0> {};

template
struct S<-1>;
$ g ++ -c test.cc -ftemplate-depth = 40000
g ++: errore interno del compilatore: errore di segmentazione (programma cc1plus)
Invia una segnalazione di bug completa,
con sorgente preelaborata, se del caso.
Vedi per istruzioni.

Non c'è ricorsione in questo file sorgente, nessuna delle classi ha se stessa come classe base, neppure indirettamente. (In C ++, in una classe modello come questa, S<1>e S<2>sono classi completamente distinte.) L'errore di segmentazione è dovuto allo overflow dello stack dopo la ricorsione nel compilatore.


7
Confesso che avrei definito questa ovvia ricorsione nel tuo metaprogramma.

2
GCC rileva la ricorsione e si ferma con grazia da questa parte (4.8 e superiori sembrano andare bene)
Alec Teal,

2
template <typename T> auto foo(T t) -> decltype(foo(t)); decltype(foo(0)) x;è un po 'più corto.
Casey,

2
@hvd Sembra che stia sfruttando un bug in GCC. clang rileva l'utilizzo errato - di cui presumo tu sia già a conoscenza - ma fa sì che il mio GCC emetta quasi 2 megabyte di messaggi di errore.
Casey,


45

Bash (avviso di pericolo)

while true
do 
  mkdir x
  cd x
done

A rigor di termini, ciò non impilerà direttamente l'overflow, ma genera ciò che può essere etichettato come " una situazione generatrice di stack overflow persistente ": quando si esegue questo fino a quando il disco è pieno e si desidera rimuovere il disordine con "rm -rf x ", quello viene colpito.

Tuttavia, non accade su tutti i sistemi. Alcuni sono più robusti di altri.

Grande pericolo AVVERTENZA:

alcuni sistemi lo gestiscono molto male e potresti avere difficoltà a ripulire (perché "rm -rf" stesso avrà un problema di recusione). Potrebbe essere necessario scrivere uno script simile per la pulizia.

Meglio provare questo in un VM scratch se non ne sono sicuro.

PS: lo stesso vale, ovviamente, se programmato o eseguito in uno script batch.
PPS: potrebbe essere interessante ricevere un commento da te, come si comporta il tuo sistema particolare ...


come ho scritto: su molti sistemi, rm -rf prova a scendere e viene colpito da uno (forse non più, in questi giorni, con spazio di indirizzi a 64 bit - ma su macchine più piccole con uno stack più piccolo, potrebbe). Certo, potrebbero esserci anche "rm" implementazioni in giro, che lo fanno diversamente ...
blabla999,

2
Mi sembra che qualcosa del genere while cd x; do :; done; cd ..; while rmdir x; cd ..; done;dovrebbe occuparsene.
Blacklight Shining

Hai assolutamente ragione (è quello che volevo dire con "script simili per la pulizia"). Tuttavia, una volta che il disco (o la quota) è pieno, potresti anche avere difficoltà ad accedere in seguito, poiché alcuni sistemi erano soliti affrontare tale situazione. Dovrai entrare come root e fare lo script (che è facile su quei PC di oggi, ma spesso era difficile nei tempi antichi, quando i computer venivano usati da più di un utente).
blabla999,

divertente, questo ottiene più voti della magia Smalltalk qui sotto (mai pensato!)
blabla999,

qualcuno l'ha provato su Windows?
Sarge Borsch,

43

Giava

Una bella di Java Puzzlers . Che cosa stampa?

public class Reluctant {
    private Reluctant internalInstance = new Reluctant();

    public Reluctant() throws Exception {
        throw new Exception("I'm not coming out");
    }

    public static void main(String[] args) {
        try {
            Reluctant b = new Reluctant();
            System.out.println("Surprise!");
        } catch (Exception ex) {
            System.out.println("I told you so");
        }
    }
}

In realtà non riesce con StackOverflowError.

L'eccezione nel costruttore è solo un'aringa rossa. Questo è ciò che il libro ha da dire al riguardo:

Quando si richiama un costruttore, gli inizializzatori di variabili di istanza vengono eseguiti prima del corpo del costruttore . In questo caso, l'inizializzatore per la variabile internalInstanceinvoca ricorsivamente il costruttore. Quel costruttore, a sua volta, inizializza il proprio internalInstancecampo invocando di nuovo il costruttore Riluttante e così via, all'infinito. Queste invocazioni ricorsive fanno sì che StackOverflowErrorprima che il corpo del costruttore abbia la possibilità di eseguire. Perché StackOverflowErrorè un sottotipo di Errorpiuttosto che Exception, la clausola catch in mainnon la cattura.


41

LaTeX

\end\end

Lo stack di input trabocca perché \endsi espande ripetutamente in un ciclo infinito, come spiegato qui .

TeX fallisce con un TeX capacity exceeded, sorry [input stack size=5000]o simile.


1
Stavo aspettando un problema con TeX / LaTeX. Quelle cose sono più noiose della maggior parte - di solito, ho dimenticato che ciò che sto facendo conta come una programmazione quando improvvisamente sono riuscito a scrivere qualcosa che è infinitamente ricorsivo.
Ernir,


1
"La capacità di TeX è stata superata, scusate" TeX è così gentile quando fallisce.
Alex A.

40

BF

Alla fine traboccerà lo stack, dipende solo da quanto l'interprete fa lo stack ...

+[>+]

5
Quel codice mi ricorda il gioco della vita.
Adam Arold,

10
A rigor di termini, non c'è stack in Brainfuck.
fejesjoco,

6
@fejesjoco Tape, se lo desideri.
Timtech,

32

C #, al momento della compilazione

Esistono diversi modi per fare in modo che il compilatore Microsoft C # esploda il suo stack; ogni volta che viene visualizzato un errore "espressione troppo complessa per la compilazione" dal compilatore C # che è quasi certamente dovuto al fatto che lo stack è saltato.

Il parser è una discesa ricorsiva, quindi tutte le strutture linguistiche sufficientemente annidate faranno impazzire:

 class C { class C { class C { ....

Il parser di espressioni è piuttosto intelligente nell'eliminare le ricorsioni sul lato che è comunemente ricorsivo. Generalmente:

x = 1 + 1 + 1 + 1 + .... + 1;

che costruisce un albero di analisi enormemente profondo, non farà esplodere la pila. Ma se si forza la ricorsione ad avvenire dall'altra parte:

x = 1 + (1 + (1 + (1 + ....+ (1 + 1))))))))))))))))))))))))))))))))))))))))))...;

quindi la pila può essere saltata.

Questi hanno la proprietà inelegante che il programma è molto grande. È anche possibile trasformare l'analizzatore semantico in ricorsioni illimitate con un piccolo programma perché non è abbastanza intelligente da rimuovere determinati cicli dispari nel sistema dei tipi. (Roslyn potrebbe migliorare questo.)

public interface IN<in U> {}
public interface IC<X> : IN<IN<IC<IC<X>>>> {}
...
IC<double> bar = whatever;
IN<IC<string>> foo = bar;  // Is this assignment legal? 

Descrivo perché questa analisi va in una ricorsione infinita qui:

http://blogs.msdn.com/b/ericlippert/archive/2008/05/07/covariance-and-contravariance-part-twelve-to-infinity-but-not-beyond.aspx

e per molti altri esempi interessanti dovresti leggere questo documento:

http://research.microsoft.com/en-us/um/people/akenn/generics/FOOL2007.pdf


2
Il messaggio di errore esatto è fatal error CS1647: An expression is too long or complex to compile near (code). La documentazione per questo messaggio di errore è qui ed è esattamente come dici tu: "Si è verificato un overflow dello stack nel compilatore che elabora il tuo codice".
bwDraco,

32

Su Internet (utilizzato da miliardi di persone / giorno)

Redirects, HTTP status code: 301

Ad esempio, sul sito Web dell'assistenza Dell (senza offesa, scusa Dell):

Se si rimuove il TAG di supporto dall'URL, si passa a reindirizzamenti infiniti . Nel seguente URL, ###### è qualsiasi TAG di supporto.

http://www.dell.com/support/drivers/uk/en/ukdhs1/ServiceTag/######?s=BSD&~ck=mn

Credo che sia equivalente a un overflow dello stack.


5
bel modo :) firefox dice che reindirizza quindi la richiesta non può essere risolta quale è lo stackoverflow rquivalent :)
masterX244

3
Si noti che i reindirizzamenti non sono infiniti: se si preme "Riprova", vengono aggiunti altri /Errors/URL all'URL e quindi si interrompe dopo aver ricevuto HTTP 400 Bad Request. Ma questo probabilmente rende un overflow dello stack migliore rispetto a un reindirizzamento infinito.
nandhp,

@nandhp, sono d'accordo, ma se pensi a un browser del terzo mondo (non moderno, IE ecc.), Non hanno idea di questa situazione.
codeSetter

2
Ecco come Wget risponde qui: pastebin.com/pPRktM1m
bwDraco,

1
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/...
Vortico,

31

PHP

Uno stackoverflow fatto solo con elementi di loop.

$a = array(&$a);
while (1) {
    foreach ($a as &$tmp) {
        $tmp = array($tmp, &$a);
    }
}

Spiegazione (passa con il mouse per vedere lo spoiler):

Il programma segfault quando l'interprete tenta di eseguire il garbage collection $tmpdell'array (durante la riassegnazione $tmpqui). Solo perché l'array è troppo profondo (autoreferenziazione) e quindi il Garbage Collector finisce in una ricorsione.


16
Il GC di PHP non è in grado di rilevare strutture autoreferenziali? Veramente?! Ahia. Questo è male.
Peter C,

1
Sì, ma non è perfetto. C'è qualche motivo per cui while (1) { $a = array(&$a); }o qualcosa di simile colpisce solo il limite di memoria ...
bwoebi,

sì, penso che sia la stessa ragione per cui PHP non funziona correttamente anche per quanto riguarda lo sviluppo orientato agli oggetti; (astrazione, eredità ecc.)
pythonian29033

1
@ pythonian29033 ti interessa elaborare?
vvondra,

30

Giava

Ho fatto esattamente il contrario: un programma che ovviamente dovrebbe generare un errore di overflow dello stack, ma non lo fa.

public class Evil {
    public static void main(String[] args) {
        recurse();
    }

    private static void recurse() {
        try {
            recurse();
        } finally {
            recurse();
        }
    }
}

Suggerimento: il programma viene eseguito in tempo O (2 n ) e n è la dimensione dello stack (in genere 1024).

Da Java Puzzlers # 45:

Supponiamo che la nostra macchina possa eseguire 10 10 chiamate al secondo e generare 10 10 eccezioni al secondo, il che è abbastanza generoso dagli standard attuali. In base a questi presupposti, il programma terminerà tra circa 1,7 × 10 291 anni. Per mettere questo in prospettiva, la durata del nostro sole è stimata in 10 10 anni, quindi è una scommessa sicura che nessuno di noi sarà in giro per vedere terminare questo programma. Sebbene non sia un ciclo infinito, potrebbe anche esserlo.


3
Evidente ricorsione ... non molto interessante.
Kami,

5
@Kami L'hai provato? Hai effettivamente ricevuto StackOverflowError? Sembra ovvio, ma non lo è.
ntoskrnl,

Solo perché viene rilevata un'eccezione non significa che non sia mai stata lanciata. Questo programma genera un'eccezione di overflow dello stack dopo il tempo O (n)
CodesInChaos,

1
@CodesInChaos Va bene, quindi ho ricontrollato questo, e la versione corretta utilizza finallyinvece di catch, e il tempo di esecuzione è O (2 ^ n). Risposta aggiornata
ntoskrnl,

@ntorkrnl nice one + 1'd; btw ha già compilato lo
stackoverflow del

30

C #

Primo post, quindi per favore andate piano con me.

class Program
{
    static void Main()
    {
        new System.Diagnostics.StackTrace().GetFrame(0).GetMethod().Invoke(null, null);
    }
}

Questo crea semplicemente una traccia dello stack, prende il frame superiore (che sarà la nostra ultima chiamata a Main()), ottiene il metodo e lo invoca.


bel modo di selfinvoke che non è così ovvio senza spiegare; +1
masterX244,

Dovresti commentare quel codice con "cosa potrebbe andare storto?": P
Spaceman,

26

Giava

  • In Java 5, printStackTrace()entra in un ciclo infinito.
  • In Java 6, printStackTrace()genera StackOverflowError.
  • In Java 7 e 8, è stato risolto.

La cosa folle è che in Java 5 e 6, non proviene dal codice utente, succede nel codice JDK. Nessuno sospetta ragionevole che printStackTrace()possa essere pericoloso da eseguire.

public class Bad {
    public static void main(String[] args) {
        try {
            evilMethod();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void evilMethod() throws Exception {
        Exception a = new Exception();
        Exception b = new Exception(a);
        a.initCause(b);
        throw a;
    }
}

7
si; Gli stackoverflows dal nulla sono i migliori amici per il codetrolling e un forte mal di testa nella caccia
masterX244

2
Se qualcuno si chiede, il codice equivalente in C # provoca un ciclo infinito. Ma la InnerExceptionproprietà è di sola lettura ed è impostata nel costruttore, quindi è necessario riflettere per causarlo.
Athari,

17

JavaScript: mutazione della funzione iterativa non ricorsiva

var f = function() {
    console.log(arguments.length);
};

while (true) {
    f = f.bind(null, 1);
    f();
}

Non c'è nessuna ricorsione qui. fverrà ripetutamente scritto con sempre più argomenti fino a quando non sarà in grado di traboccare lo stack in una singola chiamata di funzione. La console.logparte è facoltativa nel caso in cui si desideri vedere quanti argomenti sono necessari per farlo. Assicura inoltre che i motori JS intelligenti non lo ottimizzino.

Versione code-golf in CoffeeScript, 28 caratteri:

f=->
do f=f.bind f,1 while 1

14

C # con un fallimento epico

using System.Xml.Serialization;

[XmlRoot]
public class P
{
    public P X { get { return new P(); } set { } }
    static void Main()
    {
        new XmlSerializer(typeof(P)).Serialize(System.Console.Out, new P());
    }
}

Il modo in cui fallisce è epico, mi ha sconvolto completamente:

inserisci qui la descrizione dell'immagine

È solo un fotogramma di una serie ininterrotta apparentemente infinita di strane immagini.

Questa deve essere la cosa più strana di sempre. Qualcuno può spiegare? Apparentemente, la quantità sempre crescente di spazi utilizzati per il rientro fa apparire quei blocchi bianchi. Succede su un Win7 Enterprise x64 con .NET 4.5.

Non ho ancora visto la fine. Se si sostituisce System.Console.Outcon System.IO.Stream.Null, muore abbastanza veloce.

La spiegazione è piuttosto semplice. Faccio una classe che ha una sola proprietà e restituisce sempre una nuova istanza del suo tipo contenente. Quindi è una gerarchia di oggetti infinitamente profonda. Ora abbiamo bisogno di qualcosa che cerchi di leggerlo. È lì che uso il XmlSerializer, che fa proprio questo. E apparentemente, usa la ricorsione.


si; serializzare ftw: P; ma è più divertente quando la tua stranezza è completamente al di fuori del tuo codice come il modo in cui snakeyaml ottiene gli attributi e una classe ritorna se stessa in un modo che ricorre endlesly
masterX244

1
Bene, l'overflow si verifica al di fuori del mio codice, ma il vero motivo per cui l'ho pubblicato è a causa dell'imprevisto effetto collaterale nella console :-)
fejesjoco,

Penso che i bit bianchi debbano avere a che fare con .Net 4.5 o con il modo in cui il tuo ambiente è impostato, perché usando .Net 4 (anche quando attraverso cmd) ottengo solo spazi, nessun blocco bianco. La mia ipotesi è che la versione Win7 Enterprise di cmd o l'emulatore di console .Net 4.5 trattino una determinata combinazione di caratteri come "cambia lo sfondo del colore della console".
Pharap,

Non riesco a riprodurlo con .NET 4.5 su Win7 x64 Professional con Aero attivato
Ray

Non riesco nemmeno a riprodurre. .NET 4.5, Windows 8.1 Pro.
bwDraco,

13

bash

_(){ _;};_

Mentre molti potrebbero riconoscere che la ricorsione è ovvia , ma sembra carina. No?

Al momento dell'esecuzione, si è certi di vedere:

Segmentation fault (core dumped)

5
questo è più bello e peggio:_(){_|_;};_
RSFalcon7,

1
@ Avviso RSFalcon7 Forkbomb! (Inoltre, non è necessario uno spazio dopo il {per essere sintatticamente corretto?)
Blacklight Shining

3
Prova anche questo:(){:|:;}:
Tarek Eldeeb,

14
Sembra un'emoticon inquietante e mutante.
Tim Seguine,

8
Emoticon Bash: mangiatore di volti, assassino di pile.
NobleUplift,

12

Haskell

(triste ma vero fino almeno ghc-7.6, sebbene con O1o più ottimizzerà il problema via)

main = print $ sum [1 .. 100000000]

non dovrebbe eseguire automaticamente un'ottimizzazione delle chiamate in coda (in somma)?
blabla999,

2
@ blabla999: le chiamate di coda non sono così rilevanti in Haskell, è soprattutto un accumulo di colpi a causa della pigrizia segreta che sta causando tali problemi. In questo caso, il problema è che sumviene implementato in termini di foldl, che utilizza le chiamate di coda, ma perché non valuta l'accumulatore produce rigorosamente solo una pila di thunk grande come l'elenco originale. Il problema scompare quando si passa a foldl' (+), che valuta rigorosamente e quindi restituisce un WHN nella sua coda. O, come ho detto, se attivi le ottimizzazioni di GHC!
cessò di girare in senso antiorario il

aah - interessante, quindi se nessuno aspettasse il thunk (cioè tralasciando la stampa), il garbage collector li raccoglierebbe (da davanti a dietro)?
blabla999,

1
A proposito, nulla di tutto ciò è specificato dallo standard Haskell: è semplicemente richiesto che la valutazione sia non rigorosa , cioè un calcolo non terminante non si bloccherà per sempre se il risultato non è completamente richiesto. Quanto tempo blocca veramente dipende dall'implementazione, in GHC pigro standard non si blocca affatto fino a quando non si richiede il risultato.
cessò di girare in senso antiorario il

2
haskell è bello.
blabla999,

12

Smalltalk

Questo crea un nuovo metodo al volo, che
  crea un nuovo metodo al volo, che
    crea un nuovo metodo al volo, che
      ...
    ...
  ..
e quindi trasferisce ad esso.

Una piccola spezia extra deriva dallo stress della memoria dello stack E dalla memoria dell'heap allo stesso tempo, creando sia un nome di metodo sempre più lungo, sia un numero enorme come ricevitore, mentre cadiamo nel buco ... (ma la ricorsione ci colpisce per prima ).

compilare in Intero:

downTheRabbitHole
    |name deeperName nextLevel|

    nextLevel := self * 2.
    name := thisContext selector.
    deeperName := (name , '_') asSymbol.
    Class withoutUpdatingChangesDo:[
        nextLevel class 
            compile:deeperName , (thisContext method source copyFrom:name size+1).
    ].
    Transcript show:self; showCR:' - and down the rabbit hole...'.
    "/ self halt. "/ enable for debugging
    nextLevel perform:deeperName.

poi salta, valutando "2 downTheRabbitHole"...
... dopo un po ', finirai in un debugger, mostrando una RecursionException.

Quindi devi ripulire tutto il casino (sia SmallInteger che LargeInteger ora hanno molto codice wonderland):

{SmallInteger . LargeInteger } do:[:eachInfectedClass |
    (eachInfectedClass methodDictionary keys 
        select:[:nm| nm startsWith:'downTheRabbitHole_'])
            do:[:each| eachInfectedClass removeSelector:each]

oppure trascorri un po 'di tempo nel browser, rimuovendo il paese delle meraviglie di Alice.

Eccone alcuni dalla testa della traccia:

2 - and down the rabbit hole...
4 - and down the rabbit hole...
8 - and down the rabbit hole...
16 - and down the rabbit hole...
[...]
576460752303423488 - and down the rabbit hole...
1152921504606846976 - and down the rabbit hole...
2305843009213693952 - and down the rabbit hole...
[...]
1267650600228229401496703205376 - and down the rabbit hole...
2535301200456458802993406410752 - and down the rabbit hole...
5070602400912917605986812821504 - and down the rabbit hole...
[...]
162259276829213363391578010288128 - and down the rabbit hole...
324518553658426726783156020576256 - and down the rabbit hole...
[...]
and so on...

PS: è stato aggiunto il file "withoutUpdatingChangesFile:" per evitare di dover successivamente ripulire il file di registro delle modifiche persistente di Smalltalk.

PPS: grazie per la sfida: pensare a qualcosa di nuovo e innovativo è stato divertente!

PPPS: Mi piace notare che alcuni dialetti / versioni di Smalltalk copiano i frame dello stack in eccesso nell'heap, quindi potrebbero invece verificarsi una situazione di memoria insufficiente.


2
LOL +1 per quella magia nera nella sua forma pura
masterX244,

Se trovo un po 'di tempo, potrei "migliorare" usando un metodo anonimo di una classe anonima, per fare la
tana del

12

C #

Davvero grande struct, nessuna ricorsione, C # puro, codice non pericoloso.

public struct Wyern
{
    double a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z;
}
public struct Godzilla
{
    Wyern a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z;
}
public struct Cyclops
{
    Godzilla a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z;
}
public struct Titan
{
    Cyclops a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z;
}
class Program
{
    static void Main(string[] args)
    {
        // An unhandled exception of type 'System.StackOverflowException' occurred in ConsoleApplication1.exe
        var A=new Titan();
        // 26×26×26×26×8 = 3655808 bytes            
        Console.WriteLine("Size={0}", Marshal.SizeOf(A));
    }
}

come kicker si blocca le finestre di debug affermando che {Cannot evaluate expression because the current thread is in a stack overflow state.}


E la versione generica (grazie per il suggerimento NPSF3000)

public struct Wyern<T>
    where T: struct
{
    T a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z;        
}


class Program
{
    static void Main(string[] args)
    {
        // An unhandled exception of type 'System.StackOverflowException' occurred in ConsoleApplication1.exe
        var A=new Wyern<Wyern<Wyern<Wyern<int>>>>();
    }
}

Richiede più
methink di

Sembrerebbe una ricorsione, ma è possibile con argomenti di tipo nidificati.
Ja72,

1
Non ho visto la tua risposta C # struct prima che io pubblicassi la mia. Ho ancora un approccio un po 'diverso, quindi forse li lasciamo coesistere.
Thomas Weller,

11

C #

Implementazione errata ==dell'operatore overriden :

public class MyClass
{
    public int A { get; set; }

    public static bool operator ==(MyClass obj1, MyClass obj2)
    {
        if (obj1 == null)
        {
            return obj2 == null;
        }
        else
        {
            return obj1.Equals(obj2);
        }
    }

    public static bool operator !=(MyClass obj1, MyClass obj2)
    {
        return !(obj1 == obj2);
    }

    public override bool Equals(object obj)
    {
        MyClass other = obj as MyClass;
        if (other == null)
        {
            return false;
        }
        else
        {
            return A == other.A;
        }
    }
}

Si potrebbe dire che è ovvio che si operator==chiama usando l' ==operatore, ma di solito non ci si pensa così ==, quindi è facile cadere in quella trappola.


11

Iniziare la risposta usando SnakeYAML

class A
{

    public static void main(String[] a)
    {
         new org.yaml.snakeyaml.Yaml().dump(new java.awt.Point());
    }
}

Modifica: non modificato

Sta al lettore scoprire come funziona: P (tip: stackoverflow.com)

A proposito: la ricorsione è fatta dinamicamente da SnakeYAML (noterai se sai come rileva i campi che serializza e guarda quindi nel Pointcodice sorgente)

Modifica: raccontando come funziona:

SnakeYAML cerca una coppia di getXXXe setXXXmthod con lo stesso nome XXXe il tipo di ritorno del getter è lo stesso del parametro del setter; e sorprendentemente la Pointclasse ha una Point getLocation()e void setLocation(Point P)che ritorna se stessa; SnakeYAML non se ne accorge e ricorre su quella stranezza e StackOverflow. L' HashMapho scoperto lavorando con loro all'interno di una e chiedendo su stackoverflow.com.


10

C #

getter di proprietà implementato erroneamente

class C
{
   public int P { get { return P; } }
}

static void Main()
{
   int p = new C().P;
}

14
A PARER MIO. Questo è un classico esempio di ovvia ricorsione ... (e quindi non valido)
Ole Albers,

2
Bene, è ovvio solo dopo averlo fatto una volta e capito che i getter di C # non funzionano come potresti aver pensato. Dopotutto, questo codice è una dichiarazione di variabile membro, quindi perché non dovrebbe creare una variabile membro reale?
Meustrus,

2
Questo non è altro che un modo contorto di farestatic void Main() { Main(); }
Jodrell,

5
@Jodrell Non scriveresti ricorsivamente per Main()caso. Ma è abbastanza semplice scrivere accidentalmente una proprietà ricorsiva e quindi essere confuso dall'overflow dello stack.
svick,

1
Questo sarebbe utile per qualcuno che raccoglie C #.
2:00 e
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.