如何确定导致分段错误的代码中的错误在哪里?

我的编译器(gcc)可以显示程序中错误的位置吗?


当前回答

您还可以使用一个核心转储,然后用gdb检查它。要获得有用的信息,还需要使用-g标志进行编译。

每当你收到这样的信息:

 Segmentation fault (core dumped)

一个核心文件被写入当前目录。您可以使用命令检查它

 gdb your_program core_file

该文件包含程序崩溃时内存的状态。核心转储在软件部署期间非常有用。

确保您的系统没有将核心转储文件大小设置为零。你可以通过以下方法将其设置为无限:

无限制

不过小心!核心转储可能变得巨大。

其他回答

如果你们中有人(像我一样!)也在寻找同样的问题,但是使用gfortran而不是gcc,那么现在编译器要强大得多,在使用调试器之前,您还可以尝试这些编译选项。对我来说,这准确地确定了发生错误的代码行,以及我访问的哪个变量越界导致了分割错误错误。

-O0 -g -Wall -fcheck=all -fbacktrace

您还可以使用一个核心转储,然后用gdb检查它。要获得有用的信息,还需要使用-g标志进行编译。

每当你收到这样的信息:

 Segmentation fault (core dumped)

一个核心文件被写入当前目录。您可以使用命令检查它

 gdb your_program core_file

该文件包含程序崩溃时内存的状态。核心转储在软件部署期间非常有用。

确保您的系统没有将核心转储文件大小设置为零。你可以通过以下方法将其设置为无限:

无限制

不过小心!核心转储可能变得巨大。

以上答案都是正确的,建议回答;如果前面提到的方法都不能使用,这个答案只是作为最后的手段。

If all else fails, you can always recompile your program with various temporary debug-print statements (e.g. fprintf(stderr, "CHECKPOINT REACHED @ %s:%i\n", __FILE__, __LINE__);) sprinkled throughout what you believe to be the relevant parts of your code. Then run the program, and observe what the was last debug-print printed just before the crash occurred -- you know your program got that far, so the crash must have happened after that point. Add or remove debug-prints, recompile, and run the test again, until you have narrowed it down to a single line of code. At that point you can fix the bug and remove all of the temporary debug-prints.

这很乏味,但它的优点是几乎可以在任何地方工作——如果由于某种原因无法访问stdout或stderr,或者如果您试图修复的bug是一个竞态条件,其行为会随着程序的时间变化而变化(因为调试打印会减慢程序并改变其时间),则可能无法工作。

有很多工具可以帮助调试分割错误,我想把我最喜欢的工具添加到列表中:地址消毒器(通常缩写为ASAN)。

现代的编译器带有方便的-fsanitize=address标志,增加了一些编译时间和运行时开销,从而进行更多的错误检查。

根据文档,这些检查包括默认情况下捕获分段错误。这样做的好处是,您可以获得类似于gdb输出的堆栈跟踪,但无需在调试器中运行程序。一个例子:

int main() {
  volatile int *ptr = (int*)0;
  *ptr = 0;
}
$ gcc -g -fsanitize=address main.c
$ ./a.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==4848==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5654348db1a0 bp 0x7ffc05e39240 sp 0x7ffc05e39230 T0)
==4848==The signal is caused by a WRITE memory access.
==4848==Hint: address points to the zero page.
    #0 0x5654348db19f in main /tmp/tmp.s3gwjqb8zT/main.c:3
    #1 0x7f0e5a052b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)
    #2 0x5654348db099 in _start (/tmp/tmp.s3gwjqb8zT/a.out+0x1099)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /tmp/tmp.s3gwjqb8zT/main.c:3 in main
==4848==ABORTING

输出比gdb输出稍微复杂一些,但也有好处:

不需要重新生成问题来接收堆栈跟踪。在开发过程中启用标志就足够了。 asan捕捉到的不仅仅是分割错误。即使该内存区域可以被进程访问,许多越界访问也会被捕获。


¹即Clang 3.1+和GCC 4.8+。

这是一种粗略的方法来找到确切的线之后,有分割故障。

定义线路记录函数

# include \ < iostream > 无效日志(int line) { Std::cout << line << Std::endl; }

查找并替换log函数后的所有分号为";日志(_LINE_);“ 确保在for(;;)循环中替换为函数的分号被删除