为什么我会“双重免费或腐败”?

时间:2012-02-08 03:23:02

标签: c++ serialization memory-leaks runtime-error

我正在尝试序列化一个结构,但程序崩溃了:

*** glibc detected *** ./unserialization: double free or corruption (fasttop): 0x0000000000cf8010 ***

#include <iostream>
#include <cstdlib>
#include <cstring>

struct Dummy
{
    std::string name;
    double height;
};

template<typename T>
class Serialization
{
    public:
        static unsigned char* toArray (T & t)
        {
            unsigned char *buffer = new unsigned char [ sizeof (T) ];
            memcpy ( buffer , &t , sizeof (T) );
            return buffer;
        };

        static T fromArray ( unsigned char *buffer )
        {
            T t;
            memcpy ( &t , buffer , sizeof (T) );
            return t;
        };
};

int main ( int argc , char **argv ) 
{
    Dummy human;
    human.name = "Someone";
    human.height = 11.333;

    unsigned char *buffer = Serialization<Dummy>::toArray (human);

    Dummy dummy = Serialization<Dummy>::fromArray (buffer);

    std::cout << "Name:" << dummy.name << "\n" << "Height:" << dummy.height << std::endl;

    delete buffer;

    return 0;
}

4 个答案:

答案 0 :(得分:4)

我发现此代码有两个问题:

  1. 您正在通过memcpy struct包含std::string到另一个位置来调用未定义的行为。如果你memcpy一个不仅仅是纯结构的类(例如,std::string),它可能会导致各种各样的问题。在这种特殊情况下,我认为问题的一部分可能是std::string有时会存储一个指向包含字符串实际内容的字符缓冲区的内部指针。如果您memcpy std::string,则绕过将复制字符串的字符串的普通复制构造函数。相反,您现在有两个不同的std::string实例共享一个指针,所以当它们被销毁时,它们都会尝试删除字符缓冲区,从而导致您看到的错误。除了不做你正在做的事情之外,没有其他简单的解决办法。这根本就是不安全的。

  2. 您使用new[]分配内存,但使用delete删除内存。您应该使用数组删除运算符delete[]来删除此内存,因为在其上使用常规delete将导致未定义的行为,可能导致此崩溃。

  3. 希望这有帮助!

答案 1 :(得分:2)

memcpy()std::string类型的数据元素(或者实际上,任何非POD数据类型)一起使用是无效的。 std::string类将实际字符串数据存储在动态分配的缓冲区中。当您memcpy()周围的std::string内容时,您将删除内部分配的指针并最终访问已释放的内存。

您可以通过将声明更改为:

来使代码正常工作
struct Dummy
{
    char name[100];
    double height;
};

但是,它具有固定大小name缓冲区的缺点。如果您想维护动态调整大小的name,那么您需要具有更复杂的toArrayfromArray实现,而不执行直接内存复制。

答案 2 :(得分:0)

您正在string来电中复制toArray的内部缓冲区。使用fromArray进行反序列化时,您在dummy中“创建”第二个字符串,认为它拥有与human相同的缓冲区。

答案 3 :(得分:0)

std :: string可能包含一个指针到包含字符串数据的缓冲区。当你调用theArray(human)时,你是memcpy()'的Dummy类的字符串,包括指向字符串数据的指针。然后当你通过memcpy()直接创建一个新的Dummy对象时,你已经创建了一个新的字符串对象,其字符串数据的指针与第一个对象相同。接下来你知道,虚拟被破坏,指针的副本被破坏,然后人类被破坏,BAM,你有一个双重自由。

通常,使用像这样的memcpy复制对象会导致各种各样的问题,比如你所见过的问题。它可能只是冰山一角。相反,您可以考虑为要序列化的每个类显式实现某种编组函数。

或者,您可以查看c ++的json库,它可以将事物序列化为方便的基于文本的格式。 JSON协议通常与自定义网络协议一起使用,您希望序列化对象以通过套接字发送。