修复 C++ 中的分段错误

Posted

技术标签:

【中文标题】修复 C++ 中的分段错误【英文标题】:Fixing Segmentation faults in C++ 【发布时间】:2011-04-12 18:01:38 【问题描述】:

我正在为 Windows 和 Unix 编写一个跨平台的 C++ 程序。在 Window 端,代码将编译和执行没有问题。在 Unix 端,它会编译,但是当我尝试运行它时,会出现分段错误。我最初的预感是指针有问题。

什么是查找和修复分段错误的好方法?

【问题讨论】:

【参考方案1】:

    使用-g 编译您的应用程序,然后您将在二进制文件中包含调试符号。

    使用gdb打开gdb控制台。

    使用file 并在控制台中将应用程序的二进制文件传递给它。

    使用run 并传入应用程序启动所需的任何参数。

    做某事导致分段错误

    gdb 控制台中键入bt 以获取分段错误的堆栈跟踪。

【讨论】:

CMake的上下文中用g编译是什么意思? 启用调试构建类型。一种方法是cmake -DCMAKE_BUILD_TYPE=Debug【参考方案2】:

有时崩溃本身并不是问题的真正原因——也许内存在较早的时候被破坏了,但损坏需要一段时间才能显现出来。查看valgrind,它对指针问题进行了大量检查(包括数组边界检查)。它会告诉您问题从哪里开始,而不仅仅是发生崩溃的那一行。

【讨论】:

【参考方案3】:

在问题出现之前,尽量避免:

尽可能频繁地编译和运行您的代码。故障部位更容易定位。 尝试封装低级/容易出错的例程,这样您就很少需要直接使用内存(注意程序的建模) 维护一个测试套件。概述当前工作的内容,不再工作的内容等,将帮助您找出问题所在(Boost test 是一个可能的解决方案,我自己不使用它,但文档可以帮助您了解什么必须显示某种信息)。

使用适当的工具进行调试。在 Unix 上:

GDB 可以告诉您程序崩溃的位置,并让您查看在什么情况下。 Valgrind 将帮助您检测许多与内存相关的错误。 对于 GCC,您还可以使用 mudflap 对于 GCC、Clang 以及自 10 月起实验性的 MSVC,您可以使用 Address/Memory Sanitizer。它可以检测到一些 Valgrind 没有检测到的错误,并且性能损失更轻。通过使用-fsanitize=address 标志编译来使用它。

最后我会推荐通常的东西。你的程序越是可读、可维护、清晰和整洁,就越容易调试。

【讨论】:

【参考方案4】:

在 Unix 上,您可以使用 valgrind 来查找问题。它免费且功能强大。如果您想自己做,您可以重载newdelete 运算符来设置一个配置,其中每个新对象前后都有一个带有0xDEADBEEF 的字节。然后跟踪每次迭代发生的情况。这可能无法捕获所有内容(您甚至不能保证触摸这些字节),但它过去在 Windows 平台上对我有用。

【讨论】:

嗯,这将是 4 个字节而不是 1 个......但原理很好。 我可以链接到我的non-intrusive heap debugger 吗? :-) 去吧。我们在这里都是为了帮助其他人,所以应该添加任何可以提供帮助的内容。 虽然重载newdelete 可能非常有用,但使用-fsanitize=address 是更好的选择,因为编译器将在运行时检测问题时进行编译,并将内存自动转储到屏幕上,这使得调试方式更容易。 除了newdelete之外,如果你使用gcc,你可以换行malloc。见--wrap=symbol。我将在发布代码中执行此操作,以便获得一些运行时诊断。【参考方案5】:

是的,指针有问题。很可能您使用的是未正确初始化的内存,但也有可能您使用双重释放或类似的方式弄乱了内存管理。

为避免将未初始化的指针作为局部变量,请尝试尽可能晚地声明它们,最好(这并不总是可能)当它们可以用有意义的值初始化时。通过检查代码,说服自己在使用它们之前它们将具有价值。如果您对此有困难,请将它们初始化为空指针常量(通常写为NULL0)并检查它们。

为避免将未初始化的指针作为成员值,请确保它们在构造函数中正确初始化,并在复制构造函数和赋值运算符中正确处理。不要依赖init 函数进行内存管理,尽管您可以进行其他初始化。

如果您的类不需要复制构造函数或赋值运算符,您可以将它们声明为私有成员函数并且从不定义它们。如果显式或隐式使用它们将导致编译器错误。

在适用时使用智能指针。这里最大的好处是,如果你坚持并始终如一地使用它们,你可以完全避免写delete,并且不会重复删除任何内容。

尽可能使用 C++ 字符串和容器类,而不是 C 样式的字符串和数组。考虑使用.at(i) 而不是[i],因为这将强制进行边界检查。看看你的编译器或库是否可以设置为检查[i] 的边界,至少在调试模式下是这样。分段错误可能是由缓冲区溢出导致的,将垃圾写入完美的指针。

执行这些操作将大大降低出现分段错误和其他内存问题的可能性。他们无疑无法解决所有问题,这就是为什么你应该在没有问题时不时使用 valgrind,而当你有问题时使用 valgrind 和 gdb。

【讨论】:

【参考方案6】:

我不知道有什么方法可以用来解决此类问题。我认为不可能提出一个问题,因为手头的问题是您的程序的行为是未定义的(我不知道 SEGFAULT 不是由某种 UB 引起的任何情况) .

有各种各样的“方法”可以在问题出现之前避免它。一个重要的就是 RAII。

除此之外,你只需要投入你最好的精神能量。

【讨论】:

以上是关于修复 C++ 中的分段错误的主要内容,如果未能解决你的问题,请参考以下文章

带有向量和字符串的 C++ 分段错误

C++ 中的分段错误(特征库)

C++:当我添加看似无关的代码行时,分段错误消失了

C++ 分段错误 OpenCV

在 C++ 中使用向量时出现分段错误

在 C++ 中的向量中键入所有值后出现分段错误(核心转储)