Le plus bizarre moyen de produire un débordement de pile [fermé]

146

En tant que programmeur, vous connaissez certainement l’erreur d’un débordement de pile due à une récursion évidente. Mais il existe certainement de nombreux moyens étranges et inhabituels pour que votre langue préférée crache cette erreur.

Objectifs:

  1. Doit provoquer un débordement de pile clairement visible sur la sortie d'erreur.
  2. Non autorisé à utiliser une récursivité évidente.

Exemples de programmes non valides:

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

Les moyens les plus créatifs sont les meilleurs car il s’agit d’un . Par exemple, évitez les réponses évidentes ennuyeuses comme celle-ci:

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

Même si j'ai accepté une réponse maintenant, ajouter d'autres réponses est toujours correct :)

masterX244
la source
14
J'ai tendance à produire en accédant à stackoverflow.com, même s'il m'a été demandé d'interroger le «débordement de pile» sur le moteur de recherche de mon choix.
OJFord
21
Utilisez Internet Explorer. Un moyen sûr d'en attraper un :)
asgoth
64
Le moyen le plus étrange de produire un débordement de pile consiste à publier un concours de popularité sur codegolf.stackexchange.com, demandant aux utilisateurs de publier le moyen le plus étrange de produire un débordement de pile. En testant leurs solutions à la question, les intervenants produiront un débordement de pile. Je ne l’ai pas testé cependant, donc je ne peux pas être sûr que cela fonctionne (c’est pourquoi je ne l’ai pas posté comme réponse).
Tim Seguine
3
Je suis partisan de
robert le
11
Conduire une Toyota (Hey, attendez une minute, ma voiture est une Toyota ...)
ossifrage

Réponses:

224

Python

import sys
sys.setrecursionlimit(1)

Cela entraînera l'échec immédiat de l'interprète:

$ 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
$ 

Au lieu d'utiliser la récursivité, la pile est réduite afin qu'elle déborde immédiatement.

marinus
la source
12
Mignon, mais pas tout à fait ce que la question initiale visait à mon avis.
Nit
12
@Nit je ne vois pas le problème. Qu'en est-il de cette solution n'est pas satisfaisante?
SimonT
17
@ masterX244 Oui, le but de la question est "ne le faites pas comme d'habitude".
Plutor
24
@Plutor configurez-vous habituellement un StackOverFlow?
Kiwy
15
Ce n'était que légèrement intelligent la première fois qu'il a été posté .
Primo
189

Python

import webbrowser
webbrowser.open("http://stackoverflow.com/")
Le docteur
la source
7
Fantastique. Gentil et élégant.
James Webster
103
Beaucoup littéral. Très réponse.
Tim Seguine
47
Cela montre un débordement de pile, mais pour en produire un, cela ne fonctionne que s’il est exécuté par Jeff Atwood ou Joel Spolsky.
Escargot mécanique
10
@ TimSeguine Wow.
Sean Allred
9
Pas une réponse car ce n'est pas dans la sortie d'erreur.
Pierre Arlaud
131

C / Linux 32bit

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

Fonctionne en écrasant l'adresse de retour, gretourne donc au point mainavant d'appeler f. Cela fonctionnera pour toute plate-forme où les adresses de retour sont sur la pile, mais peuvent nécessiter des ajustements.

Bien sûr, écrire en dehors d'un tableau est un comportement indéfini , et vous n'avez aucune garantie que cela provoquera un débordement de pile au lieu de peindre votre moustache en bleu. Les détails des indicateurs de plate-forme, de compilateur et de compilation peuvent faire toute la différence.

Ugoren
la source
1
Cela ne produirait-il pas un segfault?
11684
4
+1 pour l'étrange manipulation de pile et absolument aucune récursivité!
RSFalcon7
6
@ 11684, c'est un comportement indéfini, donc en général cela pourrait planter. Sous Linux 32 bits (sur lequel j'ai testé), il écrit en dehors du tableau, écrasant l'adresse de retour et ne plante pas jusqu'à ce que la pile déborde.
Ugoren
46
"Peins ta moustache en bleu" -> Cette ligne m'a eu.
Aneesh Dogra
2
Sensationnel. Ceci est fantastiquement obscurci.
MirroredFate
108

JavaScript / DOM

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

    title = 'Kill me!';
}

Si vous voulez tuer votre navigateur, essayez-le dans la console.

Vision
la source
53
Je suis sûr que vous méritez plus de +1 votes, mais malheureusement, les gens l'ont essayé avant de voter .... lol
Reactgular
5
Eh bien, c'était efficace. Je ne pouvais même pas obtenir mon gestionnaire de tâches ouvert pour le tuer.
Primo
1
Utilisez Chrome, fermez l'onglet. Problème résolu.
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
Damn My Firefox a été pendu
Farhad le
91

Java

J'ai vu quelque chose comme ça quelque part ici:

Edit: Trouvé à l'endroit où je l'ai vu: la réponse de Joe K au programme le plus court qui génère une erreur StackOverflow

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

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

Cela peut dérouter certains débutants en Java. Il cache simplement l'appel récursif. val + thisdevient val + this.toString()parce que valest une chaîne.

Voir le courir ici: http://ideone.com/Z0sXiD

Justin
la source
30
En fait, c'est le cas new StringBuilder().append(val).append("").append(this).toString(), et la dernière annexe appelle String.valueOf (...), qui à son tour appelle toString. Cela rend votre trace de pile un peu variée (trois méthodes sont présentées ici).
Paŭlo Ebermann
2
@ PaŭloEbermann Oui, c'est correct. Cependant, il est beaucoup plus facile de dire que cela devient "" + this.toString().
Justin
2
Je pense que cela + "" +pourrait tromper les gens, car cela semble inutile à première vue. String val;et return val + this;pourrait être légèrement sneakier
Cruncher
@ Cruncher pas du tout. Si vous êtes un codeur Java, vous saurez que le moyen le plus simple de concaténer un int avec une chaîne pendant la ""construction de -String consiste à+ ""
Justin
5
Dans une application réelle, je préférerais éviter les erreurs de récursion et de débordement de pile
infinies
77

C

Assez facile:

int main()
{
    int large[10000000] = {0};
    return 0;
}
Shahbaz
la source
10
+1 pour non évident! Malgré cela, il est très dépendant du système (un ulimit -s unlimiteddans le shell résout ce problème sous linux)
RSFalcon7
4
@ RSFalcon7, merci pour le +1, mais pour moi, c'était le plus évident !!
Shahbaz
14
@ Cruncher: Cela ne provoque pas de récursivité. Le problème posé était de faire sauter la pile. Sur de nombreux systèmes d’exploitation, la pile est de taille fixe et bien inférieure à dix millions d’in, ce qui la détruit.
Eric Lippert
2
@HannoBinder, sous Linux où j'ai testé, vous ne recevez pas d'erreur de débordement de pile. Vous obtenez une erreur de segmentation, car un débordement de la pile entraîne l’accès à des segments non possédés. Je ne sais pas si une erreur de débordement de pile existe même sous Linux, puisqu'un appel de fonction récursif infini donne également une erreur de segmentation.
Shahbaz
3
~0uest un assez grand nombre en C.
Vortico
63

Dépassement de pile non récursif en C

Appel incompatibilité de convention.

typedef void __stdcall (* ptr) (int);

void __cdecl hello (int x) { }

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

Compiler avec gcc -O0.

__cdeclfonctions attendent l'appelant pour nettoyer la pile, et __stdcallattend l'appelé à le faire, en appelant par le pointeur de fonction typecast, le nettoyage ne se fait jamais - mainpousse le paramètre sur la pile pour chaque appel , mais rien pops et , finalement , la la pile se remplit.

Jason C
la source
2
bon moyen de spammer la pile: P
masterX244
62

JavaScript

window.toString = String.toLocaleString;
+this;
Ryan Cavanaugh
la source
5
Celui-ci est sous-estimé.
Ry-
1
Que fait +this-il?
NobleUplift
8
Unary + appelle l'opération abstraite ToNumber. Cela utilise l'opération ToPrimitive avec un type d'indice. ToPrimitive sur un objet utilise la méthode interne [[DefaultValue]], qui, lorsqu'elle est transmise à un indicateur de nombre, vérifie d'abord si la méthode valueOf () existe et renvoie une primitive. window.valueOf () renvoie un objet. Par conséquent, [[DefaultValue]] renvoie le résultat de l'appel de toString (). La première chose que fait window.toString (maintenant que c'est toLocaleString) est d'appeler toString sur 'this'. Répéter.
Ryan Cavanaugh
3
Si Chrome génère une erreur "Trop de récursivité", cela fonctionne sur Chrome, n'est-ce pas? La pile déborde, et c’est ainsi que Chrome génère une exception de débordement de pile.
David Conrad
4
Vous devriez utiliser +{toString:"".toLocaleString}:-)
Bergi
62

J'étais frustré par le fait que java 7 et java 8 sont immunisés contre mon code diabolique dans ma réponse précédente . J'ai donc décidé qu'un patch pour cela était nécessaire.

Succès! J'ai fait printStackTrace()jeter un StackOverflowError. printStackTrace()est couramment utilisé pour le débogage et la journalisation et personne ne peut raisonnablement penser que cela pourrait être dangereux. Il n’est pas difficile de voir que ce code pourrait être utilisé pour créer de graves problèmes de sécurité:

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();
        }
    }
}

Certaines personnes peuvent penser qu'il s'agit d'une récursion évidente. Ce n'est pas. Le EvilExceptionconstructeur n'appelle pas la getCause()méthode pour que cette exception puisse être levée en toute sécurité après tout. L'appel de la getCause()méthode n'entraînera pas non StackOverflowErrorplus. La récursivité fait partie du printStackTrace()comportement normalement insoupçonné de JDK et de toute bibliothèque tierce utilisée pour le débogage et la journalisation utilisés pour inspecter l'exception. De plus, il est probable que le lieu où l'exception est levée est très éloigné de celui où il est traité.

Quoi qu'il en soit, voici un code qui jette un StackOverflowErroret ne contient aucun appel de méthode récursif après tout. La StackOverflowErrorpasse en dehors de la mainméthode, dans JDK 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"
Victor Stafusa
la source
2
: D et maintenant avec une méthode compatible stackoverflow version java croisée. Bâtard maléfique! : D
Kiwy
9
Je suis enclin à considérer la récursion comme évidente.
Taemyr
1
@BlacklightShining L'appel de la getCause()méthode n'a pas pour résultat StackOverflowError. Cela repose sur le fait qu'il existe un code du JDK qui appelle de manière récursive la getCause()méthode.
Victor Stafusa
2
Je pensais que vous pourriez simplifier ceci en modifiant getCausesimplement le corps, return this;mais apparemment, Java est trop intelligent pour cela. Il remarque que c'est un " CIRCULAR REFERENCE".
David Conrad
1
Je n'ai pas reçu de StackOverflowErrorpaquet parce que le paquet OpenBSD 5.5 de jdk-1.7.0.21p2v0 a un bogue. Il ne jette pas StackOverflowError. Il frappe SIGSEGVet décharge le noyau.
kernigh
57

Assemblage Linux x86 NASM

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

Divulgacher:

Oubliant de revenir à printHello, nous sautons à nouveau dans _start.

Michael Ehrenreich
la source
78
En montage, rien n’est évident.
11684
21
@ 11684: Je trouve le contraire vrai: en assembleur, tout est évident, car personne ne peut utiliser des abstractions pour masquer ce que son code fait réellement.
Mason Wheeler
3
C'est génial pour sa simplicité et son élégance.
haneefmubarak
11
@MasonWheeler: Je préfère dire que tout est visible au lieu d'évident ... Pour une belle façon de voir la différence entre visible et évident, j'adore me référer à sournois.xcott.com
Olivier Dulac le
2
Je me souviens de mes erreurs fréquentes d'oubliret
Ruslan le
48

C ++ au moment de la compilation

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

template <>
struct S<0> {};

template
struct S<-1>;
$ g ++ -c test.cc -ftemplate-depth = 40000
g ++: erreur interne du compilateur: erreur de segmentation (programme cc1plus)
S'il vous plaît soumettre un rapport de bogue complet,
avec source prétraitée, le cas échéant.
Voir pour les instructions.

Il n'y a pas de récursivité ce fichier source, aucune des classes n'a elle-même une classe de base, même indirectement. (En C ++, dans une classe de modèle comme celle-ci, S<1>il S<2>s'agit de classes complètement distinctes.) L'erreur de segmentation est due au dépassement de capacité de la pile après la récursivité dans le compilateur.

hvd
la source
7
J'avoue que j'aurais appelé cela une récursion évidente dans votre métaprogramme.
2
GCC détecte la récursivité et s'arrête gracieusement de ce côté-ci (les versions 4.8 et supérieures semblent convenir)
Alec Teal
2
template <typename T> auto foo(T t) -> decltype(foo(t)); decltype(foo(0)) x;est un peu plus court.
Casey
2
@hvd Il semble exploiter un bogue dans GCC. Clang intercepte l’utilisation erronée - ce dont je suppose que vous êtes déjà au courant - mais mon GCC crache presque 2 mégaoctets de messages d’erreur.
Casey
4
+1 pour le dépassement de pile de méta : p
Aschratt le
45

Bash (alerte de danger)

while true
do 
  mkdir x
  cd x
done

À proprement parler, cela n'entraînera pas directement un débordement de pile, mais générera ce que l'on pourrait appeler une " situation de génération persistante de pile-over-flow ": lorsque vous l'exécutez jusqu'à ce que votre disque soit plein, et que vous souhaitiez supprimer le désordre avec "rm -rf x ", celui-là est touché.

Cela ne se produit pas sur tous les systèmes, cependant. Certains sont plus robustes que d'autres.

Gros danger AVERTISSEMENT:

certains systèmes gèrent cela très mal et vous aurez peut-être du mal à nettoyer (parce que "rm -rf" lui-même rencontrera un problème de récusion). Vous devrez peut-être écrire un script similaire pour le nettoyage.

Mieux vaut essayer cela dans une machine à gratter si vous n’en êtes pas sûr.

PS: la même chose s'applique, bien sûr, si programmé ou fait dans un script batch.
PPS: il peut être intéressant d’obtenir un commentaire de votre part, sur le comportement de votre système particulier ...

blabla999
la source
comme je l'écrivais: sur de nombreux systèmes, rm -rf tente de tomber et est touché par un (peut-être plus ces jours-ci, avec un espace d'adressage de 64 bits - mais sur des machines plus petites avec une pile plus petite, il se peut). Bien sûr, il peut aussi y avoir des "rm" -applications autour, qui le font différemment ...
blabla999
2
Il me semble que quelque chose while cd x; do :; done; cd ..; while rmdir x; cd ..; done;devrait s’occuper de ça.
Blacklight Shining
Vous avez absolument raison (c'est ce que je voulais dire par "script similaire pour le nettoyage"). Cependant, une fois que votre disque (ou quota) est plein, vous pouvez même avoir des difficultés à vous connecter par la suite, car certains systèmes traitaient mal cette situation. Vous devrez entrer en tant que root et faire le script (ce qui est facile sur les PC actuels, mais qui était souvent difficile dans le passé, quand les ordinateurs étaient utilisés par plus d'un utilisateur).
blabla999
drôle, cela obtient plus de votes que la magie Smalltalk ci-dessous (jamais pensé!)
blabla999
Quelqu'un a essayé ceci sur Windows?
Sarge Borsch
43

Java

Une belle de Java Puzzlers . Qu'est-ce que ça imprime?

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");
        }
    }
}

En fait, il échoue avec une erreur StackOverflowError.

L'exception dans le constructeur est juste un hareng rouge. Voici ce que dit le livre à ce sujet:

Lorsque vous appelez un constructeur, les initialiseurs de variables d'instance sont exécutés avant le corps du constructeur . Dans ce cas, l'initialiseur de la variable internalInstanceappelle le constructeur de manière récursive. Ce constructeur, à son tour, initialise son propre internalInstancechamp en invoquant à nouveau le constructeur Reluctant et ainsi de suite, à l'infini. Ces invocations récursives provoquent StackOverflowErroravant que le corps du constructeur ait une chance de s'exécuter. Parce que StackOverflowErrorest un sous-type de Errorplutôt que de Exception, la clause catch mainne l’attrape pas.

ntoskrnl
la source
41

Latex

\end\end

La pile d'entrée déborde parce qu'elle \endse développe de manière répétée dans une boucle infinie, comme expliqué ici .

TeX échoue avec un TeX capacity exceeded, sorry [input stack size=5000]ou similaire.

bwDraco
la source
1
J'attendais un problème TeX / LaTeX. Ces choses sont plus choquantes que la plupart - en général, j'ai oublié que ce que je fais compte est de la programmation quand soudainement j'ai réussi à écrire quelque chose qui est infiniment récursif.
Ernir
1
"Capacité de TeX dépassée, désolé" TeX est tellement poli quand il échoue.
Alex A.
40

BF

Peut-être que la pile débordera éventuellement, cela dépend de la longueur de la pile de l'interprète ...

+[>+]
Timtech
la source
5
Ce code me rappelle le jeu de la vie.
Adam Arold
10
Strictement parlant, il n'y a pas de pile dans le brainfuck.
fejesjoco
6
@fejesjoco Tape, si vous le souhaitez.
Timtech
32

C #, au moment de la compilation

Le compilateur Microsoft C # peut détruire sa pile de différentes façons. chaque fois que vous voyez une erreur "expression est trop complexe à compiler" du compilateur C #, c'est certainement parce que la pile a fondu.

L'analyseur est une descente récursive, ainsi toute structure de langage suffisamment profondément imbriquée fera sauter la pile:

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

L'analyseur d'expression est assez intelligent pour éliminer les récursions sur le côté sur lequel on rentre généralement. Habituellement:

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

qui construit un arbre d'analyse extrêmement profond, ne fera pas sauter la pile. Mais si vous forcez la récursion à se produire de l'autre côté:

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

alors la pile peut être soufflée.

Ceux-ci ont la propriété inélégante que le programme est très grand. Il est également possible de faire entrer l'analyseur sémantique dans des récursions sans limite avec un petit programme car il n'est pas assez intelligent pour supprimer certains cycles impairs dans le système de types. (Roslyn pourrait améliorer cela.)

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? 

Je décris pourquoi cette analyse entre ici dans une récursion infinie:

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

et pour de nombreux autres exemples intéressants, vous devriez lire cet article:

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

Eric Lippert
la source
2
Le message d'erreur exact est fatal error CS1647: An expression is too long or complex to compile near (code). La documentation de ce message d'erreur est ici , et c'est exactement ce que vous dites: "Il y a eu un débordement de pile dans le compilateur qui traite votre code."
bwDraco
32

Sur Internet (utilisé par un milliard de personnes / jour)

Redirects, HTTP status code: 301

Par exemple, sur le site Web de support de Dell (pas d'infraction, désolé Dell):

Si vous supprimez le TAG de support de l'URL, les redirections infinies sont effectuées . Dans l'URL suivante, ###### est un tag de support.

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

Je crois que cela équivaut à un débordement de pile.

codeSetter
la source
5
belle façon :) firefox dit qu'il redirige afin que la demande ne peut pas être résolue qui est le équivalent de stackoverflow :)
masterX244
3
Notez que les redirections ne sont pas infinies - si vous cliquez sur "Try Again", il ajoute quelques /Errors/URL supplémentaires à l'URL, puis s'arrête après la réception de HTTP 400 Bad Request. Mais ceci permet sans doute un débordement de pile supérieur à celui d’une redirection infinie.
Nandhp
@nandhp, je suis d'accord, mais si vous pensez à un navigateur du tiers monde (pas moderne, IE etc.), ils n'ont pas la moindre idée de cette situation.
codeSetter
2
Voici comment Wget répond ici: 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

Un stackoverflow réalisé avec des éléments en boucle uniquement.

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

Explication (survolez pour voir le spoiler):

Le programme commettra une erreur de segmentation lorsque l’interprète essaiera de récupérer le $tmptableau (lors de la réaffectation).$tmp ici). Tout simplement parce que le tableau est trop profond (auto-référencement) et que le garbage collector aboutit à une récursivité.

bwoebi
la source
16
Le CPG de PHP ne peut pas détecter les structures auto-référentielles? Vraiment?! Aie. C'est le mal.
Peter C
1
Oui, mais ce n'est pas parfait. Il y a une raison pour laquelle while (1) { $a = array(&$a); }ou quelque chose de similaire atteint la limite de mémoire…
bwoebi
Eh oui, je pense que c'est la même raison pour laquelle PHP ne fonctionne pas correctement en ce qui concerne le développement orienté objet; (abstraction, héritage, etc.)
pythonian29033 le
1
@ pythonian29033 vous souhaitez élaborer?
vvondra
30

Java

J'ai fait exactement le contraire - un programme qui devrait évidemment générer une erreur de débordement de pile, mais ne le fait pas.

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

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

Astuce: le programme s'exécute dans le temps O (2 n ), et n est la taille de la pile (généralement 1024).

De Java Puzzlers # 45:

Supposons que notre machine puisse exécuter 10 10 appels par seconde et générer 10 10 exceptions par seconde, ce qui est assez généreux par rapport aux normes actuelles. Selon ces hypothèses, le programme prendra fin dans environ 1,7 × 10 291 ans. Pour mettre cela en perspective, la durée de vie de notre soleil est estimée à 10 10 ans, alors il est fort à parier qu'aucun d' entre nous sera sur place pour voir ce programme fin. Bien que ce ne soit pas une boucle infinie, cela pourrait tout aussi bien l'être.

ntoskrnl
la source
3
Récursion évidente ... pas très intéressante.
Kami
5
@Kami Avez-vous essayé? Avez-vous réellement eu une erreur StackOverflowError? Cela semble évident, mais ça ne l'est pas.
ntoskrnl
Ce n’est pas parce qu’une exception est interceptée que cela n’a jamais été lancé. Ce programme lève une exception de débordement de pile après le temps O (n)
CodesInChaos
1
@CodesInChaos D'accord, j'ai donc revérifié cela, et la bonne version utilise finallyplutôt que catch, et le temps d'exécution est O (2 ^ n). Réponse mise à jour.
ntoskrnl
@ntorkrnl nice one + 1'd; btw a déjà le compilateur à stackoverflow (le compilateur fonctionne dans la VM, trop fyi)
masterX244
30

C #

Premier post, alors s'il vous plaît, allez-y doucement sur moi.

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

Cela crée simplement une trace de pile, récupère le cadre supérieur (qui sera notre dernier appel Main()), récupère la méthode et l'appelle.

BenM
la source
belle façon de soi-même qui n'est pas si évident sans expliquer; + +1
masterX244
Vous devriez commenter ce code avec "qu'est-ce qui pourrait éventuellement mal tourner?": P
Spaceman
26

Java

  • En Java 5, printStackTrace() entre une boucle infinie.
  • En Java 6, printStackTrace()jetteStackOverflowError .
  • En Java 7 et 8, cela a été corrigé.

Ce qui est fou, c'est que dans Java 5 et 6, cela ne provient pas du code utilisateur, cela se passe dans le code de JDK. Personne ne soupçonne raisonnablement qu'il printStackTrace()peut être dangereux d'exécuter.

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;
    }
}
Victor Stafusa
la source
7
Ouais; stackoverflows hors de rien sont le meilleur ami pour codetrolling et un mal de tête sérieux sur la chasse em
masterX244
2
Si quelqu'un se le demande, un code équivalent en C # provoque une boucle infinie. Mais la InnerExceptionpropriété est en lecture seule et est définie dans le constructeur, une réflexion est donc nécessaire.
Athari
17

JavaScript: Mutation de fonction itérative non récursive

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

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

Il n'y a pas de récursion ici du tout. fsera de façon répétée avec de plus en plus d’arguments jusqu’à ce qu’elle puisse déborder de la pile en un seul appel de fonction. La console.logpartie est optionnelle si vous voulez voir combien d'arguments sont nécessaires pour le faire. Cela garantit également que les moteurs intelligents JS ne l'optimiseront pas.

Version Code-golf en CoffeeScript, 28 caractères:

f=->
do f=f.bind f,1 while 1
Justin Morgan
la source
14

C # avec un échec épique

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());
    }
}

La façon dont cela échoue est épique, cela m'a complètement bouleversé:

entrez la description de l'image ici

Ce n'est qu'une image d'une série d'images étranges et apparemment infinies.

Cela doit être la chose la plus étrange jamais. Quelqu'un peut-il expliquer? Apparemment, le nombre croissant d'espaces utilisés pour l'indentation fait apparaître ces blocs blancs. Cela se produit sur une entreprise Win7 Enterprise x64 avec .NET 4.5.

Je n'en ai pas encore vu la fin. Si vous remplacez System.Console.Outpar System.IO.Stream.Null, il mourra assez vite.

L'explication est assez simple. Je crée une classe qui n'a qu'une seule propriété et renvoie toujours une nouvelle instance de son type contenant. C'est donc une hiérarchie d'objet infiniment profonde. Maintenant, nous avons besoin de quelque chose qui essaie de lire cela. C'est là que j'utilise le XmlSerializer, ce qui ne fait que cela. Et apparemment, il utilise la récursivité.

fejesjoco
la source
Ouais; sérialisation ftw: P; mais plus drôle, c’est quand votre bizarrerie est complètement en dehors de votre code, comme par exemple comment snakeyaml obtient les attributs et qu’une classe se retourne d’une manière qui se termine récursivement
masterX244
1
Eh bien, le débordement se produit en dehors de mon code, mais la vraie raison pour laquelle j'ai posté cela est à cause de l'effet secondaire inattendu sur la console :-)
fejesjoco
Je pense que les bits blancs doivent être liés à .Net 4.5 ou à la configuration de votre environnement, car en utilisant .Net 4 (même avec cmd), je n’obtiens que des espaces, pas de blocs blancs. Mon hypothèse est que la version Win7 Enterprise de cmd ou l'émulateur de console .Net 4.5 traite une certaine combinaison de caractères comme "change le fond de couleur de la console".
Pharap
Je ne peux pas le reproduire avec .NET 4.5 sur Win7 x64 Professional avec Aero activé
Ray
Ne peut pas reproduire non plus. .NET 4.5, Windows 8.1 Pro.
bwDraco
13

frapper

_(){ _;};_

Alors que beaucoup pourraient reconnaître que la récursion est évidente , elle semble jolie. Non?

Lors de l'exécution, vous êtes assuré de voir:

Segmentation fault (core dumped)
devnull
la source
5
celui-ci est plus joli et pire:_(){_|_;};_
RSFalcon7
1
@ RSFalcon7 Alerte Forkbomb! (En outre, n'a-t-il pas besoin d'un espace après la {syntaxe pour être correct?)
Blacklight Shining
3
Essayez également ceci:(){:|:;}:
Tarek Eldeeb
14
Ressemble à une émoticône mutante et terrifiante.
Tim Seguine
8
Émoticônes Bash: mangeur de visages, tueur de piles.
NobleUplift
12

Haskell

(triste mais vrai jusqu'au moins ghc-7.6, bien qu'avec O1ou plus cela optimisera le problème)

main = print $ sum [1 .. 100000000]
a cessé de tourner dans le sens antihoraire
la source
ne devrait-il pas faire une optimisation automatique de l'appel final (en somme)?
blabla999
2
@ blabla999: les appels de queue ne sont pas très pertinents dans Haskell, c'est principalement une accumulation de thunk due à une paresse dissimulée qui cause de tels problèmes. Dans ce cas, le problème est qu’il sumest implémenté en termes de foldl, ce qui utilise des appels de queue, mais parce qu’il n’évalue pas l’accumulateur, il ne produit strictement qu’une pile de thunks de la taille de la liste originale. Le problème disparaît lors du passage à foldl' (+), cela évalue strictement et renvoie donc un WHN dans son appel final. Ou, comme je l'ai dit, si vous activez les optimisations de GHC!
cessé de tourner dans le sens anti-horaire le
aah - intéressant, donc si personne n'attendait le thunk (c'est-à-dire en laissant de côté l'impression), le ramasse-miettes les ramasserait (de l'avant vers l'arrière)?
blabla999
1
En passant, rien de tout cela n’est vraiment spécifié par le standard Haskell: il est simplement requis que l’évaluation soit non stricte , c’est -à- dire qu’un calcul non -final ne bloquera pas indéfiniment si le résultat n’est pas entièrement requis. L’implémentation dure assez longtemps jusqu’à la mise en œuvre. En GHC standard, elle ne bloque pas du tout tant que vous n’avez pas demandé le résultat.
cessé de tourner dans le sens anti-horaire le
2
Haskell est cool.
blabla999
12

Petite conversation

Cela crée une nouvelle méthode à la volée, qui
  crée une nouvelle méthode à la volée, qui
    crée une nouvelle méthode à la volée, qui
      ...
    ...
  ...
puis y est transférée.

Un peu de piquant supplémentaire provient de la contrainte de mémoire de pile ET de mémoire de pile en même temps, en créant à la fois un nom de méthode de plus en plus long et un nombre énorme en tant que destinataire, au moment où nous tombons dans le trou ... (mais la récursion nous frappe en premier )

compiler en entier:

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.

puis sautez, en évaluant "2 downTheRabbitHole"...
... au bout d'un moment, vous vous retrouverez dans un débogueur, affichant une exception RecursionException.

Ensuite, vous devez nettoyer tout le désordre (SmallInteger et LargeInteger ont maintenant beaucoup de code du pays des merveilles):

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

ou bien passez quelque temps dans le navigateur, supprimant le pays des merveilles d'Alice.

Voici quelques-uns de la tête de la trace:

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: le "withoutUpdatingChangesFile:" a été ajouté pour éviter de devoir nettoyer le fichier journal des modifications persistant de Smalltalk.

PPS: merci pour le défi: penser à quelque chose de nouveau et d'innovant était amusant!

PPPS: J'aime noter que certains dialectes / versions Smalltalk copient les trames de pile débordantes dans le tas - de sorte que celles-ci risquent de se trouver dans une situation de mémoire insuffisante.

blabla999
la source
2
LOL +1 pour cette magie noire dans sa forme pure
masterX244
Si je trouve un peu de temps, je pourrai "m'améliorer" en utilisant une méthode anonyme de classe anonyme, pour créer le trou de lapin le plus sombre qui soit ...
blabla999
12

C #

Vraiment gros struct, pas de récursivité, C # pur, pas de code dangereux.

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));
    }
}

comme un kicker, il bloque les fenêtres de débogage en indiquant que {Cannot evaluate expression because the current thread is in a stack overflow state.}


Et la version générique (merci pour la suggestion 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>>>>();
    }
}
ja72
la source
Besoin de plus de code générique: P
NPSF3000 Le
Cela ressemblerait à de la récursivité, mais il est possible avec des arguments de type imbriqués.
ja72
1
Je n'ai pas vu votre réponse de C # struct avant que je poste la mienne. J'ai toujours une approche légèrement différente, alors peut-être que nous les laissons coexister.
Thomas Weller
11

C #

Mauvaise implémentation de l' ==opérateur de remplacement :

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;
        }
    }
}

On pourrait dire qu’il est évident que l’ operator==appel se fait de lui-même en utilisant l’ ==opérateur, mais vous ne pensez généralement pas de cette façon ==, il est donc facile de tomber dans ce piège.

Sebastian Negraszus
la source
11

Début de réponse en utilisant SnakeYAML

class A
{

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

Edit: non-golfé

Il appartient au lecteur de savoir comment cela fonctionne: P (astuce: stackoverflow.com)

À propos: la récursivité est faite dynamiquement par SnakeYAML (vous remarquerez si vous savez comment il détecte les champs qu'il sérialise et regarde ensuite dans Pointle code source)

Edit: racontant comment ça marche:

SnakeYAML recherche une paire de méthodes getXXXet setXXXporte le même nom XXXet le type de retour du getter est identique au paramètre de setter; et étonnamment la Pointclasse a Point getLocation()et void setLocation(Point P)qui se retourne; SnakeYAML ne le remarque pas et revient sur cette bizarrerie et StackOverflows. Découvert celui-là en travaillant avec eux à l'intérieur d'un HashMapet en demandant à stackoverflow.com dessus.

masterX244
la source
10

C #

getter de propriété implémenté à tort

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

static void Main()
{
   int p = new C().P;
}
Alberto
la source
14
A MON HUMBLE AVIS. Ceci est un exemple classique d'une récursivité évidente ... (et donc non valide)
Ole Albers
2
Eh bien, ce n’est évident qu’une fois que vous l’avez fait une fois et que vous vous êtes rendu compte que les getters C # ne fonctionnent pas comme vous le pensiez. Après tout, ce code est une déclaration de variable membre, alors pourquoi ne créerait-il pas une variable membre réelle?
Meustrus
2
Ce n'est rien de plus qu'une façon alambiquée de procéderstatic void Main() { Main(); }
Jodrell le
5
@ Jodrell Vous n'écririez pas récursivement Main()accidentellement. Mais il est assez simple d'écrire accidentellement une propriété récursive, puis de se laisser confondre par le débordement de pile.
svick
1
Cela serait utile pour quelqu'un qui prend C #.
2rs2ts