为什么我会得到“双重自由或腐败”?
我正在尝试序列化一个结构,但程序崩溃了:
*** 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;
}
I am trying to serialize a struct, but the program crashed with:
*** 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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我发现此代码有两个问题:
您通过将包含
std::string
的struct
的memcpy
转换为另一个来调用未定义的行为地点。如果您memcpy
一个不仅仅是纯结构的类(例如std::string
),它可能会导致各种问题。在这种特殊情况下,我认为部分问题可能是 std::string 有时存储一个指向包含字符串实际内容的字符缓冲区的内部指针。如果您memcpy
std::string
,您就绕过了字符串的普通复制构造函数,该构造函数会复制该字符串。相反,您现在有两个不同的std::string
实例共享一个指针,因此当它们被销毁时,它们都会尝试删除字符缓冲区,从而导致您看到的错误。除了不做你正在做的事情之外,没有简单的解决办法。这从根本上来说是不安全的。您正在使用
new[]
分配内存,但使用delete
删除它。您应该使用数组删除运算符delete[]
来删除此内存,因为对其使用常规delete
将导致未定义的行为,可能导致此崩溃。希望这有帮助!
I see two problems with this code:
You are invoking undefined behavior by
memcpy
ing astruct
containing astd::string
into another location. If youmemcpy
a class that isn't just a pure struct (for example, astd::string
), it can cause all sorts of problems. In this particular case, I think that part of the problem might be thatstd::string
sometimes stores an internal pointer to a buffer of characters containing the actual contents of the string. If youmemcpy
thestd::string
, you bypass the string's normal copy constructor that would duplicate the string. Instead, you now have two different instances ofstd::string
sharing a pointer, so when they are destroyed they will both try to delete the character buffer, causing the bug you're seeing. There is no easy fix for this other than to not do what you're doing. It's just fundamentally unsafe.You are allocating memory with
new[]
, but deleting it withdelete
. You should use the array deleting operatordelete[]
to delete this memory, since using regulardelete
on it will result in undefined behavior, potentially causing this crash.Hope this helps!
将
memcpy()
与std::string
类型的数据元素(或者实际上任何非 POD 数据类型)。std::string
类将实际字符串数据存储在动态分配的缓冲区中。当您memcpy()
周围的std::string
内容时,您正在删除内部分配的指针并最终访问已经释放的内存。您可以通过将声明更改为来使代码正常工作:
但是,这具有固定大小
name
缓冲区的缺点。如果您想维护动态大小的name
,那么您将需要一个更复杂的toArray
和fromArray
实现,这些实现不能直接执行记忆副本。It's not valid to use
memcpy()
with a data element of typestd::string
(or really, any non-POD data type). Thestd::string
class stores the actual string data in a dynamically-allocated buffer. When youmemcpy()
the contents of astd::string
around, you are obliterating the pointers allocated internally and end up accessing memory that has been already freed.You could make your code work by changing the declaration to:
However, that has the disadvantages of a fixed size
name
buffer. If you want to maintain a dynamically sizedname
, then you will need to have a more sophisticatedtoArray
andfromArray
implementation that doesn't do straight memory copies.您将在
toArray
调用中复制string
的内部缓冲区。当使用 fromArray 反序列化时,您在 dummy 中“创建”第二个字符串,它认为它拥有与 human 相同的缓冲区。You're copying the
string
's internal buffer in thetoArray
call. When deserializing withfromArray
you "create" a second string indummy
, which thinks it owns the same buffer ashuman
.std::string 可能包含一个指向包含字符串数据的缓冲区的指针。当您调用 toArray ( human) 时,您正在 memcpy() 处理 Dummy 类的字符串,包括指向字符串数据的指针。然后,当您通过直接 memcpy() 创建一个新的虚拟对象时,您就创建了一个新的字符串对象,该对象具有与第一个对象相同的指向字符串数据的指针。接下来你知道,dummy 被破坏,指针的副本被破坏,然后 human 被破坏,然后 BAM,你得到了双重释放。
一般来说,像这样使用 memcpy 复制对象会导致各种各样的问题,就像您所看到的那样。这可能只是冰山一角。相反,您可能会考虑为要序列化的每个类显式实现某种编组函数。
或者,您可以查看 C++ 的 json 库,它可以将事物序列化为方便的基于文本的格式。 JSON 协议通常与您想要序列化对象以通过套接字发送的自定义网络协议一起使用。
std::string probably contains a pointer to a buffer that contains the string data. When you call toArray (human), you're memcpy()'ing the Dummy class's string, including the pointer to the string's data. Then when you create a new Dummy object by memcpy()'ing directly into it, you've created a new string object with the same pointer to string data as the first object. Next thing you know, dummy gets destructed and the copy of the pointer gets destroyed, then human gets destructed and BAM, you got a double free.
Generally, copying objects using memcpy like this will lead to all sorts of problems, like the one you've seen. Its probably just going to be the tip of the ice berg. Instead, you might consider explicitly implementing some sort of marshalling function for each class you want to serialize.
Alternatively, you might look into json libraries for c++, which can serialize things into a convenient text based format. JSON protocols are commonly used with custom network protocols where you want to serialize objects to send over a socket.