什么是段错误(SIGSEGV)的根本原因,以及如何处理它?
维基百科和其他许多来源都提供了答案。
段错误基本上意味着你在使用指针时出了问题。这可能会导致段错误:
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
每种情况下的基本原理相同 - 你正在处理不属于自己的内存。
发生段错误有各种原因,但根本上来说是访问内存不正确导致的。这可能是由于取消引用空指针,尝试修改只读内存,或使用指向未映射到您进程内存空间的地方的指针引起的(这可能意味着您正在尝试使用数字作为指针,或者您增加了指针的偏移量)。在某些机器上,通过指针进行错误对齐访问也可能导致问题-例如,如果您有一个奇数地址并尝试从其中读取偶数个字节,则会生成SIGBUS。
SigSegV 意味着内存访问违规的信号,尝试读取或写入您的进程无法访问的内存区域。这些不是 C 或 C++ 异常,您无法捕获信号。确实可以编写信号处理程序来忽略问题并允许在未定义状态下继续执行不稳定的程序,但显然这是一个非常糟糕的想法。
大多数情况下,这是由程序中的错误引起的。给定的内存地址可以帮助调试问题(如果接近零,则很可能是空指针解引用,如果地址类似于 0xadcedfe,则是有意设置的保护或调试检查等)
“捕获”此信号的一种方法是在单独的子进程中运行您的内容,然后可以突然终止而不会使主进程崩溃。找到根本原因并加以修复显然优于像此类变通方法。
使用无效/空指针?超出数组边界?如果没有任何示例代码,很难具体说明。
基本上,您试图访问不属于您的程序的内存,因此操作系统会终止它。
以下是 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
以下是详细信息。
如何处理它?
尽可能地避免出现这种情况。
进行防御性编程:使用assert(),检查NULL指针,检查缓冲区溢出。
使用静态分析工具检查您的代码。
使用-Werror -Wall编译您的代码。
让其他人审查您的代码。
当实际发生时。
仔细检查您的代码。
检查自上次成功运行代码以来所做的更改。
希望gdb会给您一个调用栈,以便您知道崩溃发生的位置。
编辑:抱歉匆忙。 应该是 *p = 0x1234;
而不是 p = 0x1234
;
printf()
方法进行调试,逐步锁定问题所在。实际上,puts()
可能是最适合此目的的函数,因为它会自动添加换行符,并自动刷新输出。但有时我也需要打印变量值。 - Chris Lutz最初的源头原因也可以是内存不足。
当程序访问未被声明的内存时,会出现分段错误。您可以通过指针(即内存地址)来实现此操作。或者也可能是由于堆栈溢出等问题导致分段错误:
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
char *c = NULL; *c;
实际上会生成SIGBUS而不是SIGSEGV。 - Chris Lutz