Самый странный способ вызвать переполнение стека [закрыто]

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.

Несмотря на то, что я принял ответ сейчас, добавление дополнительных ответов все еще хорошо :)

masterX244
источник
14
Я стремлюсь к продукту, переходя на stackoverflow.com, хотя я, как известно, запрашивал «переполнение стека» в выбранной мной поисковой системе.
OJFord
21
Используйте Internet Explorer.
Верный
64
Самый странный способ вызвать переполнение стека - это опубликовать конкурс популярности на codegolf.stackexchange.com с просьбой опубликовать самый странный способ вызвать переполнение стека. Респонденты при тестировании своих решений вопроса будут вызывать переполнение стека. Я не проверял это, хотя, поэтому я не могу быть уверен, что это работает (именно поэтому я не отправил это как ответ).
Тим Сегин
3
Я неравнодушен к этому методу: joelonsoftware.com/items/2008/09/15.html
Роберт
11
Езжай на Тойоте (Эй, подожди, моя машина - Тойота ...)
брезгливое оссифраж

Ответы:

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
@ Нет, я не вижу проблемы. Что насчет этого решения неудовлетворительно?
SimonT
17
@ masterX244 Да, весь вопрос в том, «не делай это как обычно».
Plutor
24
@Plutor, вы обычно настраиваете StackOverFlow специально?
Kiwy
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. Будет работать для любой платформы, где адреса возврата находятся в стеке, но могут потребоваться изменения.

Конечно, запись вне массива - неопределенное поведение , и вы не можете гарантировать, что это вызовет переполнение стека, а не, скажем, раскрасит усы в синий цвет. Детали флагов платформы, компилятора и компиляции могут иметь большое значение.

ugoren
источник
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
Reactgular
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

Ява

Видел что-то вроде этого где-то здесь:

Изменить: нашел, где я видел это: ответ Джо К. на самую короткую программу, которая выдает ошибку 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 (...), что, в свою очередь, вызывает toString. Это делает вашу трассировку стека немного разнообразной (там есть три метода).
Paŭlo Ebermann
2
@ PaŭloEbermann Да, это правильно. Однако гораздо легче сказать, что это становится "" + this.toString().
Джастин
2
Я думаю, что это + "" +может спровоцировать людей, так как на первый взгляд это кажется бесполезным. String val;и return val + this;может быть немного хитрее
Cruncher
@ Cruncher нет совсем. Если вы Java-кодер, вы должны знать, что простой способ объединения int со строкой во ""время конструирования -String - это+ ""
Justin
5
В реальных приложениях я бы предпочел избежать бесконечной рекурсии и ошибок переполнения стека
jon_darkstar
77

С

Вполне легко:

int main()
{
    int large[10000000] = {0};
    return 0;
}
Shahbaz
источник
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
хороший способ спама в стеке: P
masterX244
62

JavaScript

window.toString = String.toLocaleString;
+this;
Райан Кавано
источник
5
Этот недооценен.
Ry-
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
Kiwy
9
Я склонен считать рекурсию в этом случае очевидной.
Темыр
1
@BlightlightShining Вызов getCause()метода не приводит к StackOverflowError. Он основан на том факте, что существует код JDK, который рекурсивно вызывает getCause()метод.
Виктор Стафуса
2
Я думал, что вы могли бы упростить это, изменив тело getCauseна just, 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 -ftemplate-deep = 40000
g ++: внутренняя ошибка компилятора: ошибка сегментации (программа cc1plus)
Пожалуйста, отправьте полный отчет об ошибке,
с предварительно обработанным источником, если это необходимо.
Смотрите инструкции.

В этом исходном файле нет рекурсии, ни один из классов не является базовым, даже косвенно. (В C ++ в шаблонном классе, как этот, S<1>и S<2>есть совершенно разные классы.) Ошибка сегментации происходит из-за переполнения стека после рекурсии в компиляторе.

HVD
источник
7
Признаюсь, я бы назвал это очевидной рекурсией в вашей метапрограмме.
2
GCC обнаруживает рекурсию и грациозно останавливается на этой стороне (4.8 и выше, похоже, в порядке)
Алек Тил
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» столкнется с проблемой повторного использования). Возможно, вам придется написать аналогичный скрипт для очистки.

Лучше попробуй это на виртуальной машине, если не уверен.

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

blabla999
источник
как я писал: во многих системах rm -rf пытается выйти из строя и удаляется одним (может быть, в наши дни не с 64-битным адресным пространством - но на небольших машинах с меньшим стеком это может быть). Конечно, могут быть и «rm» -реализации, которые делают это по-другому ...
blabla999
2
Кажется, что-то вроде while cd x; do :; done; cd ..; while rmdir x; cd ..; done;должно позаботиться об этом.
Blacklight Shining
Вы абсолютно правы (именно это я имел в виду под «похожим скриптом для очистки»). Однако, как только ваш диск (или квота) будет заполнен, вам может потребоваться войти в систему после этого, поскольку некоторые системы плохо справляются с этой ситуацией. Вам придется войти в систему как пользователь root и выполнить сценарий (что легко на современных компьютерах, но в древние времена это было сложно, когда компьютеры использовались более чем одним пользователем).
blabla999
забавно, это получает больше голосов, чем магия Smalltalk ниже (никогда не думал, что!)
blabla999
кто-то пробовал это на винде?
Sarge Borsch
43

Ява

Хороший из 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 и так далее, до бесконечности. Эти рекурсивные вызовы приводят к тому, StackOverflowErrorчто тело конструктора получает возможность выполнить. Поскольку StackOverflowErrorэто подтип, Errorа не Exceptionусловие maincatch, оно не улавливает его.

Ntoskrnl
источник
41

Латекс

\end\end

Стек ввода переполняется, потому что \endмногократно расширяется в бесконечном цикле, как описано здесь .

TeX терпит неудачу с TeX capacity exceeded, sorry [input stack size=5000]или подобным.

bwDraco
источник
1
Я ждал проблемы с TeX / LaTeX. Эти вещи более неприятны, чем большинство - обычно я забывал, что то, что я делаю, считается программированием, когда вдруг мне удалось написать что-то бесконечно рекурсивное.
Эрнир
1
«Превышена емкость TeX, извините» TeX настолько вежлив, когда терпит неудачу.
Алекс А.
40

BF

В конечном итоге переполнит стек, зависит только от того, как долго интерпретатор делает стек ...

+[>+]
Timtech
источник
5
Этот код напоминает мне игру жизни.
Адам Арольд
10
Строго говоря, в BrainFuck нет стека.
fejesjoco
6
@fejesjoco Tape, если хотите.
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):

Если вы удалите тег поддержки из URL-адреса, он переходит в бесконечные перенаправления . В следующем URL-адресе ###### - любой тег поддержки.

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

Я считаю, что это эквивалентно переполнению стека.

codeSetter
источник
5
хороший способ :) firefox говорит, что он перенаправляет, так что запрос не может быть решен, что является стекопотоком rquivalent :)
masterX244
3
Обратите внимание, что перенаправления не бесконечны - если вы нажмете «Попробовать еще раз», он добавит еще несколько /Errors/к URL, а затем прекратит работу после получения HTTP 400 Bad Request. Но это, вероятно, способствует лучшему переполнению стека, чем бесконечное перенаправление.
nandhp
@ 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/...
Вортико
31

PHP

Переполнение стека выполняется только с элементами зацикливания.

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

Пояснение (наведите курсор на спойлер):

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

bwoebi
источник
16
PHP GC не может обнаружить самоссылочные структуры? В самом деле?! Уч. То есть зло.
Питер С
1
Да, но это не идеально. Есть какая-то причина, почему while (1) { $a = array(&$a); }или что-то подобное просто достигает предела памяти…
bwoebi
да, я думаю, что это та же самая причина, почему PHP также не работает должным образом в отношении объектно-ориентированной разработки; (абстракция, наследование и т. д.)
pythonian29033
1
@ pythonian29033 хотите уточнить?
vvondra
30

Ява

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

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

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

Подсказка: программа выполняется за O (2 n ) времени, а n - размер стека (обычно 1024).

Из Java Puzzlers # 45:

Давайте предположим, что наша машина может выполнять 10 10 вызовов в секунду и генерировать 10 10 исключений в секунду, что довольно щедро по нынешним стандартам. В соответствии с этими предположениями, программа будет завершена примерно через 1,7 × 10 291 год. Чтобы поместить это в перспективе, время жизни нашего Солнца оценивается в 10 10 лет, так что с уверенностью , что никто из нас не будет вокруг , чтобы увидеть эту программу прекратить. Хотя это не бесконечный цикл, это может быть и так.

Ntoskrnl
источник
3
Очевидная рекурсия ... не очень интересная.
Ками
5
@ Ками Вы пробовали это? Вы действительно получили StackOverflowError? Это кажется очевидным, но это не так.
ntoskrnl
То, что исключение получено, не означает, что оно никогда не было выброшено. Эта программа выдает исключение переполнения стека после времени O (n)
CodesInChaos
1
@CodesInChaos Хорошо, так что я дважды проверил это, и правильная версия использует finallyвместо catch, и время выполнения O (2 ^ n). Ответ обновлен.
ntoskrnl
@ntorkrnl хороший +1 Кстати, у компилятора уже есть стекозагрузка (компилятор тоже работает внутри виртуальной машины)
masterX244
30

C #

Первый пост, поэтому, пожалуйста, будьте осторожны со мной.

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

Это просто создает трассировку стека, захватывает верхний кадр (который будет нашим последним вызовом Main()), получает метод и вызывает его.

BenM
источник
хороший способ selfinvoke, который не так очевиден без объяснения; + +1
masterX244
Вы должны прокомментировать этот код словами «что может пойти не так?»: P
Spaceman
26

Ява

  • В Java 5 printStackTrace() вводится бесконечный цикл.
  • В яве 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 из ничего - лучший друг для кодового троллинга и серьезная головная боль при их
поиске
2
Если кому-то интересно, эквивалентный код в C # вызывает бесконечный цикл. НоInnerException свойство доступно только для чтения и задается в конструкторе, поэтому для этого необходимо отражение.
Athari
17

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

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

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

Здесь нет никакой рекурсии. fбудет многократно каррироваться с большим количеством аргументов, пока не переполнит стек за один вызов функции. console.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, что делает именно это. И, видимо, он использует рекурсию.

fejesjoco
источник
да уж; сериализация ftw: P; но что более забавно, это когда причуды полностью выходят за пределы вашего кода, например, как snakeyaml получает атрибуты, и один класс возвращает себя способом, который бесконечно повторяется
masterX244
1
Что ж, переполнение действительно происходит вне моего кода, но настоящая причина, по которой я это опубликовал, заключается в неожиданном побочном эффекте в консоли :-)
fejesjoco
Я думаю, что белые биты должны быть связаны с .Net 4.5 или с тем, как настроена ваша среда, потому что при использовании .Net 4 (даже при запуске через cmd) я получаю только пробелы, а не белые блоки. Я предполагаю, что либо версия cmd для Win7 Enterprise, либо эмулятор консоли .Net 4.5 рассматривают определенную комбинацию символов как «изменение цвета фона консоли».
Pharap
Я не могу воспроизвести его с .NET 4.5 на Win7 x64 Professional с включенным Aero
Рэй
Также не могу воспроизвести. .NET 4.5, Windows 8.1 Pro.
bwDraco
13

удар

_(){ _;};_

Хотя многие могут признать, что рекурсия очевидна , но она кажется красивой. Нет?

После выполнения вы гарантированно увидите:

Segmentation fault (core dumped)
devnull
источник
5
этот красивее и хуже:_(){_|_;};_
RSFalcon7
1
@ RSFalcon7 Forkbomb alert! (Кроме того, не нужно ли после {синтаксически правильного пробела ?)
Blacklight Shining
3
Попробуйте это тоже:(){:|:;}:
Tarek Eldeeb
14
Похоже на жуткий смайлик-мутант.
Тим Сегин
8
Баш смайликов: пожиратель лиц, убийца стеков.
NobleUplift
12

Haskell

(грустно, но верно, по крайней мере ghc-7.6, хотя, с O1или больше, это решит проблему)

main = print $ sum [1 .. 100000000]
перестал поворачиваться против часовой стрелки
источник
не должен ли он выполнить оптимизацию хвостового вызова автоматически (в сумме)?
blabla999
2
@ blabla999: хвостовые вызовы не так уж актуальны в Хаскеле, это в основном накопление громадных потоков из-за скрытой лени, вызывающей такие проблемы. В этом случае проблема заключается в том, что sumон реализован в терминах foldl, в которых используются хвостовые вызовы, но поскольку он не оценивает аккумулятор, он просто создает кучу блоков, размером с исходный список. Проблема исчезает при переключении на foldl' (+), который строго оценивает и, таким образом, возвращает WHN в своем хвостовом вызове. Или, как я уже сказал, если вы включите оптимизацию GHC!
перестал поворачиваться против часовой стрелки с
ааа - интересно, так что, если бы никто не стал дожидаться грохота (т. е. пропустить отпечаток), сборщик мусора забрал бы их прочь (спереди назад)?
blabla999
1
Кстати, ничего из этого на самом деле не указано в стандарте Haskell: просто требуется, чтобы оценка не была строгой , то есть непрерывное вычисление не будет блокироваться вечно, если результат не требуется полностью. Сколько времени он действительно блокирует, зависит от реализации, в стандартном ленивом GHC он вообще не блокируется, пока вы не запросите результат.
перестал поворачиваться против часовой стрелки с
2
Haskell это круто.
blabla999
12

Болтовня

Это создает новый метод на лету, который
  создает новый метод на лету, который
    создает новый метод на лету, который
      ...
    ...
  ..
и затем передает ему.

Дополнительная небольшая специя возникает из-за одновременной нагрузки на стековую память и кучную память путем создания более длинного и длинного имени метода и огромного числа в качестве получателя, когда мы падаем в дыру ... (но рекурсия поражает нас первой ).

скомпилировать в Integer:

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 копируют переполненные стековые кадры в кучу - так что они могут столкнуться с нехваткой памяти.

blabla999
источник
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>>>>();
    }
}
ja72
источник
Требуется больше
метинков
Это будет выглядеть как рекурсия, но это возможно с аргументами вложенного типа.
ja72
1
Не видел ваш ответ C # struct, прежде чем я опубликовал свой. У меня все еще немного другой подход, поэтому, возможно, мы позволим им сосуществовать.
Томас Веллер
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 (tip: stackoverflow.com)

Кстати: рекурсия динамически выполняется SnakeYAML (вы заметите, если знаете, как он обнаруживает поля, которые он сериализует, и посмотрите в исходном коде России Point)

Редактировать: рассказывая, как это работает:

SnakeYAML ищет пару getXXXи setXXXmthod с тем же именем, XXXи тип возвращаемого значения получателя совпадает с параметром установщика; и удивительно, что у Pointкласса есть Point getLocation()и void setLocation(Point P)который возвращает себя; SnakeYAML этого не замечает и повторяет эту причуду и StackOverflows. Обнаружил это, когда работал с ними внутри HashMapи спрашивал об этом на stackoverflow.com.

masterX244
источник
10

C #

неправильно реализованный объект-получатель

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

static void Main()
{
   int p = new C().P;
}
Alberto
источник
14
ИМХО. Это классический пример очевидной рекурсии ... (и так недопустимый)
Оле Альберс
2
Ну, это очевидно только после того, как вы сделали это один раз и выяснили, что получатели C # работают не так, как вы, возможно, думали, что они это сделали. В конце концов, этот код является объявлением переменной-члена, так почему бы ему не создать фактическую переменную-член?
meustrus
2
Это не более чем запутанный способ сделатьstatic void Main() { Main(); }
Джодрелл
5
@Jodrell Ты бы не стал писать рекурсивно Main()случайно. Но довольно просто случайно написать рекурсивное свойство, а затем перепутать его с переполнением стека.
svick
1
Это было бы полезно для кого-то взяв C #.
2rs2ts