Cách kỳ lạ nhất để tạo ra một ngăn xếp tràn [đóng]


146

Là một lập trình viên, bạn chắc chắn biết lỗi tràn ngăn xếp do đệ quy rõ ràng. Nhưng chắc chắn có nhiều cách kỳ lạ và bất thường để khiến ngôn ngữ yêu thích của bạn thoát ra lỗi đó.

Mục tiêu:

  1. Phải gây ra lỗi tràn ngăn xếp hiển thị rõ ràng trên đầu ra lỗi.
  2. Không được phép sử dụng một đệ quy rõ ràng.

Ví dụ về các chương trình không hợp lệ:

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

Những cách sáng tạo nhất là tốt nhất vì đây là một . Tức là tránh những câu trả lời rõ ràng nhàm chán như thế này:

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

Mặc dù tôi đã chấp nhận một câu trả lời ngay bây giờ, nhưng thêm nhiều câu trả lời vẫn ổn :)


14
Tôi có xu hướng sản xuất bằng cách điều hướng đến stackoverflow.com, mặc dù tôi đã biết truy vấn 'stack overflow' trên công cụ tìm kiếm mà tôi chọn.
OJFord

21
Sử dụng Internet Explorer. Một cách chắc chắn để bắt một con :)
vào

64
Cách kỳ lạ nhất để tạo ra tràn ngăn xếp là đăng một cuộc thi phổ biến trên codegolf.stackexchange.com yêu cầu mọi người đăng cách kỳ lạ nhất để tạo ra tràn ngăn xếp. Những người trả lời, khi kiểm tra các giải pháp của họ cho câu hỏi, sẽ tạo ra một tràn ngăn xếp. Mặc dù tôi chưa kiểm tra nó, vì vậy tôi không thể chắc chắn nó hoạt động (đó là lý do tại sao tôi không đăng nó dưới dạng câu trả lời).
Tim Seguine

3
Tôi là một phần của phương pháp này: joelonsoftware.com/items/2008/09/15.html
robert

11
Lái xe Toyota (Này, đợi một chút, xe của tôi một chiếc Toyota ...)
ossifrage vuông vức

Câu trả lời:


224

Con trăn

import sys
sys.setrecursionlimit(1)

Điều này sẽ khiến trình thông dịch bị lỗi ngay lập tức:

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

Thay vì sử dụng đệ quy, nó chỉ thu nhỏ stack nên nó sẽ tràn ngay lập tức.


12
Dễ thương, nhưng không hoàn toàn những gì câu hỏi ban đầu được nhắm đến theo ý kiến ​​của tôi.
Nit

12
@ Tôi không thấy vấn đề. Điều gì về giải pháp này là không đạt yêu cầu?
SimonT

17
@ masterX244 Có, toàn bộ vấn đề là "đừng làm theo cách thông thường".
Plutor

24
@Plutor bạn có thường thiết lập StackOverFlow trên mục đích không?
Kiwy

15
Điều này chỉ thông minh nhẹ ở lần đầu tiên nó được đăng .
primo

189

Con trăn

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

7
Tuyệt vời. Đẹp và thanh lịch.
James Webster

103
Nghĩa đen nhiều. Rất trả lời.
Tim Seguine

47
Chà, điều này cho thấy Stack Overflow, nhưng để tạo ra một cái, nó chỉ hoạt động nếu đó là Jeff Atwood hoặc Joel Spolsky thực thi nó.
Ốc cơ khí

10
@TimSeguine Wow.
Sean Allred

9
Không phải là một câu trả lời vì nó không có trong đầu ra lỗi.
Pierre Arlaud

131

C / Linux 32 bit

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

Hoạt động bằng cách ghi đè địa chỉ trả về, Vì vậy, gquay trở lại điểm maintrước khi gọi f. Sẽ hoạt động cho bất kỳ nền tảng nào có địa chỉ trả về trên ngăn xếp, nhưng có thể yêu cầu chỉnh sửa.

Tất nhiên, viết bên ngoài một mảng là hành vi không xác định và bạn không có gì đảm bảo rằng nó sẽ gây ra lỗi tràn ngăn xếp hơn là nói, tô màu ria mép của bạn. Chi tiết về nền tảng, trình biên dịch và cờ biên dịch có thể tạo ra sự khác biệt lớn.


1
Điều này sẽ không tạo ra một segfault?
11684

4
+1 cho thao tác ngăn xếp kỳ lạ và hoàn toàn không có đệ quy!
RSFalcon7

6
@ 11684, Đó là hành vi không xác định, vì vậy nói chung nó có thể bị sập. Trên Linux 32 bit (mà tôi đã thử nghiệm), nó ghi bên ngoài mảng, ghi đè địa chỉ trả về và không bị sập cho đến khi ngăn xếp tràn ra.
ugoren

46
"Vẽ ria mép của bạn màu xanh" -> Dòng đó có tôi.
Aneesh Dogra

2
Ồ Điều này thật tuyệt vời.
MirroredFate

108

JavaScript / DOM

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

    title = 'Kill me!';
}

Nếu bạn muốn giết trình duyệt của mình, hãy thử điều đó trong bảng điều khiển.


53
Tôi chắc chắn rằng bạn xứng đáng được nhiều phiếu +1 hơn, nhưng thật đáng buồn là mọi người đã thử điều này trước khi bỏ phiếu .... lol
Reactgular

5
Vâng, đó là hiệu quả. Tôi thậm chí không thể mở trình quản lý tác vụ của mình để giết nó.
primo

1
Sử dụng Chrome, đóng tab. Vấn đề được giải quyết.
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
Chết tiệt Firefox của tôi đã bị treo
Farhad

91

Java

Nhìn thấy một cái gì đó như thế này ở đâu đó quanh đây:

Chỉnh sửa: Tìm thấy nơi tôi thấy: Câu trả lời của Joe K cho chương trình ngắn nhất gây ra lỗi StackOverflow

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

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

Điều đó có thể gây nhầm lẫn cho một số người mới bắt đầu Java. Nó chỉ đơn giản là ẩn cuộc gọi đệ quy. val + thistrở thành val + this.toString()bởi vì vallà một Chuỗi.

Xem nó chạy ở đây: http://ideone.com/Z0sXiD


30
Trên thực tế, nó thực hiện new StringBuilder().append(val).append("").append(this).toString()và phần bổ sung cuối cùng gọi String.valueOf (...), lần lượt gọi tớiString. Điều này làm cho Stack của bạn theo dõi một chút khác nhau (ba phương thức trong đó).
Paŭlo Ebermann

2
@ PaŭloEbermann Vâng, đúng vậy. Tuy nhiên, nó dễ dàng hơn nhiều để nói rằng nó trở thành "" + this.toString().
Justin

2
Tôi nghĩ rằng + "" +có thể khiến mọi người thất vọng, vì thoạt nhìn có vẻ vô dụng. String val;return val + this;có thể lén lút hơn một chút
Cruncher

@Cruncher hoàn toàn không. Nếu bạn là một lập trình viên Java, bạn sẽ biết rằng cách dễ dàng ghép nối một int thành một chuỗi trong khi ""xây dựng -String là với+ ""
Justin

5
Trong một ứng dụng trong thế giới thực, tôi muốn tránh đệ quy vô hạn và lỗi tràn ngăn xếp
jon_darkstar 23/214

77

C

Khá dễ dàng:

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

10
+1 cho không rõ ràng! Mặc dù nó phụ thuộc rất nhiều vào hệ thống (một ulimit -s unlimitedlớp vỏ giải quyết điều này trong linux)
RSFalcon7

4
@ RSFalcon7, cảm ơn vì +1, nhưng với tôi đây thực sự là điều rõ ràng nhất !!
Shahbaz

14
@Cruncher: Nó không dẫn đến đệ quy. Vấn đề được đưa ra là để thổi chồng. Trên nhiều hệ điều hành, ngăn xếp có kích thước cố định và nhỏ hơn mười triệu ints, do đó, điều này sẽ thổi bay ngăn xếp.
Eric Lippert

2
@HannoBinder, Trong Linux nơi tôi đã kiểm tra, bạn không gặp phải lỗi tràn ngăn xếp. Bạn gặp lỗi phân đoạn, do tràn ngăn xếp dẫn đến quyền truy cập vào các phân đoạn chưa được đặt tên. Tôi không chắc liệu lỗi tràn ngăn xếp có tồn tại trong Linux hay không, vì một lệnh gọi hàm đệ quy vô hạn cũng đưa ra lỗi phân đoạn.
Shahbaz

3
~0ulà một con số khá lớn ở C.
Vortico

63

Tràn ngăn xếp không đệ quy trong C

Gọi quy ước không khớp.

typedef void __stdcall (* ptr) (int);

void __cdecl hello (int x) { }

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

Biên dịch với gcc -O0.

__cdeclcác hàm mong muốn người gọi dọn sạch ngăn xếp và __stdcallmong đợi callee thực hiện nó, vì vậy bằng cách gọi qua con trỏ hàm typecast, việc dọn dẹp không bao giờ được thực hiện - mainđẩy tham số lên ngăn xếp cho mỗi cuộc gọi nhưng không có gì bật ra và cuối cùng là chồng chất đầy.


2
cách tốt để spam ngăn xếp: P
masterX244

62

JavaScript

window.toString = String.toLocaleString;
+this;

5
Điều này là không được đánh giá cao.
Ry-

1
Không gì +thislàm gì?
NobleUplift

8
Unary + gọi hoạt động trừu tượng ToNumber. Điều đó sử dụng hoạt động ToPrimitive với một loại số gợi ý. ToPrimitive trên một đối tượng sử dụng phương thức bên trong [[DefaultValue]], khi được thông qua một gợi ý số, trước tiên hãy kiểm tra xem phương thức valueOf () có tồn tại không và trả về nguyên hàm. window.valueOf () trả về một đối tượng, vì vậy thay vào đó [[DefaultValue]] trả về kết quả của việc gọi toString (). Điều đầu tiên window.toString thực hiện (bây giờ là toLocaleString) được gọi toString trên 'this'. Nói lại.
Ryan Cavanaugh

3
Nếu Chrome đưa ra một lỗi, 'Quá nhiều đệ quy', thì nó có hoạt động trên Chrome không? Ngăn xếp tràn ra và đó là cách Chrome cung cấp ngoại lệ tràn ngăn xếp.
David Conrad

4
Bạn nên sử dụng +{toString:"".toLocaleString}:-)
Bergi

62

Tôi đã thất vọng bởi thực tế là java 7 và java 8 miễn nhiễm với mã xấu trong câu trả lời trước của tôi . Vì vậy, tôi quyết định rằng một bản vá cho điều đó là cần thiết.

Sự thành công! Tôi đã printStackTrace()ném a StackOverflowError. printStackTrace()thường được sử dụng để gỡ lỗi và ghi nhật ký và không ai nghi ngờ rằng nó có thể nguy hiểm. Không khó để thấy rằng mã này có thể bị lạm dụng để tạo ra một số vấn đề bảo mật nghiêm trọng:

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

Một số người có thể nghĩ rằng đây là một đệ quy rõ ràng. Không phải vậy. Hàm EvilExceptiontạo không gọi getCause()phương thức, do đó, ngoại lệ thực sự có thể được ném an toàn. Gọi getCause()phương thức sẽ không dẫn đến một StackOverflowErrortrong hai. Đệ quy nằm trong printStackTrace()hành vi không được xem xét thông thường của JDK và bất kỳ thư viện bên thứ 3 nào để gỡ lỗi và ghi nhật ký được sử dụng để kiểm tra ngoại lệ. Hơn nữa, có khả năng nơi ném ngoại lệ rất xa so với nơi được xử lý.

Dù sao, đây là một mã mà ném một StackOverflowErrorvà không chứa các cuộc gọi phương thức đệ quy sau tất cả. Điều này StackOverflowErrorxảy ra bên ngoài mainphương thức, trong JDK's 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 và bây giờ với phương thức tuân thủ stackoverflow phiên bản java chéo. Tên khốn độc ác! : D
Kiwy

9
Tôi có khuynh hướng xem xét đệ quy trong trường hợp này là rõ ràng.
Taemyr

1
@BlacklightShining Gọi getCause()phương thức không có kết quả StackOverflowError. Thực tế là có một mã của JDK đang gọi đệ quy getCause()phương thức.
Victor Stafusa

2
Tôi nghĩ bạn có thể đơn giản hóa điều này bằng cách thay đổi cơ thể getCausethành chỉ return this;nhưng rõ ràng Java quá thông minh cho việc đó. Nó thông báo rằng đó là một " CIRCULAR REFERENCE".
David Conrad

1
Tôi không nhận được StackOverflowErrorvì gói OpenBSD 5.5 của jdk-1.7.0.21p2v0 có lỗi. Nó không ném StackOverflowError. Nó đánh SIGSEGVvà vứt lõi.
hạt nhân

57

Linux x86 Hội 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

Spoiler:

Quên trở về từ printHello, vì vậy chúng tôi nhảy ngay vào _start một lần nữa.


78
Trong lắp ráp, không có gì là rõ ràng.
11684

21
@ 11684: Tôi thấy điều ngược lại là đúng: trong lắp ráp, mọi thứ đều rõ ràng vì không ai có thể sử dụng trừu tượng để ẩn những gì mã của họ đang thực sự làm.
Mason Wheeler

3
Điều này là tuyệt vời cho sự đơn giản và thanh lịch của nó.
haneefmubarak

11
@MasonWheeler: Tôi thích nói rằng mọi thứ đều có thể nhìn thấy thay vì hiển nhiên ... Đối với một cách tốt đẹp để thấy sự khác biệt giữa có thể nhìn thấy và rõ ràng, tôi yêu để chỉ underhanded.xcott.com
Olivier Dulac

2
Tôi nhớ những lỗi thường xuyên quên của mìnhret
Ruslan

48

C ++ tại thời gian biên dịch

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

template <>
struct S<0> {};

template
struct S<-1>;
$ g ++ -c test.cc -ftemplate-height = 40000
g ++: lỗi trình biên dịch nội bộ: Lỗi phân đoạn (chương trình cc1plus)
Vui lòng gửi báo cáo lỗi đầy đủ,
với nguồn tiền xử lý nếu thích hợp.
Xem để được hướng dẫn.

Không có đệ quy tệp nguồn này, không phải một trong các lớp có chính nó là một lớp cơ sở, thậm chí không gián tiếp. (Trong C ++, trong một lớp mẫu như thế này S<1>S<2>là các lớp hoàn toàn khác biệt.) Lỗi phân đoạn là do tràn ngăn xếp sau khi đệ quy trong trình biên dịch.


7
Tôi thú nhận rằng tôi đã gọi đây là một đệ quy rõ ràng trong chương trình siêu dữ liệu của bạn.

2
GCC phát hiện đệ quy và dừng lại một cách duyên dáng ở bên này (4,8 trở lên có vẻ vẫn ổn)
Alec Teal

2
template <typename T> auto foo(T t) -> decltype(foo(t)); decltype(foo(0)) x;ngắn hơn một chút
Casey

2
@hvd Có vẻ như đang khai thác một lỗi trong GCC. clang nắm bắt việc sử dụng sai - mà tôi cho rằng bạn đã biết - nhưng nó khiến GCC của tôi phát ra gần 2 megabyte thông báo lỗi.
Casey


45

Bash (Cảnh báo nguy hiểm)

while true
do 
  mkdir x
  cd x
done

Nói một cách chính xác, điều đó sẽ không trực tiếp ngăn xếp tràn, nhưng tạo ra cái có thể được gắn nhãn là " tình huống tạo chồng quá dòng liên tục ": khi bạn chạy nó cho đến khi đĩa của bạn đầy, và muốn xóa mớ hỗn độn bằng "rm -rf x ", cái đó bị đánh.

Nó không xảy ra trên tất cả các hệ thống, mặc dù. Một số mạnh mẽ hơn những người khác.

CẢNH BÁO nguy hiểm lớn:

một số hệ thống xử lý việc này rất tệ và bạn có thể rất khó khăn trong việc dọn dẹp (vì bản thân "rm -rf" sẽ gặp phải vấn đề truy hồi). Bạn có thể phải viết một kịch bản tương tự để dọn dẹp.

Tốt hơn nên thử điều này trong một VM đầu nếu không chắc chắn.

PS: tất nhiên áp dụng tương tự, nếu được lập trình hoặc thực hiện trong một tập lệnh bó.
PPS: có thể can thiệp để nhận được bình luận từ bạn, cách hệ thống cụ thể của bạn hoạt động ...


như tôi đã viết: trên nhiều hệ thống, rm -rf cố gắng đi xuống và bị tấn công bởi một người (có thể không còn nữa, ngày nay, với không gian địa chỉ 64 bit - nhưng trên các máy nhỏ hơn có ngăn xếp nhỏ hơn, có thể). Tất nhiên, cũng có thể có "rm" - thực hiện xung quanh, hoạt động khác đi ...
blabla999

2
Dường như với tôi một cái gì đó như while cd x; do :; done; cd ..; while rmdir x; cd ..; done;nên quan tâm đến điều này.
Blacklight Shining

Bạn hoàn toàn đúng (đó là ý của tôi với "kịch bản tương tự để dọn dẹp"). Tuy nhiên, khi đĩa của bạn (hoặc hạn ngạch) đã đầy, bạn thậm chí có thể gặp khó khăn khi đăng nhập sau đó, vì một số hệ thống được sử dụng để xử lý tình huống đó rất tệ. Bạn sẽ phải vào quyền root và thực hiện tập lệnh (điều này dễ dàng trên các PC ngày nay, nhưng thường rất khó trong thời cổ đại, khi các máy tính được sử dụng bởi nhiều hơn một người dùng).
blabla999

thật buồn cười, điều này nhận được nhiều phiếu hơn so với phép thuật Smalltalk bên dưới (không bao giờ nghĩ vậy!)
blabla999

ai đó đã thử điều này trên Windows?
Sange Borsch

43

Java

Một cái hay từ Java Puzzlers . Nó in cái gì?

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

Nó thực sự thất bại với StackOverflowError.

Ngoại lệ trong hàm tạo chỉ là một cá trích đỏ. Đây là những gì cuốn sách đã nói về nó:

Khi bạn gọi một hàm tạo, các bộ khởi tạo biến thể hiện chạy trước phần thân của hàm tạo . Trong trường hợp này, trình khởi tạo cho biến internalInstancegọi hàm tạo theo cách đệ quy. Ngược lại, hàm tạo đó khởi tạo trường của chính nó internalInstancebằng cách gọi lại hàm tạo bất đắc dĩ, v.v., ad infinitum. Các yêu cầu đệ quy này gây ra StackOverflowErrortrước khi cơ quan xây dựng có cơ hội thực thi. Bởi vì StackOverflowErrorlà một kiểu con Errorthay vì Exception, mệnh đề bắt trong mainkhông bắt được nó.


41

Mủ cao su

\end\end

Ngăn xếp đầu vào tràn vì \endliên tục mở rộng chính nó trong một vòng lặp vô hạn, như được giải thích ở đây .

TeX thất bại với một TeX capacity exceeded, sorry [input stack size=5000]hoặc tương tự.


1
Tôi đã chờ đợi một vấn đề TeX / LaTeX. Những thứ đó chói tai hơn hầu hết - thông thường, tôi đã quên rằng những gì tôi đang làm được tính là lập trình khi đột nhiên tôi xoay sở để viết một cái gì đó là đệ quy vô hạn.
Ernir


1
"Công suất TeX vượt quá, xin lỗi" TeX rất lịch sự khi thất bại.
Alex A.

40

BF

Cuối cùng sẽ tràn vào ngăn xếp, chỉ phụ thuộc vào thời gian trình thông dịch tạo ra ngăn xếp ...

+[>+]

5
Mã đó nhắc nhở tôi về trò chơi của cuộc sống.
Adam Arold

10
Nói đúng ra, không có stack trong brainfuck.
fejesjoco

6
@fejesjoco Băng, nếu bạn muốn.
TimTech

32

C #, tại thời điểm biên dịch

Có một số cách để khiến trình biên dịch Microsoft C # thổi tung ngăn xếp của nó; bất cứ khi nào bạn thấy một lỗi "biểu thức quá phức tạp để biên dịch" từ trình biên dịch C # gần như chắc chắn là do ngăn xếp đã bị thổi.

Trình phân tích cú pháp có nguồn gốc đệ quy, do đó, bất kỳ cấu trúc ngôn ngữ nào được lồng đủ sâu sẽ thổi vào ngăn xếp:

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

Trình phân tích cú pháp biểu thức khá thông minh về việc loại bỏ các lần truy cập ở phía thường được đệ quy. Thông thường:

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

xây dựng một cây phân tích cực kỳ sâu, sẽ không thổi tung đống. Nhưng nếu bạn buộc đệ quy xảy ra ở phía bên kia:

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

sau đó ngăn xếp có thể được thổi.

Những cái này có tài sản không phù hợp mà chương trình rất lớn. Cũng có thể làm cho bộ phân tích ngữ nghĩa đi vào các lần thu hồi không giới hạn với một chương trình nhỏ vì nó không đủ thông minh để loại bỏ các chu kỳ kỳ lạ nhất định trong hệ thống loại. (Roslyn có thể cải thiện điều này.)

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? 

Tôi mô tả lý do tại sao phân tích này đi vào một đệ quy vô hạn ở đây:

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

và để biết thêm nhiều ví dụ thú vị bạn nên đọc bài viết này:

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


2
Thông báo lỗi chính xác là fatal error CS1647: An expression is too long or complex to compile near (code). Tài liệu cho thông báo lỗi này có ở đây và chính xác như bạn nói: "Có một lỗi tràn ngăn xếp trong trình biên dịch xử lý mã của bạn."
bwDraco

32

Trên Internet (được sử dụng bởi tỷ người / ngày)

Redirects, HTTP status code: 301

Ví dụ: trên trang web hỗ trợ của Dell (không vi phạm, xin lỗi Dell):

Nếu bạn xóa TAG hỗ trợ khỏi URL thì nó sẽ chuyển hướng vô hạn . Trong URL sau, ###### là bất kỳ TAG hỗ trợ nào.

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

Tôi tin rằng nó tương đương với một chồng tràn.


5
cách hay :) firefox nói rằng nó chuyển hướng để yêu cầu không thể được giải quyết, đó là rquivalent stackoverflow :)
masterX244

3
Lưu ý rằng các chuyển hướng không phải là vô hạn - nếu bạn nhấn "Thử lại", nó sẽ thêm một vài lần nữa /Errors/vào URL và sau đó dừng lại sau khi nhận được Yêu cầu xấu HTTP 400. Nhưng điều này được cho là làm cho một ngăn xếp tràn tốt hơn so với chuyển hướng vô hạn.
nandhp

@nandhp, tôi đồng ý, nhưng nếu bạn nghĩ rằng một trình duyệt thế giới thứ ba (không hiện đại, IE, v.v.), họ không có manh mối về tình huống này.
codeSetter

2
Đây là cách Wget trả lời ở đây: 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

Một stackoverflow được thực hiện chỉ với các phần tử lặp.

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

Giải thích (di chuột để xem spoiler):

Chương trình sẽ segfault khi trình thông dịch cố gắng thu gom $tmpmảng đi (khi gán lại $tmpở đây). Chỉ vì mảng quá sâu (tự tham chiếu) và sau đó trình thu gom rác kết thúc trong một đệ quy.


16
GC của PHP không thể phát hiện các cấu trúc tự tham chiếu? Có thật không?! Ôi. Đó xấu xa.
Peter C

1
Vâng, nhưng nó không hoàn hảo. Có một số lý do tại sao while (1) { $a = array(&$a); }hoặc một cái gì đó tương tự chỉ đạt giới hạn bộ nhớ
bwoebi

vâng, tôi nghĩ đó là lý do tương tự tại sao PHP cũng không hoạt động đúng đối với sự phát triển hướng đối tượng; (trừu tượng, kế thừa, v.v.)
pythonia29033

1
@ pythonia29033 quan tâm đến công phu?
vvondra

30

Java

Tôi đã làm ngược lại chính xác - một chương trình rõ ràng sẽ gây ra lỗi tràn ngăn xếp, nhưng không.

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

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

Gợi ý: chương trình chạy trong thời gian O (2 n ) và n là kích thước của ngăn xếp (thường là 1024).

Từ Java Puzzlers # 45:

Giả sử rằng máy của chúng tôi có thể thực hiện 10 10 cuộc gọi mỗi giây và tạo ra 10 10 ngoại lệ mỗi giây, khá hào phóng theo các tiêu chuẩn hiện tại. Theo các giả định này, chương trình sẽ chấm dứt trong khoảng 1,7 × 10 291 năm. Để đặt điều này trong viễn cảnh, thời gian tồn tại của mặt trời của chúng ta được ước tính là 10 10 năm, do đó, đặt cược an toàn là không ai trong chúng ta sẽ ở xung quanh để thấy chương trình này chấm dứt. Mặc dù nó không phải là một vòng lặp vô hạn, nó cũng có thể như vậy.


3
Rõ ràng đệ quy ... không thú vị lắm.
Kami

5
@Kami Bạn đã thử chưa? Bạn đã thực sự có được một StackOverflowError? Nó có vẻ rõ ràng, nhưng nó không phải là.
ntoskrnl

Chỉ vì một ngoại lệ bị bắt không có nghĩa là nó không bao giờ bị ném. Chương trình này đưa ra một ngoại lệ tràn ngăn xếp sau thời gian O (n)
CodeInChaos

1
@CodesInChaos Được rồi, vì vậy tôi đã kiểm tra kỹ điều này và phiên bản chính xác sử dụng finallychứ không phải catchvà thời gian chạy là O (2 ^ n). Trả lời cập nhật.
ntoskrnl

@ntorkrnl đẹp một + 1'd; btw đã có trình biên dịch để stackoverflow rồi (trình biên dịch chạy bên trong VM, quá fyi)
masterX244

30

C #

Bài đầu tiên, vì vậy xin vui lòng đi dễ dàng trên tôi.

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

Điều này chỉ đơn giản là tạo ra một dấu vết ngăn xếp, lấy khung trên cùng (sẽ là lệnh gọi cuối cùng của chúng ta Main()), lấy phương thức và gọi nó.


cách tốt đẹp của selfinvoke mà không rõ ràng mà không giải thích; + 1'd
masterX244

Bạn nên nhận xét mã đó với "điều gì có thể xảy ra?": P
Spaceman

26

Java

  • Trong Java 5, printStackTrace()nhập một vòng lặp vô hạn.
  • Trong Java 6, printStackTrace()ném StackOverflowError.
  • Trong Java 7 và 8, nó đã được sửa.

Điều điên rồ là trong Java 5 và 6, nó không xuất phát từ mã người dùng, nó xảy ra trong mã của JDK. Không ai nghi ngờ hợp lý printStackTrace()có thể nguy hiểm để thực hiện.

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
vâng; stackoverflows không có gì là người bạn tốt nhất cho việc lập trình và đau đầu nghiêm trọng khi săn em
masterX244

2
Nếu bất cứ ai tự hỏi, mã tương đương trong C # gây ra một vòng lặp vô hạn. Nhưng thuộc InnerExceptiontính là chỉ đọc và được đặt trong hàm tạo, do đó sự phản chiếu là cần thiết để gây ra điều này.
Athari

17

JavaScript: Không đột biến, đột biến hàm lặp

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

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

Không có đệ quy nào ở đây cả. fsẽ được lặp đi lặp lại với nhiều đối số hơn và nhiều hơn cho đến khi nó có thể tràn vào ngăn xếp trong một lệnh gọi hàm duy nhất. Phần console.lognày là tùy chọn trong trường hợp bạn muốn xem cần bao nhiêu đối số để thực hiện. Nó cũng đảm bảo rằng các công cụ JS thông minh sẽ không tối ưu hóa điều này.

Phiên bản mã golf trong CoffeeScript, 28 ký tự:

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

14

C # với một thất bại sử thi

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

Cách nó thất bại là tuyệt vời, nó hoàn toàn thổi vào tâm trí tôi:

nhập mô tả hình ảnh ở đây

Nó chỉ là một khung hình của một chuỗi những hình ảnh kỳ lạ dường như vô tận.

Đây phải là điều kỳ lạ nhất. Ai có thể giải thích? Rõ ràng, lượng không gian ngày càng tăng được sử dụng để thụt đầu dòng khiến những khối trắng đó xuất hiện. Nó xảy ra trên Win7 Enterprise x64 với .NET 4.5.

Tôi chưa thực sự nhìn thấy sự kết thúc của nó. Nếu bạn thay thế System.Console.Outbằng System.IO.Stream.Null, nó chết khá nhanh.

Lời giải thích khá đơn giản. Tôi tạo một lớp có một thuộc tính duy nhất và nó luôn trả về một thể hiện mới của kiểu chứa nó. Vì vậy, nó là một hệ thống phân cấp đối tượng sâu vô hạn. Bây giờ chúng ta cần một cái gì đó cố gắng đọc qua đó. Đó là nơi tôi sử dụng XmlSerializer, chỉ có thế. Và rõ ràng, nó sử dụng đệ quy.


vâng; nối tiếp ftw: P; nhưng buồn cười hơn là khi quirk hoàn toàn nằm ngoài mã của bạn như cách Snakeyaml có được các thuộc tính và một lớp sẽ tự trả về theo cách mà đệ quy một cách kỳ lạ
masterX244

1
Chà, lỗi tràn xảy ra bên ngoài mã của tôi, nhưng lý do thực sự tôi đăng bài này là do hiệu ứng phụ không mong muốn trong bảng điều khiển :-)
fejesjoco

Tôi nghĩ rằng các bit trắng phải được thực hiện với .Net 4.5 hoặc cách môi trường của bạn được thiết lập, bởi vì sử dụng .Net 4 (ngay cả khi chạy qua cmd) tôi chỉ nhận được khoảng trắng, không có khối trắng. Tôi đoán là phiên bản Wind Enterprise của cmd hoặc trình giả lập bảng điều khiển .Net 4.5 xử lý một tổ hợp ký tự nhất định là 'thay đổi màu nền của giao diện điều khiển'.
Pharap

Tôi không thể sao chép nó với .NET 4.5 trên Win7 x64 Professional khi bật Aero
Ray

Không thể sao chép, một trong hai. .NET 4.5, Windows 8.1 Pro.
bwDraco

13

bash

_(){ _;};_

Trong khi nhiều người có thể nhận ra rằng đệ quy là rõ ràng , nhưng nó có vẻ khá. Không?

Khi thực hiện, bạn được đảm bảo để xem:

Segmentation fault (core dumped)

5
cái này đẹp hơn và tệ hơn:_(){_|_;};_
RSFalcon7

1
@ RSFalcon7 Cảnh báo Forkbomb! (Ngoài ra, không phải nó cần một khoảng trắng sau khi {được tổng hợp chính xác sao?)
Blacklight Shining

3
Hãy thử điều này là tốt:(){:|:;}:
Tarek Eldeeb

14
Trông giống như một biểu tượng cảm xúc đáng sợ, đột biến.
Tim Seguine

8
Biểu tượng cảm xúc Bash: người ăn mặt, kẻ giết người.
NobleUplift

12

Haskell

(buồn nhưng đúng cho đến khi ít nhất ghc-7.6, dù có O1hoặc nhiều hơn nó sẽ tối ưu hóa vấn đề)

main = print $ sum [1 .. 100000000]

không nên tự động tối ưu hóa cuộc gọi đuôi (tổng hợp)?
blabla999

2
@ blabla999: các cuộc gọi đuôi không liên quan đến Haskell, đó chủ yếu là sự tích tụ do sự lười biếng ngấm ngầm gây ra những vấn đề như vậy. Trong trường hợp này, vấn đề là sumđược thực hiện theo nghĩa foldl, trong đó sử dụng các cuộc gọi đuôi, nhưng vì nó không đánh giá bộ tích lũy chỉ đơn thuần tạo ra một đống thunks lớn như danh sách ban đầu. Vấn đề biến mất khi chuyển sang foldl' (+), đánh giá nghiêm ngặt và do đó trả về WHN trong lệnh gọi đuôi của nó. Hoặc, như tôi đã nói, nếu bạn bật tối ưu hóa của GHC!
đã ngừng quay ngược chiều

aah - thật thú vị, vậy nếu không ai chờ đợi thunk (tức là bỏ đi bản in), người thu gom rác sẽ thu gom chúng đi (từ trước ra sau)?
blabla999

1
BTW, không có điều nào trong số này thực sự được chỉ định bởi tiêu chuẩn Haskell: chỉ yêu cầu đánh giá là không nghiêm ngặt , tức là một tính toán không có giá trị sẽ không bị chặn vĩnh viễn nếu kết quả không được yêu cầu đầy đủ. Thời gian thực sự chặn tùy thuộc vào việc triển khai, trong GHC lười biếng tiêu chuẩn, nó hoàn toàn không chặn cho đến khi bạn yêu cầu kết quả.
đã ngừng quay ngược chiều

2
haskell là mát mẻ.
blabla999

12

Smalltalk

Điều này tạo ra một phương thức mới khi đang bay,
  tạo ra một phương thức mới khi đang bay,
    tạo ra một phương thức mới khi đang bay,
      ...
    ...
  ..
và sau đó chuyển sang phương thức đó.

Một gia vị nhỏ thêm đến từ bộ nhớ ngăn xếp căng thẳng VÀ bộ nhớ heap cùng một lúc, bằng cách tạo cả tên phương thức dài hơn và dài hơn, và một số lượng lớn là máy thu, khi chúng ta rơi xuống lỗ hổng ... (nhưng đệ quy đánh vào chúng ta trước ).

biên dịch trong 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.

sau đó nhảy, bằng cách đánh giá "2 downTheRabbitHole"...
... sau một thời gian, bạn sẽ kết thúc trình gỡ lỗi, hiển thị RecursionException.

Sau đó, bạn phải dọn dẹp tất cả mớ hỗn độn (cả SmallInteger và LargeInteger hiện có rất nhiều mã wonderland):

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

hoặc người khác dành một chút thời gian trong trình duyệt, loại bỏ xứ sở thần tiên.

Đây là một số từ đầu của dấu vết:

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:" đã được thêm vào để tránh phải dọn dẹp tệp nhật ký thay đổi liên tục của Smalltalk sau đó.

PPS: cảm ơn vì thử thách: nghĩ về điều gì đó mới mẻ và sáng tạo thật thú vị!

PPPS: Tôi muốn lưu ý rằng một số phương ngữ / phiên bản Smalltalk sao chép các khung stack tràn vào heap - vì vậy chúng có thể gặp phải tình huống hết bộ nhớ.


2
LOL +1 cho ma thuật đen đó ở dạng thuần túy
masterX244

Nếu tôi tìm thấy chút thời gian, tôi có thể "cải thiện" bằng cách sử dụng một phương thức ẩn danh của một lớp ẩn danh, để tạo ra lỗ thỏ đen tối nhất từng có ...
blabla999

12

C #

Thực sự lớn struct, không có đệ quy, C # thuần túy, không phải mã không an toàn.

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

như một kicker, nó phá vỡ các cửa sổ gỡ lỗi nói rằng {Cannot evaluate expression because the current thread is in a stack overflow state.}


Và phiên bản chung (cảm ơn vì gợi ý 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>>>>();
    }
}

Cần thêm
methinks

Nó trông giống như đệ quy, nhưng có thể với các đối số kiểu lồng nhau.
ja72

1
Không thấy câu trả lời C # struct của bạn trước khi tôi đăng bài của tôi. Tôi vẫn có một cách tiếp cận khác, vì vậy có lẽ chúng ta để chúng cùng tồn tại.
Thomas Weller

11

C #

Triển khai lỗi của ==toán tử overriden :

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

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

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

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

Người ta có thể nói rõ ràng là operator==tự gọi mình bằng cách sử dụng ==toán tử, nhưng bạn thường không nghĩ ==như vậy, vì vậy rất dễ rơi vào cái bẫy đó.


11

Bắt đầu trả lời bằng SnakeYAML

class A
{

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

Chỉnh sửa: không hiểu

Tùy thuộc vào người đọc để tìm hiểu cách thức hoạt động: P (mẹo: stackoverflow.com)

Nhân tiện: đệ quy được tạo tự động bởi SnakeYAML (bạn sẽ chú ý nếu bạn biết cách nó phát hiện các trường mà nó tuần tự hóa và xem sau đó trong Pointmã nguồn)

Chỉnh sửa: cho biết cách thức hoạt động của một:

SnakeYAML tìm kiếm một cặp getXXXsetXXXmthod có cùng tên cho XXXvà kiểu trả về của getter giống như tham số của setter; và đáng ngạc nhiên là Pointlớp có một Point getLocation()void setLocation(Point P)nó tự trả về; SnakeYAML không chú ý đến nó và đệ quy về điều đó và StackOverflows. Phát hiện ra cái đó khi làm việc với họ bên trong a HashMapvà hỏi về stackoverflow.com trên đó.


10

C #

thực hiện sai tài sản getter

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

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

14
IMHO. Đây là một ví dụ kinh điển cho một đệ quy rõ ràng ... (và vì vậy không hợp lệ)
Ole Albers

2
Chà, điều đó chỉ rõ ràng một khi bạn đã thực hiện nó một lần và nhận ra rằng các nhân viên C # không hoạt động theo cách bạn có thể nghĩ họ đã làm. Rốt cuộc, mã này là một khai báo biến thành viên, vậy tại sao nó không nên tạo một biến thành viên thực tế?
meustrus

2
Đây không phải là một cách làm phức tạpstatic void Main() { Main(); }
Jodrell

5
@Jodrell Bạn sẽ không viết đệ quy Main()vô tình. Nhưng thật đơn giản để viết một thuộc tính đệ quy vô tình và sau đó bị nhầm lẫn bởi tràn ngăn xếp.
Svick

1
Điều này sẽ hữu ích cho ai đó chọn C #.
2rs2ts
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.