什么导致SIGSEGV错误?

122

什么是段错误(SIGSEGV)的根本原因,以及如何处理它?

7个回答

134

维基百科和其他许多来源都提供了答案。

段错误基本上意味着你在使用指针时出了问题。这可能会导致段错误:

char *c = NULL;
...
*c; // dereferencing a NULL pointer
或者这样:
char *c = "Hello";
...
c[10] = 'z'; // out of bounds, or in this case, writing into read-only memory
也许是这个:
char *c = new char[10];
...
delete [] c;
...
c[2] = 'z'; // accessing freed memory

每种情况下的基本原理相同 - 你正在处理不属于自己的内存。


24

发生段错误有各种原因,但根本上来说是访问内存不正确导致的。这可能是由于取消引用空指针,尝试修改只读内存,或使用指向未映射到您进程内存空间的地方的指针引起的(这可能意味着您正在尝试使用数字作为指针,或者您增加了指针的偏移量)。在某些机器上,通过指针进行错误对齐访问也可能导致问题-例如,如果您有一个奇数地址并尝试从其中读取偶数个字节,则会生成SIGBUS。


这里的不同错误信号定义不清 - 在OS X上,char *c = NULL; *c;实际上会生成SIGBUS而不是SIGSEGV。 - Chris Lutz
1
是的 - SIGBUS和SIGSEGV都有些系统和/或处理器特定。两者都意味着您正在滥用内存。两者都不健康。 - Jonathan Leffler

10

SigSegV 意味着内存访问违规的信号,尝试读取或写入您的进程无法访问的内存区域。这些不是 C 或 C++ 异常,您无法捕获信号。确实可以编写信号处理程序来忽略问题并允许在未定义状态下继续执行不稳定的程序,但显然这是一个非常糟糕的想法。

大多数情况下,这是由程序中的错误引起的。给定的内存地址可以帮助调试问题(如果接近零,则很可能是空指针解引用,如果地址类似于 0xadcedfe,则是有意设置的保护或调试检查等)

“捕获”此信号的一种方法是在单独的子进程中运行您的内容,然后可以突然终止而不会使主进程崩溃。找到根本原因并加以修复显然优于像此类变通方法。


7

使用无效/空指针?超出数组边界?如果没有任何示例代码,很难具体说明。

基本上,您试图访问不属于您的程序的内存,因此操作系统会终止它。


6

以下是 SIGSEGV 的一个示例。

root@pierr-desktop:/opt/playGround# cat test.c
int main()
{
     int * p ;
     * p = 0x1234;
     return 0 ;
}
root@pierr-desktop:/opt/playGround# g++ -o test test.c  
root@pierr-desktop:/opt/playGround# ./test 
Segmentation fault

以下是详细信息

如何处理它?

  1. 尽可能地避免出现这种情况。

    进行防御性编程:使用assert(),检查NULL指针,检查缓冲区溢出。

    使用静态分析工具检查您的代码。

    使用-Werror -Wall编译您的代码。

    让其他人审查您的代码。

  2. 当实际发生时。

    仔细检查您的代码。

    检查自上次成功运行代码以来所做的更改。

    希望gdb会给您一个调用栈,以便您知道崩溃发生的位置。


编辑:抱歉匆忙。 应该是 *p = 0x1234; 而不是 p = 0x1234;


为什么给指针分配一个无效值而不是对该指针进行解引用会导致 SIGSEGV? - sharptooth
1
这个程序无法使用任何 C++ 编译器编译。如果您添加必要的转换,它将不会崩溃,因为它实际上没有任何无效内存访问。 - Employed Russian
严格来说,将任意值强制转换为指针对象可能会导致C/C++程序立即崩溃,而不需要进行解引用尝试(请阅读“陷阱表示”),但这并不是我们大多数人在实践中遇到的情况。当然,这不是一个很好的例子来说明SIGSEGV :) - AnT stands with Russia
每当我遇到段错误时,我都会使用经典的printf()方法进行调试,逐步锁定问题所在。实际上,puts()可能是最适合此目的的函数,因为它会自动添加换行符,并自动刷新输出。但有时我也需要打印变量值。 - Chris Lutz

3

最初的源头原因也可以是内存不足。


我正在尝试寻找更多信息,但并没有取得太大的成功。我的猜测是,如果该进程已经分配了一些大块内存,并且开启了过度承诺,一旦开始写入,内核将寻找物理内存,如果失败,则会出现分段错误。这是正确的吗? - martinkunev
我认为尝试分配你实际上没有的内存,或超出沙盒限制,会在程序中某个地方引起崩溃或触发SIGSEGV。 - Makusensu

1

当程序访问未被声明的内存时,会出现分段错误。您可以通过指针(即内存地址)来实现此操作。或者也可能是由于堆栈溢出等问题导致分段错误:

void rec_func() { int q = 5; rec_func(); }

public int Main() { rec_func(); return 0; }

这个调用将继续消耗堆栈内存,直到完全填满,最终导致堆栈溢出的情况发生。 注意: 在一些竞争性问题中可能看不到它,因为它首先会导致超时错误,但对于那些超时不会发生的问题来说,找出 SIGSEGV 是很困难的。


这会抛出异常 Exception of type 'System.StackOverflowException' was thrown.,但不是 SIGSEGV。 - Matt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接