Почему я получаю "двойную свободу или коррупцию"? - PullRequest
2 голосов
/ 08 февраля 2012

Я пытаюсь сериализовать структуру, но программа завершилась с:

*** 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 ]

4 голосов
/ 08 февраля 2012

Я вижу две проблемы с этим кодом:

  1. Вы вызываете неопределенное поведение, memcpy вставляя struct, содержащий std::string, в другое место.Если вы memcpy класс, который не является просто структурой (например, std::string), это может вызвать всевозможные проблемы.В этом конкретном случае, я думаю, что частью проблемы может быть то, что std::string иногда хранит внутренний указатель на буфер символов, содержащий фактическое содержимое строки.Если вы memcpy std::string, вы обойдете конструктор обычной копии строки, который продублирует строку.Вместо этого теперь у вас есть два разных экземпляра std::string, совместно использующих указатель, поэтому, когда они будут уничтожены, они оба попытаются удалить буфер символов, вызывая ошибку, которую вы видите.Это не легко исправить, кроме как не делать то, что вы делаете.Это просто принципиально небезопасно.

  2. Вы выделяете память с помощью new[], но удаляете ее с помощью delete.Вы должны использовать оператор удаления массива delete[], чтобы удалить эту память, так как регулярное использование delete приведет к неопределенному поведению, потенциально вызывающему этот сбой.

Надеюсь, это поможет!

2 голосов
/ 08 февраля 2012

Недопустимо использовать memcpy() с элементом данных типа std::string (или на самом деле любым типом данных, отличным от POD ). Класс std::string хранит фактические строковые данные в динамически распределенном буфере. Когда вы memcpy() содержимое std::string вокруг, вы стираете указатели, выделенные внутри, и в итоге получаете доступ к уже освобожденной памяти.

Вы можете заставить свой код работать, изменив объявление на:

struct Dummy
{
    char name[100];
    double height;
};

Однако, у этого есть недостатки фиксированного размера name буфера. Если вы хотите поддерживать динамический размер name, вам понадобится более сложная реализация toArray и fromArray, которая не делает прямые копии памяти.

0 голосов
/ 08 февраля 2012

std :: string, вероятно, содержит указатель на буфер, содержащий строковые данные. Когда вы вызываете toArray (human), вы выполняете memcpy () для строки класса Dummy, включая указатель на данные строки. Затем, когда вы создаете новый объект Dummy с помощью memcpy (), прямо в него, вы создаете новый строковый объект с тем же указателем на строковые данные, что и у первого объекта. Следующее, что вы знаете, манекен разрушается, а копия указателя уничтожается, затем человек разрушается и БАМ, вы получаете двойное освобождение.

Обычно копирование объектов с использованием memcpy приводит к возникновению всевозможных проблем, например, той, которую вы видели. Вероятно, это будет лишь верхушка айсберга Вместо этого вы могли бы рассмотреть возможность явной реализации какой-либо функции сортировки для каждого класса, который вы хотите сериализовать.

В качестве альтернативы вы можете обратиться к библиотекам json для c ++, которые могут сериализовать вещи в удобный текстовый формат. Протоколы JSON обычно используются с пользовательскими сетевыми протоколами, где вы хотите сериализовать объекты для отправки через сокет.

0 голосов
/ 08 февраля 2012

Вы копируете внутренний буфер string в вызове toArray. При десериализации с помощью fromArray вы «создаете» вторую строку в dummy, которая считает, что ей принадлежит тот же буфер, что и human.

...