确定导致分段错误的代码行?

136 c c++ debugging segmentation-fault

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

在编写了一些代码后,为了确定我在哪里有分段错误,我的编译器(gcc)能告诉我程序中的错误位置吗?

nc3*_*c3b 190

海湾合作委员会不能这样做,但GDB肯定可以.使用-g开关编译程序,如下所示:

gcc program.c -g
Run Code Online (Sandbox Code Playgroud)

然后使用gdb:

$ gdb ./a.out
(gdb) run
<segfault happens here>
(gdb) backtrace
<offending code is shown here>
Run Code Online (Sandbox Code Playgroud)

是一个很好的教程,可以帮助您开始使用GDB.

  • 请注意,发生段错误的地方通常只是关于"导致错误"的地方的一个线索.一个重要的线索,但它不一定是问题所在. (21认同)
  • 您还可以使用(bt full)获取更多详细信息. (9认同)
  • 使用 `bt` 作为 `backtrace` 的简写。 (3认同)
  • 我觉得这很有用:https://www.gnu.org/software/gcc/bugs/segfault.html (2认同)

小智 37

此外,您可以尝试Valgrind:如果您安装Valgrind并运行valgrind --leak-check = full,那么它将运行您的程序并显示任何段错误的堆栈跟踪,以及任何无效的内存读取或写入以及内存泄漏.这真的很有用.

  • +1,Valgrind更快/更容易用来发现内存错误.在带有调试符号的非优化构建中,它会告诉您_exactly_发生段错误的原因以及原因. (2认同)
  • --leak-check = full将无助于调​​试段错误。仅对调试内存泄漏有用。 (2认同)

Luc*_*cas 17

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

每当你收到消息时:

 Segmentation fault (core dumped)
Run Code Online (Sandbox Code Playgroud)

核心文件写入当前目录.你可以用命令检查它

 gdb your_program core_file
Run Code Online (Sandbox Code Playgroud)

该文件包含程序崩溃时的内存状态.在部署软件期间,核心转储可能很有用.

确保您的系统未将核心转储文件大小设置为零.您可以将其设置为无限制:

ulimit -c unlimited

虽然小心!核心转储可能会变得巨大.


asy*_*nts 11

有许多工具可以帮助调试分段错误,我想将我最喜欢的工具添加到列表中:Address Sanitizers(通常缩写为 ASAN)

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

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

int main() {
  volatile int *ptr = (int*)0;
  *ptr = 0;
}
Run Code Online (Sandbox Code Playgroud)
$ 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
Run Code Online (Sandbox Code Playgroud)

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

  • 无需重现问题即可接收堆栈跟踪。只需在开发过程中启用标志就足够了。

  • ASAN 捕获的不仅仅是分段错误。即使进程可以访问该内存区域,也会捕获许多越界访问。


¹ 那是Clang 3.1+GCC 4.8+

  • 这对我来说是最有帮助的。我有一个非常微妙的错误,它随机发生,频率约为 1%。我使用(16 个主要步骤;每个步骤由不同的 C 或 C++ 二进制文件完成)处理大量输入文件。由于多线程,后面的步骤只会随机触发分段错误。很难调试。这个选项触发了调试信息输出,至少它给了我一个代码审查的起点,以找到错误的位置。 (3认同)

Jer*_*ner 9

以上所有答案均正确且推荐;如果无法使用上述方法,则此答案仅作为最后的手段。

如果所有其他方法都失败了,您始终可以使用各种临时调试打印语句(例如fprintf(stderr, "CHECKPOINT REACHED @ %s:%i\n", __FILE__, __LINE__);)重新编译您的程序,这些语句散布在您认为是代码的相关部分中。然后运行该程序,并观察崩溃发生之前打印的最后一个调试打印内容 - 您知道您的程序已经走到了这一步,因此崩溃一定是在该点之后发生的。添加或删除调试打印、重新编译并再次运行测试,直到将范围缩小到一行代码。此时,您可以修复错误并删除所有临时调试打印。

这很乏味,但它的优点是几乎可以在任何地方工作 - 唯一可能不会的情况是,如果您由于某种原因无法访问 stdout 或 stderr,或者您试图修复的错误是一个竞争-当程序时间改变时其行为发生变化的条件(因为调试打印会减慢程序速度并改变其时间)