【发布时间】:2011-04-25 16:42:54
【问题描述】:
使用 memcpy 序列化结构对象是邪恶的吗?
在我的一个项目中,我正在执行以下操作:我 memcpy 一个结构对象,base64 对其进行编码,并将其写入文件。我在解析数据时做相反的事情。它似乎可以正常工作,但在某些情况下(例如,当使用WINDOWPLACEMENT 作为 Windows Media Player 的 HWND 时),解码后的数据与sizeof(WINDOWPLACEMENT) 不匹配。
以下是一些代码片段:
// Using WINDOWPLACEMENT from Windows API headers:
typedef struct tagWINDOWPLACEMENT {
UINT length;
UINT flags;
UINT showCmd;
POINT ptMinPosition;
POINT ptMaxPosition;
RECT rcNormalPosition;
#ifdef _MAC
RECT rcDevice;
#endif
} WINDOWPLACEMENT;
static std::string EncodeWindowPlacement(const WINDOWPLACEMENT & inWindowPlacement)
{
std::stringstream ss;
{
Poco::Base64Encoder encoder(ss); // From the Poco C++ libraries
const char * offset = reinterpret_cast<const char*>(&inWindowPlacement);
std::vector<char> buffer(offset, offset + sizeof(inWindowPlacement));
for (size_t idx = 0; idx != buffer.size(); ++idx)
{
encoder << buffer[idx];
}
encoder.close();
}
return ss.str();
}
static WINDOWPLACEMENT DecodeWindowPlacement(const std::string & inEncoded)
{
std::string decodedString;
{
std::istringstream istr(inEncoded);
Poco::Base64Decoder decoder(istr); // From the Poco C++ libraries
decoder >> decodedString;
assert(decoder.eof());
if (decoder.fail())
{
throw std::runtime_error("Failed to parse Window placement data from the configuration file.");
}
}
if (decodedString.size() != sizeof(WINDOWPLACEMENT))
{
// !! Occurs frequently !!
throw std::runtime_error("Errors occured during parsing of the Window placement.");
}
WINDOWPLACEMENT windowPlacement;
memcpy(&windowPlacement, &decodedString[0], decodedString.size());
return windowPlacement;
}
我知道在 C++ 中使用 memcpy 复制类可能会导致麻烦,因为复制构造函数没有正确执行。我不确定这是否也适用于 C 风格的结构。或者内存转储的序列化只是没有完成?
更新: Poco 的 Base64Encoder/Decoder 中的错误并非不可能,但不太可能。它的测试用例看起来相当彻底:Base64Test.cpp。
【问题讨论】:
-
memcpy 对类造成问题的可能性与对结构的影响相同。除非您考虑到大多数人将结构用作 POD 类型,而将类用作更复杂类型的事实。 C++ 中的结构也使用复制构造函数。并且默认的复制构造函数相当于一个 memcpy,至少对于 POD 类型来说是这样。
-
关于 Hans 观点,在结构定义中告诉编译器明确的“打包”指令并使用固定大小的字段是明智的。如果您不能这样做,那么另一种选择是将大小字段作为结构的第一个成员。大小字段可以用作版本,可以在加载时进行比较,如果不匹配,将来的版本可以进行调整。
标签: c++ c serialization