通常,是什么原因导致不包含任何动态内存分配的程序中出现双重空闲
更准确地说,我的代码中没有一个使用动态分配。我正在使用STL,但它更有可能是我做错了什么,而不是G++/glibc/STL的一个坏实现。
我四处搜索,试图找到这个问题的答案,但我找不到任何在没有任何动态内存分配的情况下生成此错误的例子。
我很想分享产生这个错误的代码,但我不被允许发布它,我不知道如何将问题减少到足够小的程度。我将尽我所能描述我的代码所做的事情的要点。
该错误是在离开函数时抛出的,堆栈跟踪显示它来自std::vector<std::set<std::string>>
的析构函数。emplace_back()
正在初始化矢量中的一些元素。在最后一次尝试中,我将其更改为push_back({{}})
,问题就消失了。也可以通过设置环境变量MALLOC_CHECK_=2来避免该问题。根据我的理解,该环境变量应该导致glibc中止并提供更多信息,而不是导致错误消失。
问这个问题只是为了满足我的好奇心,所以我只能接受一个蒙在鼓里的答案。我能想出的最好的办法是,这是一个编译器错误,但这总是我的错。
一般来说,在不包含任何动态内存分配的程序中,是什么原因导致双重空闲?
通常情况下,当您复制一个类型时,该类型会动态分配内存,但不遵循三规则
struct Type
{
Type() : ptr = new int(3) { }
~Type() { delete ptr; }
// no copy constructor is defined
// no copy assign operator is defined
private:
int * ptr;
};
void func()
{
{
std::vector<Type> objs;
Type t; // allocates ptr
objs.push_back(t); // make a copy of t, now t->ptr and objs[0]->ptr point to same memory location
// when this scope finishes, t will be destroyed, its destructor will be called and it will try to delete ptr;
// objs go out of scope, elements in objs will be destroyed, their destructors are called, and delete ptr; will be executed again. That's double free on same pointer.
}
}
我提取了一个演示示例,展示了我所犯的导致"双重释放或损坏"运行时错误的错误。请注意,该结构没有显式使用任何动态内存分配,但在内部std::vector使用了(因为它的内容可以增长以容纳更多项)。因此,这个问题有点难以诊断,因为它没有违反"三条规则"原则。
#include <vector>
#include <string.h>
typedef struct message {
std::vector<int> options;
void push(int o) { this->options.push_back(o); }
} message;
int main( int argc, const char* argv[] )
{
message m;
m.push(1);
m.push(2);
message m_copy;
memcpy(&m_copy, &m, sizeof(m));
//m_copy = m; // This is the correct method for copying object instances, it calls the default assignment operator generated 'behind the scenes' by the compiler
}
当main()返回m_copy被销毁时,它调用std::vector析构函数。这将尝试删除在销毁m对象时已释放的内存。
具有讽刺意味的是,我实际上是在使用memcpy来尝试实现"深度复制"。这就是我的过错所在。我的猜测是,通过使用赋值运算符,message.options的所有成员实际上都被复制到"新分配的内存"中,而memcpy只会复制那些在编译时分配的成员(例如uint32_t大小的成员)。请参阅memcpy或memmove会导致复制类时出现问题吗?。显然,这也适用于具有非基本类型成员的结构(如这里的情况)。
也许你也错误地复制了std::vector,并看到了相同的行为,也许你没有。最后,这完全是我的错:)。