使C ++程序崩溃的最简单方法是什么?


318

我正在尝试制作一个与其他崩溃进程连接的Python程序(这在我手中)。不幸的是,我正在连接的程序甚至无法可靠地崩溃!因此,我想制作一个有意崩溃的快速C ++程序,但实际上我不知道这样做的最好和最短的方法,有人知道我该怎么做吗?

int main() {
    crashyCodeGoesHere();
}

使我的C ++程序可靠地崩溃


4
您可以使用内联汇编程序尝试执行特权指令:asm { cli; };
Nate Koppenhaver 2011年

@aitchnyu我认为每个问题的答案的可用性有所不同。(仅供参考:我没有对任何一个问题投任何票)
Andrew Barber

在已经传播的情况下抛出异常的任何评论?请在下面的评论中回答我的答案
Abhinav

4
Redis使用以下*((char*)-1) = 'x';代码引发崩溃,以调试我的答复中的
Shafik Yaghmour 2014年

我发现这个问题正在寻找崩溃报告系统的测试用例。我需要在正常运行时强制崩溃以调用崩溃报告器和堆栈转储发送。谢谢!
Cory Trese,

Answers:


264

abort()功能可能是您最好的选择。它是C标准库的一部分,被定义为“导致程序异常终止”(例如,致命错误或崩溃)。


14
请注意,通过崩溃abort()不会调用任何析构函数或atexit函数,尽管在这里这可能无关紧要。
Xeo

139
@Xeo:如果它确实调用了析构函数和atexits,那么现在不会崩溃了吗?
Donal Fellows

既然abort()是正确的答案,那么'exit(-1);'应该可以接受吗?
2011年

9
不,因为它不会导致崩溃,所以只能报告无法完成的事情。
boatcoder

视窗。GCC-5.4.0。退出代码:3.没有错误消息框。控制台消息:“此应用程序已请求运行时以异常方式终止它。请联系该应用程序的支持团队以获取更多信息。”。
Vadzim '18年

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- 2011年

12
它的崩溃方式与应用程序中的常规SIGSEGV完全相同(这是大多数应用程序崩溃的方式)。明确定义了它的作用(默认情况下,它退出应用程序并生成一个核心文件)。是的,您可以设置一个处理程序,但是如果您没有,请以相同的方式进行测试!
马丁·约克

1
为+1 raise()。这样,您只需更改参数即可测试大量不同类型的异常。

3
最喜欢的解决方案,但是它取决于平台。
Nadim Farhat 2014年

@NadimFarhat:用什么方式。信号是所有平台上的信号。
马丁·约克

74

除以零将使应用程序崩溃:

int main()
{
    return 1 / 0;
}

29
根据编译器的智能程度,它会在编译时被捕获。我知道Visual Studio 2008不会针对c ++或c#进行编译。
AidanO 2012年

2
我已经编译并运行它,您是否希望我为.exe打磨您?
Roee Gavirel'4

1
在Visual Studio最新版本(如2010)中的发行配置中,它可以正常运行。我想这是一些优化。
tedyyu

2
iirc它不会在手臂上崩溃
sherpya 2015年

2
这是未定义的行为,不能保证会崩溃。通常,编译器会认为未定义的行为是不可访问的。这可能导致main包括ret指令在内的主体被完全删除。执行可能只属于以下功能。
usr

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

54
这不能保证会崩溃。
Windows程序员

8
@Windowsprogrammer:不,不保证。但是,哪个理智的OS 不会停止尝试访问地址0处的内存的应用程序?
约阿希姆·绍尔

29
“但是,哪个理智的OS不会停止尝试访问地址0处的内存的应用程序?” -这不是您真正要问的,但我还是会回答。在某些计算机中,RAM的地址为0,这对于程序在其中存储值非常有意义。一个更有意义的问题是“哪个OS不会在C ++实现为空指针保留的地址处停止访问内存的应用程序?” 在那种情况下,我什么都不知道。但是原始程序是关于C ++语言的,而不是关于操作系统的。
Windows程序员

6
它的不确定行为。不执行任何操作完全可以。不会崩溃的机器:任何运行Z80系列处理器的设备(我想(我的Z80a不会执行任何操作))。
马丁·约克

28
虽然这并不保证死机,这是最常见的一个种类的在C ++中崩溃。因此,如果您要模拟崩溃,这是一种“真实的”方法:)
克里斯·伯特·布朗

53

好吧,我们堆栈溢出了吗?

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

(不保证按任何标准崩溃,但是无论如何都不会建议任何建议的答案,包括可接受的答案SIGABRT。因为实际上可能会被捕获。)


4
我可以看到在没有受保护代码页的系统上很有趣,并且您用一些代码覆盖了程序,而这些代码偶然是一个无所事事的无限循环。高度,高度,高度不可能,但可能。
马丁·约克

@Loki:如果仅从第4000个字节读取该怎么办?那会不会更容易崩溃?绝对不那么危险。
Mooing Duck 2011年

70
那个崩溃算法不是O(1)!
安东·巴科夫斯基2011年

@MooingDuck:我只是在做一个有趣的评论。别那么认真:-)但是,如果有人发现了一系列有趣的指令,那将很有趣。
马丁·约克

1
@LokiAstari:你绝对正确。我当时正在考虑(&i)[i] += !i,但是我担心编译器可能足够聪明,并且希望对其进行优化。:-)
sam hocevar 2011年

35
 throw 42;

只是答案... :)


1
视窗。GCC-5.4.0。退出代码:3.没有错误消息框。控制台消息:“在引发'int'实例后调用终止方法。此应用程序已请求运行时以一种不寻常的方式终止它。请联系应用程序的支持团队以获取更多信息。”。
Vadzim '18年

15

assert(false); 也很好

根据ISO / IEC 9899:1999,保证在未定义NDEBUG时崩溃:

如果定义了NDEBUG,则将assert宏定义为

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

每次包含时,将根据NDEBUG的当前状态重新定义断言宏。

[...]

assert宏将诊断测试放入程序中;[...]如果expression(应为标量类型)为假[...]。然后,它调用中止功能。


我隐约记得VC 2005在调试和使用断言发布之间的行为有所不同吗?
汤姆·克尔

8
@Tom 在发布模式下assert等效((void)0)
塞斯·卡内基

2
@SethCarnegie看不出这是怎么回事-仅当定义的NDEBUG不会崩溃时?丹斯的回答很不错,恕我直言。
Adrian Cornish

@AdrianCornish我只是在回答汤姆·克尔的问题,并不是说这个答案是错误的。我没有拒绝这个答案。
塞斯·卡内基

3
我不知道他为什么要对此测试代码进行“发布”构建。
Joel B

11

由于崩溃是调用未定义行为的征兆,并且由于调用未定义行为可能导致任何事情,包括崩溃,因此我认为您并不想真正使程序崩溃,而只是将其放入调试器中。这样做最方便的方法是abort()

虽然raise(SIGABRT)具有相同的效果,但肯定要写得多。但是,可以通过为安装安装信号处理程序来拦截两种方式SIGABRT。因此,根据您的情况,您可能希望/需要提出另一个信号。SIGFPESIGILLSIGINTSIGTERM或者SIGSEGV可能是要走的路,但他们都可以被截获。

当您无法携带时,您的选择可能会更广泛,例如SIGBUS在linux上使用。


1
我真的怀疑他是否需要调试器。他似乎想测试当崩溃的程序的调用者发送崩溃信息时会发生什么。这是非常合理的。
Donal Fellows

9

我唯一的闪光是abort()函数

它中止具有异常程序termination.It该过程生成的SIGABRT信号,默认情况下导致程序终止返回不成功的终止错误码给主机environment.The程序被终止而不执行析构函数用于自动或静态存储持续时间的对象,而无需调用任何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 :: terminate(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提到没有try块的throw导​​致隐式调用中止:

如果当前没有异常在处理,则执行不带操作数的throw-expression调用std :: terminate()

其他包括:从析构函数中抛出:ISO / IEC 14882§15.2/ 3


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

这将产生分段错误。


10
这不能保证会崩溃。
Windows程序员

23
“将会发生什么呢?” -可能发生任何事情。行为是不确定的,因此该实现可以将0分配给您的程序变量之一,或者可以将42分配给您程序的变量之一,或者可以格式化硬盘并继续执行您的程序。
Windows程序员

7
(继续使用“ Windows程序员”的想法)它可能使您的计算机爆炸,或者可能使它爆炸并接替人类。或...它将以99.9%的速度崩溃,并且被定义为“未定义的行为”,因为没有人愿意对此承担责任。
罗伊·加维雷尔

1
实际上,甚至不能保证做到未定义的行为-它可以完全定义并正常工作。考虑以下代码:pastebin.com/WXCtTiDD(在Linux上以root用户身份进行测试,如果您对wiki.debian.org/mmap_min_addr进行一些配置更改,也可以以非root用户身份执行此操作
cha0site 2012年

2
@ cha0site:标准保证它是未定义的行为,因为这是在取消引用空指针。无论你在Linux上观察到的行为是允许的“未定义行为”的保护伞下
本·福格特

6

这一个失踪了:

int main = 42;

1
是的,它确实; 但是当您运行它时,它会做得非常壮观。
约书亚

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
什么会阻止足够智能的编译器优化未使用的堆栈分配和尾调用?
JB。

@JB:不幸的是不知道,因为不熟悉现有的编译器优化逻辑
sll

8
好吧,在这里使用gcc 4.6.0在-O2和更高的优化级别上进行编译,它可以很好地进行优化。分段需要-O1或更低。
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);
}

与SIGSEGV相比,这可以确保杀死子进程。


2
这不能保证会崩溃。
Windows程序员

11
C ++语言不保证1/0将导致SIGFPE。行为是不确定的。可以说结果的实施是42
Windows程序员

1
当行为未定义时,实现可以执行任何所需的操作。C ++语言既不阻止也不需要执行写一个故障转储,C ++语言既不阻止也不需要执行分配42等
视窗程序员

2
@Giorgio如果硬件没有自动捕获它的某种方式,您仍然会强制编译器发出至少两条指令,其中一条也将是一个分支。这大约是一个部门的成本的两倍。每个人都这样付钱。如果它是可选的并且您想要它,则始终可以始终使用库函数。如果它不是可选的并且您不想要它,那么最终还是要付出代价。
Flexo

2
@乔治:我有一个应用程序,进行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,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000 * 10 ^ 1,000,000的划分。我知道,尽管编译器无法知道这些,但其中的0将被零除。我绝对不希望编译器将检查除以零。
马丁·约克

4

这是以上答案中给出的更有保证的中止版本。它可以处理sigabrt被阻止时的情况。您可以使用任何信号来代替实际上具有使程序崩溃的默认行为的中止。

#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崩溃,我猜应该在所有OS-es上都执行相同的操作。


5
on all OS-es不,它不会在非受保护的操作系统崩溃(如MS-DOS)。其实,有的时候一些在地址0!对于x86实模式,中断向量表的地址为
0。– ikh

它没有在Irix上崩溃。当我们移植的代码到Linux(在这里我们甚至没有达到我懊丧地意识到main()
雪夫

3

这是Google在Breakpad中提供的代码段。

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

1
由于我正在测试Breakpad,所以这正是我想要的!我发现某些Breakpad minidumps不会产生指向导致崩溃的代码行的堆栈跟踪。确实如此,因此我可以将其用作良好的poc测试。
BuvinJ


2

尽管这个问题已经有了可接受的答案...

void main(){
    throw 1;
}

要么... void main(){throw 1;}


2

除非系统不支持只读内存块,否则写入只读内存将导致分段错误。

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

我已经在Windows 7上的MingGW 5.3.0和Linux Mint的GCC上对其进行了测试。我想其他编译器和系统也会产生类似的效果。


1

或者说另一种方式,因为我们在乐队旅行中。

可爱的无限递归。保证会打击您的筹码。

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

打印输出:

分段故障(核心已转储)


13
调用main自己实际上是未定义的行为,以防万一您不知道:)此外,也不保证尾部递归会破坏堆栈。如果要“保证”,则必须在递归调用之后执行某些操作,否则编译器可以将递归优化为无限循环。
fredoverflow 2012年

0

尚未提及的一个:

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

这会将空指针视为函数指针,然后调用它。像大多数方法一样,这不能保证程序会崩溃,但是操作系统允许对此进行检查的可能性以及程序返回的可能性都可以忽略不计。


3
我知道有几台计算机,这将导致重新启动,因为OS启动代码已有效地映射到地址0。您可以说这是崩溃,但是它并不是很有用,因为您无法调试它,因为启动时会擦除所有状态。
马丁·约克

@Loki Astari:我还是要说这是崩溃-如果这可能导致崩溃,那么被调试的程序也可能会崩溃,这意味着它与任何测试一样好。另一方面,我很好奇这些机器中哪些可以运行Python。
安东·戈洛夫

我想你错过了重点。我已经看到OS代码为0。这并不意味着没有系统可以正常运行的良好代码在0时就可以正常工作。或者在0处的字节很容易成为返回的操作码。
马丁·约克

我知道OS代码为0;这是我强烈怀疑0处的字节是否为返回操作码的原因之一。显然,该程序不再执行,也没有以通常的方式退出,即崩溃了-如果这对询问者而言还不够好,我希望他自己对此发表评论。
安东·戈洛夫

2
您仅知道计算机的OS代码。我要说的是,它可能对您失败。但这没什么意义。那里有很多系统。我确信其中一些可能有效(即不崩溃)。依靠机器/操作系统特定的行为是一个坏主意,从长远来看会导致维护问题。该站点的想法是推广好的代码(而不仅仅是推广这种工作的代码)。
马丁·约克

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
有一点澄清将很
高兴

1
p指针将超出程序的地址空间,这将是一个内存错误,因为一个进程无法访问另一个进程的内存。这将导致程序崩溃。指针p指向其地址空间中的一个随机位置,如果在某个点上对其进行无限递增和取消引用,它将指向另一个程序的(进程)地址空间。因此一段时间后它将崩溃。
sc_cs 2014年

或者,假设它可以实现整数溢出并无限循环运行。我会尝试使用long longsize_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系统,其他系统可能具有其他陷阱/断点指令。

int main()
{
    __asm int 3;

    return 0;
}

INT 3 导致中断并调用由操作系统设置的中断向量。


0

在GCC或clang中使用__builtin_trap(),在MSVC中使用__debugbreak()。不处理这些断点/陷阱将导致未处理的断点异常/崩溃。其他使用abort()或exit()的建议:这些建议可能由其他线程处理,这使得查看导致崩溃的线程堆栈更加困难。


-2
char*freeThis;
free(freeThis);

释放未初始化的指针是未定义的行为。在许多平台/编译器上,freeThis将具有随机值(之前位于该内存位置的任何值)。释放它会要求系统释放该地址处的内存,这通常会导致分段错误并使程序崩溃。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.