วิธีที่ง่ายที่สุดในการทำให้โปรแกรม C ++ ผิดพลาดคืออะไร


318

ฉันกำลังพยายามสร้างโปรแกรม Python ที่เชื่อมต่อกับกระบวนการขัดข้องที่แตกต่างกัน น่าเสียดายที่โปรแกรมที่ฉันกำลังเชื่อมต่ออยู่นั้นไม่ได้ผิดพลาดอย่างน่าเชื่อถือ! ดังนั้นฉันต้องการสร้างโปรแกรม C ++ อย่างรวดเร็วที่ล้มเหลวในจุดประสงค์ แต่จริงๆแล้วฉันไม่รู้วิธีที่ดีที่สุดและสั้นที่สุดในการทำเช่นนั้นไม่มีใครรู้ว่าควรใส่อะไรระหว่าง:

int main() {
    crashyCodeGoesHere();
}

เพื่อทำให้โปรแกรม C ++ ของฉันทำงานล้มเหลวอย่างน่าเชื่อถือ


4
คุณสามารถใช้ชุดประกอบแบบอินไลน์เพื่อพยายามดำเนินการคำสั่งพิเศษ:asm { cli; };
Nate Koppenhaver

@ aitchnyu ฉันคิดว่ามีความแตกต่างในการใช้งานของคำตอบสำหรับคำถามแต่ละข้อ (FYI: ฉันไม่ได้ลงคะแนนอะไรทั้งคำถาม)
แอนดรูว์บาร์เบอร์

ความคิดเห็นใด ๆ ของการขว้างปาข้อยกเว้นในขณะที่มีใครเสนอ ได้โปรดคำตอบของฉันด้านล่างนี้ anc ความคิดเห็น
Abhinav

4
Redis ใช้*((char*)-1) = 'x';รหัสต่อไปนี้เพื่อชักนำให้เกิดความผิดพลาดเพื่อดีบักอ่านเพิ่มเติมในคำตอบ
Shafik Yaghmour

ฉันพบคำถามนี้เพื่อค้นหากรณีทดสอบสำหรับระบบรายงานข้อขัดข้อง ฉันต้องการบังคับให้เกิดความผิดพลาดในระหว่างรันไทม์ปกติเพื่อเรียกใช้ตัวรายงานข้อขัดข้องและการส่งกองซ้อน ขอบคุณ!
Cory Trese

คำตอบ:


264

abort()ฟังก์ชั่นน่าจะเป็นทางออกที่ดีที่สุดของคุณ มันเป็นส่วนหนึ่งของไลบรารี่มาตรฐาน C และถูกกำหนดให้เป็น "ก่อให้เกิดการยุติโปรแกรมที่ผิดปกติ" (เช่นข้อผิดพลาดร้ายแรงหรือล้มเหลว)


14
โปรดทราบว่าการขัดข้องabort()ไม่ได้เรียกตัวทำลายหรือatexitฟังก์ชันใด ๆ
Xeo

139
@ Xeo: ถ้ามันเรียก destructors และatexits มันจะไม่ผิดพลาดตอนนี้หรือไม่
Donal Fellows

ตั้งแต่abort()เป็นคำตอบที่ถูกต้องแล้วควรออก (-1); `เป็นที่ยอมรับ?
asir6

9
ไม่เพราะมันไม่ก่อให้เกิดความผิดพลาดเพียงรายงานสิ่งที่ไม่สามารถทำได้
boatcoder

ของ windows GCC-5.4.0 รหัสออก: 3. ไม่มีกล่องข้อความแสดงข้อผิดพลาด ข้อความคอนโซล: "แอปพลิเคชันนี้ขอให้รันไทม์ยุติการทำงานในลักษณะที่ผิดปกติกรุณาติดต่อทีมสนับสนุนของแอปพลิเคชันสำหรับข้อมูลเพิ่มเติม"
Vadzim

113

ลอง:

raise(SIGSEGV);  // simulates a standard crash when access invalid memory
                 // ie anything that can go wrong with pointers.

พบใน:

#include <signal.h>

3
มันเป็นมากกว่าแค่การกำหนดการใช้งาน - สัญญาณสามารถถูกจับsignal()ได้ แอปพลิเคชันที่มีสติส่วนใหญ่ทำไม่ได้
duskwuff -inactive-

12
มันจะล้มเหลวในลักษณะเดียวกับ SIGSEGV ปกติภายในแอปพลิเคชัน (ซึ่งเป็นวิธีที่แอปพลิเคชันส่วนใหญ่ผิดพลาด) มันถูกกำหนดไว้อย่างดีว่ามันทำอะไร (โดยปกติแล้วมันจะออกจากแอปพลิเคชั่นและสร้างไฟล์แกน) ใช่คุณสามารถตั้งเครื่องจัดการได้ แต่ถ้าคุณไม่มีคุณไม่ต้องการทดสอบมันในแบบเดียวกัน !!
Martin York

1
+1 raise()สำหรับ สิ่งนี้ช่วยให้คุณทดสอบข้อยกเว้นประเภทต่าง ๆ ได้มากมายเพียงแค่เปลี่ยนอาร์กิวเมนต์

3
ทางออกที่ชื่นชอบ แต่ขึ้นอยู่กับแพลตฟอร์ม
Nadim Farhat

@NadimFarhat: ด้วยวิธีใด สัญญาณเป็นสัญญาณในทุกแพลตฟอร์ม
Martin York

74

การหารด้วยศูนย์จะทำให้แอปพลิเคชันมีปัญหา:

int main()
{
    return 1 / 0;
}

29
ขึ้นอยู่กับว่าคอมไพเลอร์ของคุณฉลาดแค่ไหนในเวลารวบรวม ฉันรู้ว่า visual studio 2008 จะไม่รวบรวมสิ่งนี้สำหรับ c ++ หรือ c #
AidanO

2
ฉันได้รวบรวมและเรียกใช้คุณต้องการให้ฉัน. exe คุณทราย?
Roee Gavirel

1
ในการกำหนดค่าการนำออกใช้ใน Visual Studio รุ่นล่าสุดเช่น 2010 มันจะทำงานโดยไม่มีปัญหา ฉันคิดว่ามันเป็นการเพิ่มประสิทธิภาพ
tedyyu

2
iirc ไม่เกิดปัญหาบนแขน
sherpya

2
นี่คือพฤติกรรมที่ไม่ได้กำหนดและไม่รับประกันว่าจะเกิดความผิดพลาด บ่อยครั้งที่คอมไพเลอร์ถือว่าพฤติกรรมที่ไม่ได้กำหนดไม่สามารถเข้าถึงได้ สิ่งนี้สามารถนำไปสู่เนื้อหาของmainการถูกลบทั้งหมดรวมถึงretคำแนะนำ การประมวลผลอาจตกอยู่ในฟังก์ชันต่อไปนี้
usr

64
*((unsigned int*)0) = 0xDEAD;

54
สิ่งนี้ไม่รับประกันว่าจะผิดพลาด
โปรแกรมเมอร์ Windows

8
@Windowsprogrammer: ไม่มีก็ไม่ได้รับประกัน แต่ OS ใดที่มีสติไม่สามารถหยุดแอพพลิเคชั่นที่พยายามเข้าถึงหน่วยความจำที่ที่อยู่ 0?
Joachim Sauer

29
"แต่ OS ใดที่มีสติไม่สามารถหยุดแอปพลิเคชันที่พยายามเข้าถึงหน่วยความจำที่ที่อยู่ 0" - นั่นไม่ใช่สิ่งที่คุณต้องการถาม แต่ฉันจะตอบกลับ ในคอมพิวเตอร์บางเครื่องมี RAM ที่ที่อยู่ 0 และมันมีความหมายอย่างสมบูรณ์สำหรับโปรแกรมที่จะเก็บค่าไว้ที่นั่น คำถามที่มีความหมายมากกว่านี้คือ "ระบบปฏิบัติการใดไม่หยุดแอปพลิเคชันที่เข้าถึงหน่วยความจำตามที่อยู่ซึ่งการใช้งาน C ++ ที่สงวนไว้สำหรับตัวชี้โมฆะ" ในกรณีนั้นฉันไม่รู้อะไรเลย แต่โปรแกรมดั้งเดิมนั้นเกี่ยวกับภาษา C ++ ไม่ใช่เกี่ยวกับระบบปฏิบัติการ
โปรแกรมเมอร์ Windows

6
พฤติกรรมที่ไม่ได้กำหนด มันโอเคที่จะไม่ทำอะไรเลย เครื่องที่จะไม่ผิดพลาด: มีสิ่งใดที่ใช้โปรเซสเซอร์ซีรีส์ Z80 (ฉันถือว่า (Z80a ของฉันไม่ทำอะไรเลย)
Martin York

28
แม้ว่าจะไม่ได้รับประกันความผิดพลาดก็เป็นหนึ่งที่พบมากที่สุดชนิดของความผิดพลาดใน C ++ ดังนั้นหากคุณต้องการที่จะจำลองความผิดพลาดนี้เป็นวิธีที่ "ของแท้" ที่จะทำมัน :)
คริสเบิร์ทสีน้ำตาล

53

เราอยู่ในกองล้นหรือไม่?

for (long long int i = 0; ++i; (&i)[i] = i);

(ไม่รับประกันว่าจะผิดพลาดตามมาตรฐานใด ๆ แต่ไม่มีคำตอบที่แนะนำใด ๆ รวมถึงคำตอบที่ได้รับการยอมรับเนื่องจากSIGABRTอาจถูกจับได้แล้วในทางปฏิบัติสิ่งนี้จะผิดพลาดได้ทุกที่)


4
ฉันเห็นได้ว่าการเป็นคนตลกในระบบที่ไม่มีโค้ดเพจที่มีการป้องกันและคุณเขียนทับโปรแกรมของคุณด้วยโค้ดบางอย่างที่บังเอิญวนซ้ำไม่สิ้นสุดที่ไม่ทำอะไรเลย สูงมากสูงมากไม่น่าเป็นไปได้ แต่อาจเป็นไปได้
Martin York

@ โลกิ: ถ้ามันอ่านจากทุก ๆ 4000th ล่ะ? นั่นจะมีโอกาสน้อยที่จะผิดพลาดหรือไม่? อันตรายน้อยกว่าแน่นอน
Mooing Duck

70
อัลกอริทึมที่ล้มเหลวนั้นไม่ใช่ O (1)!
Anton Barkovsky

@MooingDuck: ฉันแค่ทำให้ความคิดเห็นตลก อย่าคิดว่าเป็นเรื่องจริงจัง :-) แต่มันน่าสนใจถ้ามีคนพบคำสั่งตามลำดับที่ทำสิ่งที่ตลก
Martin York

1
@ LokiAstari: คุณพูดถูก ฉันกำลังคิดถึงเรื่อง(&i)[i] += !iนี้อยู่ แต่ฉันเกรงว่าคอมไพเลอร์อาจฉลาดพอและต้องการปรับให้เหมาะสม :-)
sam hocevar

35
 throw 42;

แค่คำตอบ ... :)


1
ของ windows GCC-5.4.0 รหัสออก: 3. ไม่มีกล่องข้อความแสดงข้อผิดพลาด ข้อความคอนโซล: "ยุติการโทรหลังจากโยนอินสแตนซ์ของ 'int' แอปพลิเคชันนี้ได้ขอให้รันไทม์ยุติการทำงานในลักษณะที่ผิดปกติโปรดติดต่อทีมสนับสนุนของแอปพลิเคชันเพื่อขอข้อมูลเพิ่มเติม"
Vadzim

15

assert(false); ก็ค่อนข้างดีเช่นกัน

ตามมาตรฐาน ISO / IEC 9899: 1999 รับประกันว่าจะมีปัญหาเมื่อไม่ได้กำหนด NDEBUG:

หากกำหนด NDEBUG [... ] แมโคร assert จะถูกกำหนดอย่างง่าย ๆ ดังนี้

#define assert(ignore) ((void)0)

แมโครยืนยันจะถูกกำหนดใหม่ตามสถานะปัจจุบันของ NDEBUG ทุกครั้งที่รวม

[ ... ]

แมโคร assert ทำการทดสอบวินิจฉัยลงในโปรแกรม [... ] ถ้าการแสดงออก (ซึ่งจะมีประเภทสเกลาร์) เป็นเท็จ [... ] จากนั้นจะเรียกใช้ฟังก์ชันยกเลิก


ฉันจำ VC 2005 มีพฤติกรรมแตกต่างกันระหว่าง debug และ release กับ asserts หรือไม่?
Tom Kerr

8
@Tom assertทำเทียบเท่ากับ((void)0)ในโหมด Release
Seth Carnegie

2
@SethCarnegie ไม่เห็นมีอะไรผิดปกติกับสิ่งนี้ - เฉพาะในกรณีที่กำหนด NDEBUG ที่กำหนดไว้จะไม่ผิดพลาด? คำตอบก็ค่อนข้างยุติธรรม IMHO
Adrian Cornish

@AdrianCornish ฉันแค่ตอบคำถามของ Tom Kerr เท่านั้นไม่ได้บอกว่าคำตอบนี้ผิด ฉันไม่ได้ลงคะแนนคำตอบนี้
Seth Carnegie

3
ฉันไม่รู้ว่าทำไมเขาถึงสร้าง "ปล่อย" ของรหัสทดสอบนี้
Joel B

11

เนื่องจากความผิดพลาดเป็นอาการของการเรียกใช้พฤติกรรมที่ไม่ได้กำหนดและเนื่องจากการเรียกใช้พฤติกรรมที่ไม่ได้กำหนดสามารถนำไปสู่การใด ๆ รวมถึงความผิดพลาดฉันไม่คิดว่าคุณต้องการที่จะผิดพลาดโปรแกรมของคุณจริงๆ วิธีพกพาที่น่าทำที่สุดน่าจะเป็นabort()วิธีแบบพกพามากที่สุดในการทำเช่นนั้นอาจจะเป็น

ในขณะที่raise(SIGABRT)มีเอฟเฟกต์เหมือนกัน ทั้งสองวิธี SIGABRTแต่สามารถดักโดยการติดตั้งจัดการสัญญาณสำหรับ ดังนั้นขึ้นอยู่กับสถานการณ์ของคุณคุณอาจต้องการ / ต้องการเพิ่มสัญญาณอื่น SIGFPE,SIGILL , SIGINT, SIGTERMหรือSIGSEGVอาจจะมีวิธีที่จะไป แต่พวกเขาทั้งหมดสามารถดัก

เมื่อคุณไม่สามารถถอดออกได้ตัวเลือกของคุณอาจกว้างขึ้นเช่นใช้งานSIGBUSบน linux


1
ฉันสงสัยจริงๆว่าเขาต้องการให้ดีบักเกอร์เกี่ยวข้อง ดูเหมือนว่าเขาต้องการที่จะทดสอบว่าเกิดอะไรขึ้นเมื่อผู้เรียกโปรแกรมหยุดทำงานได้รับความผิดพลาดส่งมาถึงเขา ซึ่งมีความสมเหตุสมผลมาก
Donal Fellows

9

แฟลชเดียวที่ฉันมีคือยกเลิก () ฟังก์ชั่น :

มันยกเลิกกระบวนการที่มีการยกเลิกโปรแกรมที่ผิดปกติซึ่งจะสร้างสัญญาณ SIGABRTซึ่งโดยปกติจะทำให้โปรแกรมยุติการส่งคืนรหัสข้อผิดพลาดการเลิกจ้างที่ไม่สำเร็จไปยังสภาพแวดล้อมโฮสต์โปรแกรมจะถูกยกเลิกโดยไม่ต้องดำเนินการ destructorsสำหรับวัตถุ และโดยไม่ต้องเรียกใช้atexitใด ๆ(ซึ่งถูกเรียกโดย exit () ก่อนที่โปรแกรมจะสิ้นสุด) ฟังก์ชัน มันจะไม่ส่งกลับไปยังผู้เรียก


9

คำตอบคือแพลตฟอร์มเฉพาะและขึ้นอยู่กับเป้าหมายของคุณ แต่นี่คือฟังก์ชันการทำงานผิดพลาดของ Mozilla Javascript ซึ่งฉันคิดว่าแสดงให้เห็นถึงความท้าทายมากมายในการทำให้งานนี้:

static JS_NEVER_INLINE void
CrashInJS()
{
    /*
     * We write 123 here so that the machine code for this function is
     * unique. Otherwise the linker, trying to be smart, might use the
     * same code for CrashInJS and for some other function. That
     * messes up the signature in minidumps.
     */

#if defined(WIN32)
    /*
     * We used to call DebugBreak() on Windows, but amazingly, it causes
     * the MSVS 2010 debugger not to be able to recover a call stack.
     */
    *((int *) NULL) = 123;
    exit(3);
#elif defined(__APPLE__)
    /*
     * On Mac OS X, Breakpad ignores signals. Only real Mach exceptions are
     * trapped.
     */
    *((int *) NULL) = 123;  /* To continue from here in GDB: "return" then "continue". */
    raise(SIGABRT);  /* In case above statement gets nixed by the optimizer. */
#else
    raise(SIGABRT);  /* To continue from here in GDB: "signal 0". */
#endif
}

2
คุณควรดร็อปและใช้ jQuery แทน
Thomas Weller

1
นี่คือพฤติกรรมที่ไม่ได้กำหนดและไม่รับประกันว่าจะเกิดความผิดพลาด บ่อยครั้งที่คอมไพเลอร์ถือว่าพฤติกรรมที่ไม่ได้กำหนดไม่สามารถเข้าถึงได้ ในกรณีนั้นอย่างน้อยที่สุดก็จะต้องลบบรรทัดการขัดข้องและรหัสอื่นอาจจะเป็นเช่นกัน
usr

8

ฉันเห็นว่ามีคำตอบมากมายที่โพสต์ที่นี่ซึ่งจะตกอยู่ในกรณีผู้โชคดีที่จะได้งานทำ แต่ไม่มีใครกำหนดได้ว่าจะขัดข้อง 100% บางส่วนจะมีปัญหากับฮาร์ดแวร์และระบบปฏิบัติการเดียวส่วนอื่น ๆ จะไม่ทำงาน อย่างไรก็ตามมีวิธีมาตรฐานตามมาตรฐาน C ++ อย่างเป็นทางการเพื่อให้เกิดความผิดพลาด

ข้อความอ้างอิงจากC ++ มาตรฐานISO / IEC 14882 §15.1-7 :

หากกลไกการจัดการข้อยกเว้นหลังจากเสร็จสิ้นการเริ่มต้นของวัตถุยกเว้น แต่ก่อนที่การเปิดใช้งานของตัวจัดการข้อยกเว้นเรียกใช้ฟังก์ชันที่ออกจากข้อยกเว้น std :: ยุติจะถูกเรียก (15.5.1)

struct C {
    C() { }
    C(const C&) {
        if (std::uncaught_exceptions()) {
            throw 0; // throw during copy to handler’s exception-declaration object (15.3)
        }
    }
};
int main() {
    try {
    throw C(); // calls std::terminate() if construction of the handler’s
    // exception-declaration object is not elided (12.8)
    } catch(C) { }
}

ฉันได้เขียนโค้ดขนาดเล็กที่จะแสดงให้เห็นถึงนี้และสามารถพบและพยายามIdeone ที่นี่

class MyClass{
    public:
    ~MyClass() throw(int) { throw 0;}
};

int main() {
  try {
    MyClass myobj; // its destructor will cause an exception

    // This is another exception along with exception due to destructor of myobj and will cause app to terminate
     throw 1;      // It could be some function call which can result in exception.
  }
  catch(...)
  {
    std::cout<<"Exception catched"<<endl;
  }
  return 0;
}

ISO / IEC 14882 §15.1 / 9กล่าวถึงการโยนโดยไม่ลองบล็อกทำให้การโทรโดยปริยาย:

หากไม่มีข้อยกเว้นอยู่ในขณะนี้การจัดการการดำเนินการการแสดงออกโดยไม่มีตัวถูกดำเนินการเรียก std :: ยุติ ()

คนอื่น ๆ รวมถึง: โยนจาก destructor: ISO / IEC 14882 §15.2 / 3


7
*( ( char* ) NULL ) = 0;

สิ่งนี้จะสร้างความผิดพลาดในการแบ่งส่วน


10
สิ่งนี้ไม่รับประกันว่าจะผิดพลาด
โปรแกรมเมอร์ Windows

23
"จะเกิดอะไรขึ้นแทน" - อาจเกิดอะไรขึ้นแทน พฤติกรรมไม่ได้ถูกกำหนดดังนั้นการใช้งานสามารถกำหนด 0 ให้เป็นหนึ่งในตัวแปรของโปรแกรมของคุณหรือสามารถกำหนด 42 ให้เป็นหนึ่งในตัวแปรของโปรแกรมของคุณหรือสามารถฟอร์แมตฮาร์ดไดรฟ์ของคุณและดำเนินการโปรแกรมของคุณต่อไป
โปรแกรมเมอร์ Windows

7
(ความคิดต่อเนื่องของ "โปรแกรมเมอร์ Windows") มันสามารถทำให้คอมพิวเตอร์ของคุณระเบิดได้หรืออาจทำให้มันมีชีวิตขึ้นมาและเข้ายึดครองมนุษยชาติ หรือ ... มันจะล้มเหลวใน 99.9% และถูกกำหนดเป็น "พฤติกรรมที่ไม่ได้กำหนด" เพราะไม่มีใครอยากรับผิดชอบ
Roee Gavirel

1
ที่จริงแล้วมันไม่ได้รับประกันว่าจะทำแม้กระทั่งพฤติกรรมที่ไม่ได้กำหนด - มันสามารถกำหนดได้อย่างสมบูรณ์และทำงานได้อย่างถูกต้อง พิจารณาโค้ดนี้: pastebin.com/WXCtTiDD (ทดสอบบน Linux ในฐานะ root คุณสามารถทำสิ่งนี้ในฐานะผู้ใช้ที่ไม่ใช่รูทได้เช่นกันหากคุณทำการเปลี่ยนแปลงการกำหนดค่าบางอย่างwiki.debian.org/mmap_min_addr )
cha0site

2
@ cha0site: รับประกันได้ว่าจะไม่ได้กำหนดพฤติกรรมตามมาตรฐานเพราะนั่นคือการลงทะเบียนตัวชี้โมฆะ พฤติกรรมใดก็ตามที่คุณสังเกตเห็นบน Linux นั้นสามารถทำได้ภายใต้ "พฤติกรรมที่ไม่ได้กำหนด"
Ben Voigt

6

อันนี้หายไป:

int main = 42;

1
ใช่แล้ว; แต่มันจะทำสิ่งที่งดงามเมื่อคุณเรียกใช้
Joshua

5

แล้วการล้นสแต็กโดยการเรียกวนซ้ำแบบวนซ้ำ

#include <windows.h>
#include <stdio.h>

void main()
{
    StackOverflow(0);
}

void StackOverflow(int depth)
{
    char blockdata[10000];
    printf("Overflow: %d\n", depth);
    StackOverflow(depth+1);
}

ดูตัวอย่างต้นฉบับบน Microsoft KB


4
อะไรจะป้องกัน Smart Compiler อย่างเพียงพอจากการปรับให้เหมาะสมทั้งการจัดสรรสแต็กที่ไม่ได้ใช้และการเรียกหาง
JB

@JB: ขออภัยมีความคิดไม่ได้เพราะไม่คุ้นเคยกับคอมไพเลอร์ที่มีอยู่ตรรกะการเพิ่มประสิทธิภาพ
SLL

8
ดีรวบรวมที่นี่ด้วย gcc 4.6.0 ที่ระดับการเพิ่มประสิทธิภาพ -O2 และสูงกว่าก็เพิ่มประสิทธิภาพได้ดี ต้องการ -O1 หรือต่ำกว่าเพื่อ segfault
JB

@Abhinav: เพียงแค่โพสต์คำตอบของคุณที่มีทั้งหมดของวิธีการเหล่านี้แสดงเป็นตัวอย่างใน C ++ :)
SLL

5

สิ่งนี้ขัดข้องในระบบ Linux ของฉันเนื่องจากตัวอักษรสตริงถูกเก็บไว้ในหน่วยความจำแบบอ่านอย่างเดียว:

0[""]--;

โดยวิธีการที่ g ++ ปฏิเสธที่จะรวบรวมนี้ คอมไพเลอร์กำลังฉลาดขึ้นและฉลาดขึ้น :)


4
int i = 1 / 0;

คอมไพเลอร์ของคุณอาจเตือนคุณเกี่ยวกับเรื่องนี้ แต่คอมไพล์ก็ใช้ได้ตาม GCC 4.4.3 นี่จะ อาจเป็นไปได้ทำให้ SIGFPE (ข้อยกเว้นทศนิยม) ซึ่งอาจไม่น่าเป็นไปได้ในแอปพลิเคชันจริงเช่น SIGSEGV (การแบ่งส่วนหน่วยความจำ) คำตอบอื่น ๆ ทำให้เกิด แต่ก็ยังคงผิดพลาด ในความคิดของฉันนี้สามารถอ่านได้มากขึ้น

อีกวิธีหนึ่งถ้าเราจะโกงและใช้งานsignal.hคือ:

#include <signal.h>
int main() {
    raise(SIGKILL);
}

สิ่งนี้รับประกันได้ว่าจะฆ่า subprocess เพื่อเปรียบเทียบกับ SIGSEGV


2
สิ่งนี้ไม่รับประกันว่าจะผิดพลาด
โปรแกรมเมอร์ของ Windows

11
ภาษา C ++ ไม่รับประกันว่า 1/1 จะทำให้เกิด SIGFPE พฤติกรรมไม่ได้กำหนด การใช้งานอาจกล่าวได้ว่าผลลัพธ์คือ 42
โปรแกรมเมอร์ Windows

1
เมื่อพฤติกรรมไม่ได้กำหนดการใช้งานสามารถทำสิ่งที่ต้องการ ภาษา C ++ ไม่ป้องกันหรือไม่ต้องการการใช้งานเพื่อเขียนการถ่ายโอนข้อมูลผิดพลาด, ภาษา C ++ ไม่ป้องกันหรือไม่ต้องการการใช้งานเพื่อกำหนด 42, ฯลฯ
โปรแกรมเมอร์ของ Windows

2
@Giorgio หากฮาร์ดแวร์ไม่มีวิธีในการดักจับมันโดยอัตโนมัติคุณยังคงบังคับให้คอมไพเลอร์ปล่อยคำสั่งอย่างน้อยสองคำสั่งซึ่งหนึ่งในนั้นจะเป็นสาขาด้วย ประมาณสองเท่าของค่าใช้จ่ายของแผนก ทุกคนจ่ายเงินแบบนั้น หากเป็นตัวเลือกและคุณต้องการคุณสามารถใช้ฟังก์ชั่นห้องสมุดได้ตลอดเวลา หากไม่ใช่ตัวเลือกและคุณไม่ต้องการให้เป็นเช่นนั้นคุณยังต้องชำระค่าใช้จ่ายด้วย
เฟล็กโซ

2
@Giorgio: ฉันมีแอปพลิเคชั่นที่ทำหน้าที่ได้ 100,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000 ฉันรู้สำหรับข้อเท็จจริงที่ว่า 0 ของเหล่านี้จะถูกหารด้วยศูนย์แม้ว่าจะไม่มีวิธีที่คอมไพเลอร์สามารถรู้สิ่งนี้ ฉันไม่ต้องการให้คอมไพเลอร์ทำการตรวจสอบการหารด้วยศูนย์
Martin York

4

นี่เป็นเวอร์ชันที่ยกเลิกการรับประกันเพิ่มเติมที่แสดงในคำตอบข้างต้นมันจะดูแลสถานการณ์เมื่อ sigabrt ถูกบล็อกคุณสามารถ infact ใช้สัญญาณใด ๆ แทนการยกเลิกที่มีการดำเนินการเริ่มต้นของการทำงานล้มเหลวของโปรแกรม

#include<stdio.h>
#include<signal.h>
#include<unistd.h> 
#include<stdlib.h>
int main()
{
    sigset_t act;
    sigemptyset(&act);
    sigfillset(&act);
    sigprocmask(SIG_UNBLOCK,&act,NULL);
    abort();
}

3
int* p=0;
*p=0;

สิ่งนี้น่าจะผิดพลาดเช่นกัน ใน Windows มันขัดข้องกับ AccessViolation และควรทำเช่นเดียวกันในทุกระบบปฏิบัติการฉันเดา


5
on all OS-esไม่มีก็ไม่ผิดพลาดในการป้องกันระบบปฏิบัติการที่ไม่ได้ (เช่น MS-DOS.) ที่จริงบางครั้งมีเป็นบางสิ่งบางอย่างที่อยู่ใน 0! สำหรับโหมดจริง x86 ตารางเวกเตอร์ขัดจังหวะอยู่ในที่อยู่ 0
ikh

มันไม่ได้ผิดพลาดกับ Irix ฉันเศร้ารู้เมื่อเรา port รหัสที่ Linux (ที่เรายังไม่ถึงmain()#
Scheff

3

นี่คือตัวอย่างข้อมูลที่จัดทำโดย Google ใน Breakpad

  volatile int* a = reinterpret_cast<volatile int*>(NULL);
  *a = 1;

1
ตั้งแต่ฉันทดสอบ Breakpad นี่เป็นสิ่งที่ฉันต้องการ! ฉันพบว่าตัวแบ่ง Breakpad บางตัวไม่ได้สร้างการติดตามสแต็กที่ชี้ไปที่บรรทัดในรหัสของฉันทำให้เกิดความเสียหาย อันนี้ทำดังนั้นฉันสามารถใช้มันเป็นแบบทดสอบที่ดี
BuvinJ



2

การเขียนไปยังหน่วยความจำแบบอ่านอย่างเดียวจะทำให้การแบ่งเซ็กเมนต์ผิดปกติเว้นแต่ว่าระบบของคุณไม่รองรับบล็อกหน่วยความจำแบบอ่านอย่างเดียว

int main() {
    (int&)main = 0;
}

ฉันได้ทำการทดสอบด้วย MingGW 5.3.0 บน Windows 7 และ GCC บน Linux Mint ฉันคิดว่าคอมไพเลอร์และระบบอื่น ๆ จะให้ผลคล้ายกัน


1

หรืออีกวิธีหนึ่งเนื่องจากเราอยู่ในวงเกวียน

การเรียกซ้ำแบบไม่สิ้นสุดที่น่ารัก รับประกันว่าจะระเบิดกองของคุณ

int main(int argv, char* argc)
{
   return main(argv, argc)
}

พิมพ์ออกมา:

การแบ่งส่วนความผิดพลาด (หลักถูกทิ้ง)


13
การเรียกmainตัวเองว่าเป็นพฤติกรรมที่ไม่ได้กำหนดจริงๆในกรณีที่คุณไม่รู้ :) นอกจากนี้การเรียกซ้ำหางไม่รับประกันว่าจะทำให้สแตกของคุณ หากคุณต้องการ "รับประกัน" คุณต้องทำอะไรบางอย่างหลังจากการเรียกซ้ำมิฉะนั้นคอมไพเลอร์สามารถปรับการสอบถามซ้ำให้เป็นวงวนไม่สิ้นสุด
fredoverflow

0

ที่ยังไม่ได้กล่าวถึง:

((void(*)())0)();

สิ่งนี้จะถือว่าตัวชี้โมฆะเป็นตัวชี้ฟังก์ชันแล้วเรียกใช้ เช่นเดียวกับวิธีการส่วนใหญ่สิ่งนี้ไม่รับประกันว่าจะเกิดความผิดพลาดของโปรแกรม แต่โอกาสของระบบปฏิบัติการที่ทำให้สิ่งนี้ไม่ถูกตรวจสอบและโปรแกรมที่เคยกลับมานั้นไม่มีความสำคัญ


3
ฉันรู้ว่ามีหลายเครื่องที่จะทำให้เกิดการรีบูตเนื่องจากรหัสเริ่มต้นระบบปฏิบัติการถูกแมปกับที่อยู่ 0 อย่างมีประสิทธิภาพอย่าคิดว่าทุกอย่างจะทำงานได้เหมือนพีซีของคุณ คุณสามารถพูดได้ว่าเป็นความผิดพลาด แต่มันไม่มีประโยชน์มากเพราะคุณไม่สามารถตรวจแก้จุดบกพร่องได้เนื่องจากสถานะทั้งหมดถูกลบเมื่อเริ่มต้น
Martin York

@ Loki Astari: ฉันจะไม่พูดน้อยว่านี่คือความผิดพลาด - ถ้ามันสามารถทำให้มันแล้วโปรแกรมที่กำลังดีบั๊กอาจหมายความว่ามันเป็นแบบทดสอบที่ดี ในทางกลับกันฉันสงสัยว่าเครื่องใดที่สามารถใช้งาน Python ได้
Anton Golov

ฉันคิดว่าคุณพลาดจุด ฉันเคยเห็นรหัสระบบปฏิบัติการที่ 0 ไม่ได้หมายความว่าไม่มีระบบที่มีรหัสที่ดีปกติในการทำงานที่จะทำงานได้ดีที่ 0 หรือไบต์ที่ 0 อาจเป็น opcode สำหรับส่งคืนได้อย่างง่ายดาย
Martin York

ฉันรู้รหัสระบบปฏิบัติการที่ 0; นั่นเป็นหนึ่งในเหตุผลที่ฉันสงสัยอย่างมากว่าไบต์ที่ 0 จะเป็น opcode สำหรับการส่งคืน โปรแกรมค่อนข้างชัดเจนว่าไม่ได้ดำเนินการใด ๆ อีกต่อไปและไม่ได้ออกไปตามปกติคือมันพัง - หากนี่ยังไม่ดีพอสำหรับผู้ถามฉันคาดหวังให้เขาแสดงความคิดเห็นด้วยตัวเอง
Anton Golov

2
คุณตระหนักถึงรหัส OS สำหรับเครื่องของคุณเท่านั้น สิ่งที่ฉันพยายามจะพูดคืออาจล้มเหลวสำหรับคุณ แต่นั่นไม่ได้หมายความว่าอะไร มีระบบจำนวนมากออกมี ฉันแน่ใจว่าบางส่วนของพวกเขานี้อาจทำงานได้ (เช่นในขณะที่ไม่ผิดพลาด) การใช้ลักษณะการทำงานเฉพาะของเครื่อง / OS เป็นความคิดที่ไม่ดีและทำให้เกิดปัญหาการบำรุงรักษาในระยะยาว แนวคิดของเว็บไซต์คือการส่งเสริมรหัสที่ดี (ไม่ใช่แค่รหัสที่เรียงลำดับของงาน)
Martin York

0
void main()
{

  int *aNumber = (int*) malloc(sizeof(int));
  int j = 10;
  for(int i = 2; i <= j; ++i)
  {
      aNumber = (int*) realloc(aNumber, sizeof(int) * i);
      j += 10;
  }

}

หวังว่าจะเกิดปัญหานี้ ไชโย


0
int main()
{
    int *p=3;
    int s;
    while(1) {
        s=*p;
        p++;
    }
}

2
มันจะดีที่จะมีการชี้แจงบาง :)
olyv

1
ตัวชี้ p จะไปไกลกว่าพื้นที่ที่อยู่ของโปรแกรมซึ่งจะเป็นข้อผิดพลาดของหน่วยความจำเนื่องจากกระบวนการไม่สามารถเข้าถึงหน่วยความจำของกระบวนการอื่นได้ ซึ่งจะทำให้โปรแกรมขัดข้อง ตัวชี้ p กำลังชี้ไปยังตำแหน่งที่สุ่มในพื้นที่ที่อยู่ของมันหากมีการเพิ่มและยกเลิกการลงทะเบียนไม่สิ้นสุดในบางจุดมันจะชี้ไปยังพื้นที่ที่อยู่ (กระบวนการ) ของโปรแกรมอื่น ดังนั้นจึงจะมีปัญหาหลังจากเวลาผ่านไปสักครู่
sc_cs

หรือสมมุติว่ามันสามารถทำให้เกิดการล้นจำนวนเต็มและล้อมรอบวิ่งไปเรื่อย ๆ ฉันพยายามที่จะใช้long longหรือsize_tเริ่มต้นด้วยpค่าสูงสุดที่เกี่ยวข้องหรือใกล้เคียงเพื่อให้พังเร็วขึ้น แม้ว่ามันจะยังไม่รับประกันว่าจะผิดพลาดแม้ในกรณีนี้
Patrick Roberts

0

วิธีที่มีสไตล์ในการทำเช่นนี้คือการเรียกฟังก์ชั่นเสมือนจริงที่บริสุทธิ์:

class Base;

void func(Base*);

class Base
{
public:
   virtual void f() = 0;
   Base() 
   {
       func(this);
   }
};

class Derived : Base
{
   virtual void f()
   {
   }
};

void func(Base* p)
{
   p->f();
}


int main()
{
    Derived  d;
}

รวบรวมด้วย gcc ภาพนี้:

วิธีเสมือนบริสุทธิ์ที่เรียกว่า

ยุติการเรียกใช้โดยไม่มีข้อยกเว้นที่ใช้งานอยู่

ยกเลิก (มีการทิ้งคอร์)


0

คุณสามารถใช้แอสเซมบลีใน c ++ code BUT ของคุณINT 3เท่านั้นสำหรับระบบ x86 ระบบอื่น ๆ อาจมีคำสั่ง trap / breakpoint อื่น ๆ

int main()
{
    __asm int 3;

    return 0;
}

INT 3 ทำให้เกิดการขัดจังหวะและเรียกเวกเตอร์ขัดจังหวะที่ตั้งค่าโดยระบบปฏิบัติการ


0

ใช้ __builtin_trap () ใน GCC หรือเสียงดังกราวหรือ __debugbreak () ใน MSVC การไม่จัดการกับจุดพัก / กับดักเหล่านี้จะนำไปสู่ข้อยกเว้น / ข้อผิดพลาดของจุดพักที่ไม่สามารถจัดการได้ คำแนะนำอื่น ๆ ที่ใช้ abort () หรือ exit (): อาจถูกจัดการโดยเธรดอื่นทำให้ยากต่อการดูสแต็กของเธรดที่แพร่กระจายความล้มเหลว


-2
char*freeThis;
free(freeThis);

การเพิ่มตัวชี้ที่ไม่ได้กำหนดค่าเริ่มต้นนั้นเป็นพฤติกรรมที่ไม่ได้กำหนดไว้ ในหลาย ๆ แพลตฟอร์ม / คอมไพเลอร์freeThisจะมีค่าสุ่ม (อะไรก็ตามที่อยู่ในตำแหน่งหน่วยความจำนั้นมาก่อน) การปล่อยให้ว่างจะขอให้ระบบเพิ่มหน่วยความจำตามที่อยู่นั้นซึ่งโดยปกติจะทำให้เกิดการแบ่งเซ็กเมนต์และทำให้โปรแกรมขัดข้อง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.