C++ 回溯显示析构函数在陌生线路上调用的偶发性崩溃
我有一个程序,通常运行得很好,但今天它在启动时崩溃了。之后立即再次运行它效果很好,所以很不幸,我不能给出一个简单的例子。但是,代码如下所示:C++ 回溯显示析构函数在陌生线路上调用的偶发性崩溃,c++,segmentation-fault,destructor,C++,Segmentation Fault,Destructor,我有一个程序,通常运行得很好,但今天它在启动时崩溃了。之后立即再次运行它效果很好,所以很不幸,我不能给出一个简单的例子。但是,代码如下所示: #include "Configuration.hpp" #include "Program.hpp" int main() { ConfigurationReader confReader; // this is line 6, where gdb ind
#include "Configuration.hpp"
#include "Program.hpp"
int main()
{
ConfigurationReader confReader; // this is line 6, where gdb indicates a
// segfault in the *destructor* of
// ConfigurationReader
confReader.readConf();
Conf & conf = confReader.getConf();
Program program(conf);
program.run();
return 0;
}
程序报告了一个SEGFULT,并在gdb中启动了核心。它说SEGFULT发生在上面代码示例中的第6行,在ConfigurationReader的析构函数中
当然,在这里调用析构函数是没有意义的,因为只有一个ConfigurationReader实例在浮动,在main结尾超出范围之前,它不应该进行析构函数。即使进行了积极的优化,在程序进行破坏之前,它也无法进行破坏,因为程序被传递到了对confReader内部某个东西的引用
问题:这里发生了或可能发生了什么?是否有一些我没有看到的未定义的行为?gdb的堆栈跟踪是否非常错误?我是否应该怀疑构建过程出了问题
注意:我知道在设计上最好不要让ConfigurationReader拥有它读入的Conf实例,但这不是这个问题的重点。请不要只是告诉我这样做而不回答实际问题
更新:正如John在评论中指出的,我还应该在这里提供一些关于ConfigurationReader::getConf的信息:
更新2:为了使代码示例可复制,我删除了行号,只添加了一条注释,指示gdb显示调用析构函数的位置
注2:正如我最初所说,不幸的是,我无法提供一个最小的可复制示例。我不能在玩具程序中重现这个问题。我甚至不能在真实的程序中重现这个问题;此崩溃只发生过一次。仅凭您发布的代码,无法找出实际问题所在。因此,这不是对您的问题的回答,而是帮助您自己使用gdb找到解决方案的一种方法。不过,这是一个有点大的评论 在gdb中运行程序,并在main的最开始处停止。您可以使用“开始”命令进行此操作。现在在程序的最后一条指令处添加一个断点。您可以在gdb中使用b_exit实现这一点。只有当问题没有发生时,您的程序才应该达到此断点。当您在gdb中添加一个断点时,您会得到一个标识该断点的数字。假设_exit中的断点是断点2。您可以在gdb中添加命令,这些命令应该在命中某个特定断点时自动运行。使用命令2向断点2添加命令,键入run添加run命令,按ENTER键,键入end完成输入命令。现在键入run开始运行程序
这样,您的程序将在gdb中运行,如果没有发生错误,它将在命中最后一条指令时从头开始重新运行。当错误最终发生时,gdb将停止执行,您可以使用实际的进程而不仅仅是核心转储来调查问题。我实际上能够在这里解决自己的问题,部分是为了回答PaulMcKenzie在评论中提出的问题。为了举例说明,这里有一个玩具程序:
class C { };
class D
{
public:
~D()
{
int * p = nullptr;
int x = *p;
}
};
class E { };
class F { };
int main(int, char **)
{
C c;
D d; // this is line 21
E e;
F f;
return 0;
}
当d自毁时,这当然会在main的末尾崩溃,因为我故意在自毁函数中放入一个segfault
如果我使用调试符号构建它,然后在gdb中运行它,我会得到以下回溯:
#0 0x00005555555546cc in D::~D (this=0x7fffffffde67, __in_chrg=<optimized out>) at program.cpp:10
#1 0x000055555555469a in main () at program.cpp:21
注意它在堆栈帧1中报告的行号。当然,在现实中,第22、23、24、25和26行已经完成,如果需要,我们可以通过登录来证明这一点
显然,gdb或负责将行号关联到编译器输出的工具的任何部分已经决定,当对象超出范围时,析构函数调用应该关联到最终导致它们的实际代码行,即构建对象的代码行
因此,我的程序中可能发生的情况是,程序完成了,而崩溃实际上发生在运行过程中,比预期的要晚得多。这是它自己的问题,因为program.run是一个没有退出条件的无限循环。由于ConfigurationReader的析构函数不可访问,而且以前从未运行过,因此它不能正常工作也就不足为奇了。请按要求执行?您是否尝试过使用valgrind运行可执行文件?或者使用净化程序编译?getConf的返回类型是什么?之后立即再次运行它效果很好,所以很遗憾,我不能给出一个简单的示例-将代码发布到这些头中。您按原样发布的任何代码都不会产生任何错误。可能您有一个依赖于另一个要创建的全局对象的全局对象,但它没有被创建。另外,main实际上没有运行的证据在哪里?也许是一些问题导致了main的右转 直到最后,你认为这不会发生。
#0 0x00005555555546cc in D::~D (this=0x7fffffffde67, __in_chrg=<optimized out>) at program.cpp:10
#1 0x000055555555469a in main () at program.cpp:21