我正在为Windows和Unix编写跨平台的C++程序。在Windows方面,代码可以编译和执行没有问题。但是在Unix方面,它可以编译,但是当我尝试运行它时,会出现段错误。我的第一个想法是指针存在问题。
有哪些好的方法可以找到并解决段错误?
我正在为Windows和Unix编写跨平台的C++程序。在Windows方面,代码可以编译和执行没有问题。但是在Unix方面,它可以编译,但是当我尝试运行它时,会出现段错误。我的第一个想法是指针存在问题。
有哪些好的方法可以找到并解决段错误?
使用-g
编译你的应用程序,这样在二进制文件中就会有调试符号。
使用gdb
打开gdb控制台。
在控制台中使用file
并传入你的应用程序的二进制文件。
使用run
并传入应用程序启动所需的任何参数。
执行某些操作以导致分段错误。
在gdb
控制台中输入bt
,以获取分段错误的堆栈跟踪信息。
在问题出现之前,尽可能避免它:
使用适当的调试工具。在 Unix 上:
-fsanitize=address
标志进行编译。最后,我建议做一些常规的事情。你的程序越可读性、可维护性、清晰和整洁,调试就会越容易。
valgrind
来查找问题。如果你想自己处理问题,可以重载new
和delete
运算符来设置一个配置,其中每个新对象之前和之后都有一个带有0xDEADBEEF
的1字节。然后跟踪每次迭代发生的情况。这种方法可能无法捕捉到所有问题(你甚至不能保证会接触到那些字节),但在过去的Windows平台上对我起过作用。new
和 delete
可以非常有用,但使用 -fsanitize=address
是更好的选择,因为编译器将在运行时检测问题并自动将内存转储到屏幕上,这使得调试变得更加容易。 - Tarick Wellingnew
和 delete
,如果你使用的是 gcc
,你还可以包装 malloc
。请参见 --wrap=symbol
。我将在发布代码中执行此操作,以便获得一些运行时诊断信息。 - Daisuke Aramaki是的,指针存在问题。很可能您正在使用未正确初始化的指针,但也有可能您在双重释放等内存管理方面出现了问题。
为避免本地变量作为未初始化的指针,请尽可能晚地声明它们,最好(并非总是可行)在可以使用有意义的值进行初始化时声明它们。通过检查代码来确信在使用它们之前它们将具有值。如果您在这方面遇到困难,请将它们初始化为空指针常量(通常写为NULL
或0
),然后检查它们。
为避免成员变量作为未初始化的指针,请确保在构造函数中正确初始化它们,并在复制构造函数和赋值运算符中正确处理它们。不要依赖于init
函数进行内存管理,尽管您可以使用它进行其他初始化。
如果您的类不需要复制构造函数或赋值运算符,则可以将它们声明为私有成员函数并从未定义它们。如果显式或隐式使用它们,这将导致编译器错误。
在适用的情况下,请使用智能指针。这里的一个重要优点是,如果您坚持使用它们并始终使用它们,则可以完全避免编写delete
,也不会出现双重删除。
尽可能使用C++字符串和容器类,而不是C风格的字符串和数组。考虑使用.at(i)
而不是[i]
,因为这将强制进行边界检查。请查看您的编译器或库是否可以在调试模式下设置为检查[i]
的边界。缓冲区溢出可能会导致段错误,从而在完全正确的指针上写入垃圾。
执行这些操作将大大降低段错误和其他内存问题的可能性。它们无疑不能解决所有问题,这就是为什么当您没有问题时应使用valgrind,有问题时应同时使用valgrind和gdb。
我不知道有任何方法可以用来修复这种情况。我认为也不可能想出一个,因为你的程序行为未定义(我不知道在什么情况下没有由某种UB导致SEGFAULT)。
在问题出现之前,有各种“方法”可以避免这个问题。其中一个重要的是RAII。
除此之外,你只需要把你最好的能量投入到它中。
g
编译意味着什么? - Schützecmake -DCMAKE_BUILD_TYPE=Debug
。 - Antonin Décimogdb <filename>
来完成第2步和第3步吗? - RARA