c++ - 使用指向结构的指针序列化结构
问题描述
我正在尝试关注https://accu.org/index.php/journals/2317上写的一篇文章, 我发现这很有趣,因为我正在尝试深入了解一些管理工具的序列化/文件保存/加载我正在尝试。我将它标记为 c++,因为在我让它工作后我会把它包装在一个类上(希望在你的帮助下)。
现在,这是出于学习目的,因为我目前不打算使用 boost 或任何其他序列化库。我想知道或尝试找出这些东西是否有效。
目前,我必须做一些细微的修改才能工作,因为断言总是错误的,并且在尝试使用 memcpy 写入外部分配的内存时遇到问题。
现在,它可以编译,但是当我对结构进行序列化和反序列化时,数据是不一样的。
请查看并帮助我,或指出我正确的方向。在代码之后,我将尝试解释我是如何理解它的。
#include <iostream>
#include <cassert>
struct Y
{
int yy;
};
struct X
{
int xx;
struct Y* y = nullptr;
int z;
};
// Changed OutMemStram and InMemStream for IOMemStream, same data
struct IOMemStream
{
// changed from uint8_t* to char*
char* pp;
char* ppEnd;
};
// Output
inline void WriteToStream( IOMemStream* dst, void* p, size_t sz )
{
dst->pp = (char*)p; // original code doesn't contain this line
dst->ppEnd = (char*)p + sz; // original code doesn't contain this line
assert( (dst->pp + sz) <= dst->ppEnd );
memcpy( dst->pp, p, sz );
dst->pp += sz;
}
void SerializeX( IOMemStream* dst, X* x )
{
WriteToStream( dst, x, sizeof( X ) );
WriteToStream( dst, x->y, sizeof( Y ) );
}
// Input
inline void ReadFromStream( IOMemStream* src, void* p, size_t sz )
{
//assert( (src->pp + sz) <= src->ppEnd );
memcpy( p, src->pp, sz );
src->pp += sz;
}
void DeserializeX( IOMemStream* src, X* x )
{
ReadFromStream( src, x, sizeof( X ) );
// x->y contains garbage at this point(!)
// ok, not exactly garbage - but a pointer
// which is utterly invalid in our current space
x->y = new Y;
assert( x->y );
ReadFromStream( src, x->y, sizeof( Y ) );
}
// Usage sample
int main()
{
// Assume struct x was previously filled by other function
X x;
x.xx = 1000;
x.z = 2000;
x.y = new Y;
x.y->yy = 3000;
// IO buffer
IOMemStream ioms;
// Test for output
SerializeX( &ioms, &x );
// Test for input
X x1;
DeserializeX( &ioms, &x1 );
// x1.xx should be 1000 and x1.< should be 2000
std::cout << x1.xx << ", " << x1.z << std::endl;
delete x.y;
delete x1.y;
//delete ioms.pp; // gets exception
std::cin.get();
return 0;
}
这就是我理解(或不理解)的方式。
- 结构 X,包含 2 个整数和 1 个指向 Y 结构的结构指针,假设 int size = 4,那么 X 的大小将为 12 个字节。Y 它是 4 个字节。
- IOMemStream 包含 struct X 和 struct Y 的指针。
- IOMemStream->pp 和 IOMemStream->ppEnd 应该有 12 个字节和来自 struct X 和 X->Y 的 4 个字节。
- 函数 WriteToStream 将结构中的字节打包到 pp 和 ppEnd 指针,在分配 X 后,指针增加大小以准备 Y 结构。(在原始文章中,断言是在没有分配变量 pp 和 ppEnd 的情况下进行的
- IOMemStream) 函数 SerializeX 对 X 和 Y 结构都使用 WriteToStrea。
反序列化几乎相同,但顺序相反,ReadFromStream 将为结构 Y 分配内存。
我从这里迷路了,因为序列化和反序列化的值不一样。另外,我希望我能正确理解:D
先感谢您!
解决方案
免责声明:这只是一堆太长的评论,不能写成评论,而不是正确的答案。
事情不是那么简单,你有一些错误的假设。特别是,即使 anint
占用 4 个字节而指针占用 8 个字节,X
由于填充,您的结构也可以有超过 16 个字节。
尝试将下面的代码添加到您的 main 中,看看您会得到什么
X x;
std::cout << sizeof(x.xx) << std::endl;
std::cout << sizeof(x.y) << std::endl;
std::cout << sizeof(x.z) << std::endl;
std::cout << sizeof(x) << std::endl;
在我的系统中,每个整数得到 4 个字节,指针得到 8 个字节,但对于结构,我得到 24 个字节。原因是由于填充(有关更多信息,请参阅此问题)。请注意,您声明结构成员的顺序对填充有影响。相反,如果您声明
struct X {
struct Y* y = nullptr;
int xx;
int z;
};
or
```c++
struct X {
int xx;
int z;
struct Y* y = nullptr;
};
那么该结构将只需要 16 个字节(至少在我的系统中)。
另一个问题是序列化指针。显然,存储指针值用处不大。您将需要考虑如何处理 struct Y
。您必须Y
在反序列化期间创建一个类型的对象,然后将其地址放入反序列化的 structX
中,但细节可能会根据程序的其余部分而改变,这里我们只有示例代码。
最后,小心memcpy
,因为它只会保存字节(可能包括填充)并且它不关心字节顺序。
推荐阅读
- azure-devops - Azure DevOps 中针对用户故事的障碍
- spring - spring jpa 查询 SQLGrammarException on join @ManyToMany
- django - 从基础模型继承模型的 FK 关联查询优化
- c - 如何使用 c 程序在 linux 中获取磁盘使用情况?
- java - 在java 8中合并recusive Hashmap
- javascript - 有什么区别?异步与嵌套承诺语句
- azure-devops - 在 Azure DevOps (ADO) 中根据草稿拉取请求自动触发拉取请求构建
- dhtml - DHTMLX Scheduler Month 仅查看事件计数
- android - 如何更改抽屉菜单中的第一个片段
- python - Scikit-Learn 投票分类器预测器分数始终为 0