Найцікавіший спосіб отримати переповнення стека [закрито]


146

Як програміст, ви, звичайно, знаєте помилку переповнення стека через очевидну рекурсію. Але, безумовно, існує багато дивних і незвичайних способів, як заставити свою улюблену мову виплюнути цю помилку.

Цілі:

  1. Повинно викликати переповнення стека, що добре видно на виході помилки.
  2. Не дозволяється використовувати очевидну рекурсію.

Приклади недійсних програм:

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

Найбільш креативні способи є найкращими, оскільки це на . Тобто, уникайте нудних очевидних відповідей, як це:

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

Незважаючи на те, що я прийняв відповідь зараз, додати більше відповідей все одно добре :)


14
Я схильний виробляти, переходячи до stackoverflow.com, хоча мені було відомо, що запит "переповнення стека" у моїй пошуковій системі на вибір.
OJFord

21
Використовуйте Internet Explorer.
Вірний

64
Найбільш дивний спосіб створення переповнення стека - це розміщення конкурсу на популярність на codegolf.stackexchange.com з проханням, щоб люди розмістили найсмішніший спосіб отримати переповнення стека. Відповідаючи, тестуючи свої рішення на запитання, буде створюватися переповнення стека. Я ще не перевіряв його, тому не можу бути впевнений, що він працює (саме тому я не опублікував це як відповідь).
Тім Сегуїн

3
Я частково ставлюсь
robert

11
Заїжджайте на Toyota (Гей, почекайте хвилину, моя машина - Toyota ...)
кричущий ossifrage

Відповіді:


224

Пітон

import sys
sys.setrecursionlimit(1)

Це призведе до негайного відмови перекладача:

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

Замість використання рекурсії він просто скорочує стек, щоб він негайно переповнювався.


12
Мило, але не зовсім те, на що, на мою думку, було спрямовано оригінальне запитання.
Ніт

12
@Nit я не бачу проблеми. Що щодо цього рішення незадовільно?
SimonT

17
@ masterX244 Так, вся суть питання в тому, що "не роби це звичайним способом".
Plutor

24
@Plutor Ви зазвичай спеціально встановлюєте StackOverFlow?
Ківі

15
Це було лише м'яко розумно в перший раз, коли він був розміщений .
примо

189

Пітон

import webbrowser
webbrowser.open("http://stackoverflow.com/")

7
Фантастичний. Приємно і елегантно.
Джеймс Вебстер

103
Значно буквальне. Дуже відповідь.
Тім Сегуїн

47
Ну, це показує переповнення стека, але для його створення це працює лише в тому випадку, якщо це виконує Джефф Етвуд або Джоел Спольський.
Механічний равлик

10
@TimSeguine Нічого собі
Шон Аллред

9
Не відповідь, оскільки це не у виході з помилками.
П'єр Арло

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

Працює шляхом заміни зворотної адреси, Тому gповертається до пункту, mainперш ніж дзвонити f. Працює для будь-якої платформи, де зворотні адреси знаходяться на стеці, але можуть знадобитися налаштування.

Звичайно, запис поза масивом - це невизначена поведінка , і ви не маєте гарантії, що це спричинить переповнення стека, а не, скажімо, пофарбуйте вуса синім кольором. Деталі прапорців платформи, компілятора та компіляції можуть мати велике значення.


1
Хіба це не призведе до сегментації?
11684 р.

4
+1 за дивні маніпуляції з стеком і абсолютно без рекурсії!
RSFalcon7

6
@ 11684, Це невизначена поведінка, тому загалом вона може розбитися. У 32-бітовому Linux (який я тестував) він пише поза масиву, перезаписуючи зворотну адресу, і не виходить з ладу, поки стек не переповнений.
угорен

46
"Пофарбуй вуса синім" -> Ця лінія була у мене.
Анееш Догра

2
Ого. Це фантастично заплутано.
MirroredFate

108

JavaScript / DOM

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

    title = 'Kill me!';
}

Якщо ви хочете вбити свій браузер, спробуйте це в консолі.


53
Я впевнений, що ти заслужив більше +1 голосів, але, на жаль, люди спробували це перед голосуванням .... lol
Реагує

5
Ну, це було ефективно. Я навіть не зміг відкрити менеджера завдань, щоб його вбити.
прим

1
Використовуйте Chrome, закрийте вкладку. Проблема вирішена.
Коул Джонсон

1
with (document.body) { addEventListener('DOMSubtreeModified', function() { appendChild(firstChild); }, false); title = 'Kill me!'; } 15:43:43.642 TypeError: can't convert undefined to object
Брайан Мінтон

1
Чорт мій Firefox був повішений
Фархад

91

Java

Побачив щось подібне десь тут:

Редагувати: Знайдено там, де я це бачив: відповідь Джо K на найкоротшу програму, яка кидає помилку StackOverflow

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

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

Це може заплутати деяких початківців Java. Він просто приховує рекурсивний виклик. val + thisстаєval + this.toString() тому, що valє струною.

Дивіться його тут: http://ideone.com/Z0sXiD


30
Власне, це і є new StringBuilder().append(val).append("").append(this).toString(), і останнє додавання викликає String.valueOf (...), що в свою чергу викликаєString. Це робить ваш слід стека трохи різноманітним (три способи).
Paŭlo Ebermann

2
@ PaŭloEbermann Так, це правильно. Однак набагато простіше сказати, що це стає "" + this.toString().
Джастін

2
Я думаю, що, + "" +можливо, люди підкажуть, як це здається марним на перший погляд. String val;і return val + this;може бути незначно прихованим
Cruncher

@Cruncher зовсім не є. Якщо ви Java-кодер, ви знаєте, що найпростіший спосіб об'єднання int до рядка під час ""побудови -String - за допомогою+ ""
Джастін,

5
У реальному застосуванні я вважаю за краще уникати нескінченних рекурсій та помилок переповнення стека
jon_darkstar

77

С

Досить легко:

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

10
+1 для не очевидних! Незважаючи на це дуже залежно від системи ( ulimit -s unlimitedв оболонці вирішується це в Linux)
RSFalcon7

4
@ RSFalcon7, дякую за +1, але для мене це насправді було найбільш очевидним !!
Шахбаз

14
@Cruncher: Це не призводить до рекурсії. Дана проблема полягала в тому, щоб підірвати стек. У багатьох операційних системах стек має фіксований розмір і набагато менший, ніж десять мільйонів точок, тому це вибиває стек.
Ерік Ліпперт

2
@HannoBinder, В Linux, де я тестував, ви не отримуєте помилку переповнення стека. Ви отримуєте помилку сегментації, оскільки переповнення стека призводить до доступу до невідомих сегментів. Я не впевнений, чи існує помилка переповнення стека в Linux, оскільки нескінченний рекурсивний виклик функції також видає помилку сегментації.
Шахбаз

3
~0uце досить велика кількість у C.
Vortico

63

Нерекурсивний переповнення стека в C

Викликання невідповідності конвенції

typedef void __stdcall (* ptr) (int);

void __cdecl hello (int x) { }

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

Компілювати з gcc -O0.

__cdeclфункції очікують, що абонент очистить стек, і __stdcallочікує, що викликає це робити, тому, зателефонувавши через покажчик функції typecast, очищення ніколи не робиться - mainнатискає параметр на стек для кожного виклику, але нічого не з'являється, і в кінцевому підсумку стек заливки.


2
приємний спосіб
спамування

62

JavaScript

window.toString = String.toLocaleString;
+this;

5
Цей недооцінений.
Ри-

1
Що робить +this?
NobleUplift

8
Unary + викликає абстрактну операцію ToNumber. При цьому використовується операція ToPrimitive з натяком типу номера. ToPrimitive на об’єкті використовує внутрішній метод [[DefaultValue]], який при передачі підказки про число спочатку перевіряє, чи існує метод valueOf () і повертає примітив. window.valueOf () повертає об'єкт, тому замість [[DefaultValue]] повертає результат виклику toString (). Перше, що робить window.toString (тепер, коли це toLocaleString), викликає toString на "this". Повторіть.
Райан Кавано

3
Якщо Chrome видає помилку "Занадто багато рекурсії", це працює в Chrome, правда? Стек переповнюється, і ось Chrome забезпечує виняток переповнення стека.
Девід Конрад

4
Вам слід скористатися +{toString:"".toLocaleString}:-)
Бергі

62

Мене розчарував той факт, що java 7 та java 8 не захищені від мого злого коду в попередній відповіді . Тому я вирішив, що патч для цього необхідний.

Успіху! Я зробив printStackTrace()кидок StackOverflowError. printStackTrace()зазвичай використовується для налагодження та ведення журналів, і ніхто обґрунтовано не підозрює, що це може бути небезпечно. Не важко помітити, що цим кодом можна було б зловживати, щоб створити серйозні проблеми безпеки:

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

Деякі люди можуть подумати, що це очевидна рекурсія. Це не так. EvilExceptionКонструктор не викликає getCause()метод, так що виключення на насправді може бути викинуто благополучно в кінці кінців. Виклик getCause()методу також не призведе StackOverflowError. Рекурсія знаходиться у звичайній несподіваній printStackTrace()поведінці JDK та будь-якій сторонній бібліотеці для налагодження та ведення журналів, які використовуються для перевірки виключення. Далі, ймовірно, що місце, де викинуто виняток, знаходиться дуже далеко від місця, де воно обробляється.

У будь-якому випадку, тут є код, який дійсно кидає a StackOverflowErrorі не містить рекурсивних викликів методів. Це StackOverflowErrorвідбувається поза mainметодом у 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"

2
: D і тепер із крос-java-версією методу, сумісного з stackoverflow. Злий ублюдок! : D
Ківі

9
Я схильний вважати рекурсію в цьому випадку очевидною.
Taemyr

1
@BlacklightShining Виклик getCause()методу не призводить до StackOverflowError. Він покладається на те, що існує код JDK, який рекурсивно викликає getCause()метод.
Віктор Стафуса

2
Я думав, ви можете спростити це, змінивши тіло getCauseна просто, return this;але, мабуть, Java занадто розумний для цього. Він помічає, що це " CIRCULAR REFERENCE".
Девід Конрад

1
У мене не вийшло, StackOverflowErrorтому що у пакета OpenBSD 5.5 jdk-1.7.0.21p2v0 є помилка. Це не кидає StackOverflowError. Він б’є SIGSEGVі скидає серцевину.
Керніг

57

Linux x86 NASM Assembly

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

Спойлер:

Забувши повернутися з printHello, тому ми знову стрибаємо прямо у _start.


78
У зборі нічого очевидного немає.
11684 р.

21
@ 11684: Я вважаю, що правда навпаки: в зборах все очевидно, тому що ніхто не може використовувати абстракції, щоб приховати, що насправді робить їх код.
Мейсон Уілер

3
Це блискуче своєю простотою та вишуканістю.
haneefmubarak

11
@MasonWheeler: Я вважаю за краще сказати, що все видно замість очевидного ... Для приємного способу побачити різницю між видимим і очевидним, я люблю посилатися на underhanded.xcott.com
Олів'є Дулак

2
Я пам’ятаю мої часті помилки забуванняret
Руслан

48

C ++ під час компіляції

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

template <>
struct S<0> {};

template
struct S<-1>;
$ g ++ -c test.cc-глибина шаблону = 40000
g ++: внутрішня помилка компілятора: помилка сегментації (програма cc1plus)
Надішліть повний звіт про помилку,
з попередньо обробленим джерелом, якщо це доречно.
Дивіться інструкції.

У цьому вихідному файлі немає рекурсії, жоден з класів не є базовим класом, навіть не побічно. (У C ++ у такому класі шаблонів, S<1>і S<2>це абсолютно різні класи.) Помилка сегментації пояснюється переповненням стека після рекурсії в компіляторі.


7
Признаюсь, я би назвав це очевидною рекурсією у вашій метапрограмі.

2
GCC виявляє рекурсію і витончено зупиняє цю сторону (4,8 і вище здається нормально)
Alec Teal

2
template <typename T> auto foo(T t) -> decltype(foo(t)); decltype(foo(0)) x;трохи коротше.
Кейсі

2
@hvd Здається, що в GCC використовується помилка. clang вловлює помилкове використання - про яке я припускаю, що ви вже знаєте - але це змушує мій GCC випромінювати майже 2 мегабайти повідомлень про помилки.
Кейсі


45

Bash (Попередження про небезпеку)

while true
do 
  mkdir x
  cd x
done

Строго кажучи, це не буде безпосередньо стеком переповнення, але генерує те, що може бути позначено як " стійка ситуація, що генерує стек-переповнення ": коли ви запускаєте це, поки ваш диск не заповниться, і хочете видалити безлад з "rm -rf х ", той, хто потрапив.

Однак це відбувається не у всіх системах. Деякі є більш надійними, ніж інші.

Велика небезпека УВАГА:

деякі системи справляються з цим дуже погано, і, можливо, вам доведеться дуже важко очистити (адже "rm -rf" сам по собі виникне у проблемі рекусії). Можливо, вам доведеться написати аналогічний сценарій для очищення.

Краще спробуйте це з нульовим VM, якщо не впевнений.

PS: те ж саме стосується, звичайно, якщо вони запрограмовані або виконані в пакетному сценарії.
PPS: можливо, вам цікаво отримати коментар від вас, як поводиться ваша конкретна система ...


як я писав: у багатьох системах rm -rf намагається зійти вниз і потрапляє на нього (може, вже не сьогодні, з 64-бітовим адресним простором - але на менших машинах із меншим стеком, можливо). Звичайно, також можуть бути "rm" -виконання навколо, які роблять це інакше ...
blabla999

2
Мені здається, щось подібне while cd x; do :; done; cd ..; while rmdir x; cd ..; done;повинно подбати про це.
Blacklight Shining

Ви абсолютно праві (це я мав на увазі під "подібним сценарієм для очищення"). Однак, коли ваш диск (або квота) заповнений, вам може навіть після цього важко ввійти, оскільки деякі системи погано справлялися з цією ситуацією. Вам доведеться увійти як root і виконати сценарій (що на тих ПК сьогодні є простим, але в давні часи це було важко, коли на комп'ютерах, де використовуються більш ніж один користувач).
blabla999

смішно, це отримує більше голосів, ніж магія Smalltalk внизу (ніколи про це не думав!)
blabla999

хтось спробував це в Windows?
Sarge Borsch

43

Java

Гарний від Java Puzzlers . Що воно друкує?

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

Він фактично не працює з StackOverflowError.

Виняток у конструкторі - просто червона оселедець. Про це має сказати книга:

Коли ви викликаєте конструктор, ініціалізатори змінної екземпляра запускаються перед тілом конструктора . У цьому випадку ініціалізатор змінної internalInstanceвикликає конструктор рекурсивно. Цей конструктор, у свою чергу, ініціалізує своє власне internalInstanceполе, викликаючи конструктор Reluctant знову і так далі, ad infinitum. Ці рекурсивні виклики викликають а, StackOverflowErrorперш ніж тіло конструктора колись отримає шанс виконати. Оскільки StackOverflowErrorпідтип є, Errorа не Exceptionпідпунктом, то застереження у програмі mainне підходить .


41

LaTeX

\end\end

Вхідний стек переповнюється, тому що \endнеодноразово розширюється у нескінченному циклі, як пояснено тут .

TeX виходить з ладу з TeX capacity exceeded, sorry [input stack size=5000]подібним або подібним.


1
Я чекав випуску TeX / LaTeX. Ці речі є більш жалкими, ніж більшість - зазвичай я забув, що те, що я роблю, вважається програмуванням, коли раптом мені вдалося написати щось, що нескінченно рекурсивно.
Ернір


1
"Потужність TeX перевищена, вибачте" TeX настільки ввічливий, коли не працює.
Олексій А.

40

BF

З часом переповнюється стек, просто залежить, як довго інтерпретатор робить стек ...

+[>+]

5
Цей код нагадує мені гру життя.
Адам Арольд

10
Власне кажучи, немає жодної стопки в мозкових заходах.
fejesjoco

6
@fejesjoco Стрічка, якщо хочете.
Timtech

32

C #, під час компіляції

Існує кілька способів змусити компілятор Microsoft C # підірвати свій стек; кожного разу, коли ви побачите помилку "вираз занадто складний для компілювання" від компілятора C #, що майже напевно, тому що стек підірвався.

Аналізатор має рекурсивний спуск, тому будь-які досить глибоко вкладені мовні структури підірвуть стек:

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

Аналізатор виразів досить розумний щодо усунення рекурсій на стороні, що зазвичай повторюється. Зазвичай:

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

яке будує надзвичайно глибокий розбір дерева, не підірве стека. Але якщо ви змусите рекурсію відбутися з іншого боку:

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

тоді стек можна роздути.

Вони мають неелегантну властивість, що програма дуже велика. Можна також змусити семантичний аналізатор переходити до необмежених рекурсій за допомогою невеликої програми, оскільки він недостатньо розумний для видалення певних непарних циклів у системі типів. (Рослін це може покращити.)

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? 

Я описую, чому цей аналіз переходить у нескінченну рекурсію тут:

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

а ще для багатьох цікавих прикладів слід прочитати цей документ:

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


2
Точне повідомлення про помилку fatal error CS1647: An expression is too long or complex to compile near (code). Документація для цього повідомлення про помилку знаходиться тут , і це точно так, як ви говорите: "У компіляторі, що обробляє ваш код, було переповнено стек".
bwDraco

32

В Інтернеті (використовується мільйонами людей на день)

Redirects, HTTP status code: 301

Наприклад, на веб-сайті служби підтримки Dell (без образи, вибачте Dell):

Якщо ви вилучите TAG підтримки з URL-адреси, вона переходить у нескінченні переадресації . У наступній URL-адресі ###### є будь-яка підтримка TAG.

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

Я вважаю, що це еквівалентно переповненню стека.


5
приємний спосіб :) firefox повідомляє, що він переспрямовує, тому запит не може бути вирішений, який є stackoverflow rquivalent :)
masterX244

3
Зауважте, що переспрямовування не є нескінченним - якщо натиснути "Спробуйте ще раз", воно додасть ще кілька /Errors/s до URL-адреси, а потім зупиняється після отримання HTTP 400 Bad Request. Але це, мабуть, сприяє кращому переповненню стека, ніж нескінченне переадресація.
нандхп

@nandhp, я згоден, але якщо ви думаєте, що браузер третього світу (не сучасний, IE тощо), вони не мають уявлення про цю ситуацію.
codeSetter

2
Ось як тут реагує Wget: 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

Поток потоку, виконаний лише з петельними елементами.

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

Пояснення (наведіть курсор, щоб побачити спойлер):

Програма буде за замовчуванням, коли інтерпретатор намагається зібрати сміття зібрати $tmpмасив (при переназначенні $tmpтут). Просто тому, що масив занадто глибокий (самонавіювання), і тоді сміттєзбірник закінчується рекурсією.


16
PHP GC не може виявити самореференційні структури? Дійсно ?! Ой. Те є зло.
Пітер С

1
Так, але це не ідеально. Є якась причина, чому while (1) { $a = array(&$a); }чи щось подібне просто потрапляє на межу пам'яті…
bwoebi

так, я думаю, що це та сама причина, через яку PHP не працює належним чином і щодо об'єктно-орієнтованої розробки; (абстракція, успадкування тощо)
pythonian29033

1
@ pythonian29033 дбайливо розробити?
vvondra

30

Java

Я зробив навпаки - програму, яка, очевидно, повинна викинути помилку переповнення стека, але не робить.

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

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

Підказка: програма запускається в O (2 n ) час, а n - розмір стека (зазвичай 1024).

Із загадок Java № 45:

Припустимо, що наша машина може виконувати 10 10 дзвінків в секунду і генерувати 10 10 винятків в секунду, що досить щедро за сучасними стандартами. Згідно з цими припущеннями, програма закінчиться приблизно через 1,7 × 10 291 рік. Якщо говорити про це , то час життя нашого сонця оцінюється в 10 10 років, тому це безпечна ставка, що ніхто з нас не буде навколо, щоб ця програма припинилася. Хоча це не нескінченна петля, все-таки може бути.


3
Очевидна рекурсія ... не дуже цікава.
Камі

5
@Kami Ви пробували? Ви насправді отримали StackOverflowError? Це здається очевидним, але це не так.
ntoskrnl

Просто те, що виняток потрапляє, не означає, що його ніколи не кидали. Ця програма скидає виняток переповнення стека після часу O (n)
CodesInChaos

1
@CodesInChaos Гаразд, тому я двічі перевірив це, і правильна версія використовує, finallyа не catch, і час роботи - O (2 ^ n). Відповідь оновлено.
ntoskrnl

@ntorkrnl приємний один +1; btw вже отримав компілятор для stackoverflow (компілятор працює всередині VM, теж fyi)
masterX244

30

C #

Перший пост, тому будь ласка, будь ласка, на мене.

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

Це просто створює слід стека, захоплює верхній кадр (який буде нашим останнім дзвінком Main()), отримує метод і викликає його.


приємний спосіб самозванки, який не очевидний без пояснень; Поставив +1
masterX244

Ви повинні прокоментувати цей код із записом "що може піти не так?": P
Spaceman

26

Java

  • У Java 5 printStackTrace()входить нескінченний цикл.
  • На Java 6 printStackTrace()кидки StackOverflowError.
  • У Java 7 і 8 це було виправлено.

Божевільна річ у тому, що в Java 5 та 6 вона не походить від коду користувача, це відбувається з кодом JDK. Ніхто з розумних підозрюваних не printStackTrace()може бути небезпечним для страти.

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
так; stackoverflows з нічого не є найкращим другом для кодетролінгу та серйозного головного болю при полюванні на em
masterX244

2
Якщо хтось цікавиться, еквівалентний код у C # викликає нескінченний цикл. Але InnerExceptionвластивість є лише для читання і встановлюється в конструкторі, тому для цього необхідно відображення.
Афарі

17

JavaScript: Нерекурсивна, ітеративна мутація функції

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

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

Тут взагалі немає рекурсії. fбуде неодноразово кривитися все більше аргументів, поки він не зможе переповнити стек в одному виклику функції. Theconsole.logЧастина НЕ є обов'язковою в разі , якщо ви хочете , щоб побачити , скільки аргументів він приймає , щоб зробити це. Це також гарантує, що розумні двигуни JS не оптимізують це.

Версія коду-гольфу в CoffeeScript, 28 символів:

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

14

C # з епічним провалом

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

Те, як це не вдалося, епічне, воно повністю підірвало мою думку:

enter image description here

Це лише один кадр із, здавалося б, нескінченної трипінг-серії дивних зображень.

Це повинно бути найдивовижнішою річчю. Хтось може пояснити? Мабуть, постійно зростаюча кількість пробілів, що використовуються для відступу, викликає появу білих блоків. Це відбувається в Win7 Enterprise x64 з .NET 4.5.

Я ще не бачив кінця цьому. Якщо замінити System.Console.Outз System.IO.Stream.Null, вона вмирає досить швидко.

Пояснення досить просте. Я складаю клас, який має єдине властивість, і він завжди повертає новий екземпляр його типу, що містить. Отже, це нескінченно глибока ієрархія об'єктів. Тепер нам потрібно щось, що намагається прочитати через це. Ось де я використовую XmlSerializer, що робить саме це. І, мабуть, для цього використовується рекурсія.


так; серіалізація ftw: P; але кумедніше це, коли вигадка повністю поза вашим кодом, як, наприклад, як snakeyaml отримує атрибути, і один клас повертає себе таким чином, що в кінці повторюється
masterX244

1
Ну, переповнення трапляється поза моїм кодом, але справжня причина, яку я опублікував, це через несподіваний побічний ефект у консолі :-)
fejesjoco

Я думаю, що білі біти повинні стосуватися будь-якого. Думаю, що або cmd-версія Win7 Enterprise, або емулятор консолі .Net 4.5 трактують певну комбінацію символів як "зміну кольору консолі".
Фарап

Я не можу відтворити його за допомогою .NET 4.5 у програмі Win7 x64 Professional із включеним Aero
Рей,

Неможливо також відтворити. .NET 4.5, Windows 8.1 Pro.
bwDraco

13

баш

_(){ _;};_

Хоча багато хто може визнати, що рекурсія очевидна , але це здається гарним. Немає?

Після виконання ви гарантовано побачите:

Segmentation fault (core dumped)

5
цей гарніший і гірший:_(){_|_;};_
RSFalcon7

1
@ RSFalcon7 Forkbomb попередження! (Також, чи не потрібен простір після того, {щоб бути синтаксично правильним?)
Blacklight Shining

3
Спробуйте також це:(){:|:;}:
Тарек Елдіб

14
Схоже на моторошний, мутантний смайлик.
Тім Сегуїн

8
Bash смайлики: їдець обличчя, вбивця стеків.
NobleUplift

12

Хаскелл

(сумно, але правда, поки щонайменше ghc-7.6, хоча з O1або більше вона оптимізує проблему)

main = print $ sum [1 .. 100000000]

чи не слід робити оптимізацію хвостового дзвінка автоматично (у сумі)?
blabla999

2
@ blabla999: хвостові дзвінки не такі актуальні в Haskell, це, в основному, накопичення через приховану лінь, яка викликає такі проблеми. У цьому випадку проблема полягає в тому, що sumреалізовано в термінах foldl, який використовує хвостові дзвінки, але через те, що він не оцінює акумулятор строго, просто створює купу гронів, великих за вихідний список. Проблема зникає при переході на foldl' (+), що суворо оцінює і, таким чином, повертає WHN у своєму хвостовому дзвінку. Або, як я вже сказав, якщо ввімкнути оптимізацію GHC!
перестала повертати проти годинника,

ааа - цікаво, тож якщо ніхто не чекав би грона (тобто не залишаючи відбитку), сміттєзбірник збирає їх геть (спереду назад)?
blabla999

1
BTW, нічого з цього насправді не визначено стандартом Haskell: просто потрібно, щоб оцінка була не суворою , тобто невпинне обчислення не блокується назавжди, якщо результат не буде повністю необхідним. Скільки часу це насправді блокує до реалізації, у стандартному ледачому GHC він взагалі не блокується, поки ви не запитаєте результат.
перестали повертати проти годинника,

2
хаскелл класно.
blabla999

12

Невеличка розмова

Це створює новий метод на льоту, який
  створює новий метод на льоту, який
    створює новий метод на льоту, який
      ...
    ...
  ..
і потім передається йому.

Додаткова маленька спеція походить від того, що підкреслює стек-пам'ять І купує пам’ять одночасно, створюючи і довше, і довше ім'я методу, і величезну кількість в якості приймача, коли ми потрапляємо в діру ... (але рекурсія вражає нас першими ).

компілювати в цілий ряд:

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.

потім стрибайте, оцінюючи "2 downTheRabbitHole"...
... через деякий час, ви опинитесь на налагоджувачі, показуючи RecursionException.

Тоді вам доведеться очистити весь безлад (і в SmallInteger, і в LargeInteger зараз є безліч кодів чудес):

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

або ще провести деякий час у браузері, видаляючи країну чудес Аліси.

Ось дещо з голови сліду:

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: додано "withoutUpdatingChangesFile:", щоб потім не очищати стійкий файл журналу змін Smalltalk.

PPS: дякую за виклик: думати про щось нове та інноваційне було весело!

PPPS: Я хотів би зазначити, що деякі діалекти / версії Smalltalk копіюють переповнені кадри стека в купу - тому вони можуть наштовхуватися на ситуацію поза пам'яттю.


2
LOL +1 для цієї чорної магії в чистому вигляді
masterX244

Якщо я знайду якийсь час, я можу "вдосконалитись", використовуючи анонімний метод анонімного класу, щоб зробити найтемнішу лунку з кроликом колись ...
blabla999

12

C #

Дійсно великий struct, без рекурсії, чистий C #, не небезпечний код.

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

як ударник розбиває вікна налагодження, заявляючи, що {Cannot evaluate expression because the current thread is in a stack overflow state.}


І загальна версія (спасибі за пропозицію 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>>>>();
    }
}

Потрібно більше Узагальнений код на увазі: P
NPSF3000

Це виглядатиме як рекурсія, але це можливо за допомогою вкладених аргументів типу.
ja72

1
Я не бачив вашої відповіді С # структура, перш ніж я розмістив свою. У мене ще трохи інший підхід, тому, можливо, ми дамо їм співіснувати.
Томас Веллер

11

C #

Несправна реалізація переопределеного ==оператора:

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

Можна сказати, що очевидно, що operator==дзвонить сам за допомогою ==оператора, але зазвичай так не думаєш ==, тому легко потрапити в цю пастку.


11

Початок відповіді за допомогою SnakeYAML

class A
{

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

Редагувати

Читач дізнається, як це працює: P (порада: stackoverflow.com)

До речі: рекурсія динамічно проводиться SnakeYAML (ви помітите, якщо знаєте, як вона виявляє поля, які вона серіалізує, і перегляньте потім Point )

Редагувати: розповідаючи, як це працює:

SnakeYAML шукає пару getXXXі setXXXmthod з однаковою назвою для XXXі тип повернення геттера, що відповідає параметру setter; і дивно, що Pointклас має Point getLocation()і void setLocation(Point P)який повертається сам; SnakeYAML цього не помічає і повторюється на цій вигадці та StackOverflows. Виявив це, коли працював з ними всередині HashMapта запитував на сайті stackoverflow.com.


10

C #

неправильно реалізований майстер отримання власності

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

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

14
ІМХО. Це класичний приклад очевидної рекурсії ... (і так не вірно)
Оле Альберс

2
Що ж, це очевидно лише після того, як ви це зробили один раз і зрозуміли, що C # отримувачі не працюють так, як ви могли подумати, що вони зробили. Зрештою, цей код є декларацією змінної члена, то чому б йому не створити фактичну змінну члена?
meustrus

2
Це не більше, ніж звинувачений спосібstatic void Main() { Main(); }
Джодрелл

5
@Jodrell Ви не писали рекурсивно Main()випадково. Але записати рекурсивну властивість досить просто випадково, а потім переплутати переповнення стека.
svick

1
Це було б корисно для того, хто збирає C #.
2rs2ts
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.