在结构体内部将数组切换为向量,如何处理文件io字节对齐
我们曾经有一个结构:
const int NUMBER_OF_ELEMENTS = 10;
struct myStruct1
{
uint32_t var1;
uint64_t var2;
uint32_t elements[NUMBER_OF_ELEMENTS];
};
但是,展望未来,我们希望元素的数量是可变的。我想我最好这样做:
struct myStruct2
{
uint32_t var1;
uint64_t var2;
std::vector<uint32_t> elements;
myStruct2(int len){ elements.resize(len);};
};
对于从文件读取/写入,我们过去只是这样做:
myStruct1 ms1;
std::ofstream outfile(FILENAME,std::ofstream::out | std::ofstream::binary);
outfile.write((const char*)&ms1,sizeof(myStruct1));
outfile.close();
myStruct1 msread1;
std::ifstream infile(FILENAME, std::ifstream::in | std::ifstream::binary);
infile.read((char *)&msread1, sizeof(myStruct1));
infile.close();
显然我不能再为矢量版本这样做了。所以,我必须逐个元素地阅读。
myStruct2 msread2(NUMBER_OF_ELEMENTS);
std::ifstream infile(FILENAME, std::ifstream::in | std::ifstream::binary);
infile.read((char *)&msread2.var1, sizeof(uint32_t));
infile.read((char *)&msread2.var2, sizeof(uint64_t));
for (int i=0; i<NUMBER_OF_ELEMENTS; i++)
{
infile.read((char *)&msread2.elements[i], sizeof(uint32_t));
}
infile.close();
然而,这会遇到字节对齐的问题...结构体(以及文件中)的 var1 之后有 4 个字节的填充。先读取 var1,然后读取 var2 不会跳过此 pad。
我可以使用 #pragma pack(1) 进行写作和阅读。不过,我希望读者能够与使用填充创建的旧文件兼容。
读取 var1 后,我可以手动查找 4 个字节(或读取虚拟 4 字节变量)。但我觉得可能有更好的方法。
我可以将 var1 和 var2 放在 myStruct1 或 myStruct2 中它们自己的结构中,然后一起读取它们,也许这对于 io 来说更干净一些,但访问它们会需要一个额外的步骤,例如 ms1.headvars.var1 而不仅仅是 ms1.var1。 (整个代码库中有更多更改)
有关更好的解决方案的建议吗?
We used to have a structure:
const int NUMBER_OF_ELEMENTS = 10;
struct myStruct1
{
uint32_t var1;
uint64_t var2;
uint32_t elements[NUMBER_OF_ELEMENTS];
};
However, going forward we want the number of elements to be variable. I think I would best do this as:
struct myStruct2
{
uint32_t var1;
uint64_t var2;
std::vector<uint32_t> elements;
myStruct2(int len){ elements.resize(len);};
};
For reading/writing from a file, we used to simply do:
myStruct1 ms1;
std::ofstream outfile(FILENAME,std::ofstream::out | std::ofstream::binary);
outfile.write((const char*)&ms1,sizeof(myStruct1));
outfile.close();
myStruct1 msread1;
std::ifstream infile(FILENAME, std::ifstream::in | std::ifstream::binary);
infile.read((char *)&msread1, sizeof(myStruct1));
infile.close();
Obviously I can't do that anymore for the vector version. So, I would have to read element by element.
myStruct2 msread2(NUMBER_OF_ELEMENTS);
std::ifstream infile(FILENAME, std::ifstream::in | std::ifstream::binary);
infile.read((char *)&msread2.var1, sizeof(uint32_t));
infile.read((char *)&msread2.var2, sizeof(uint64_t));
for (int i=0; i<NUMBER_OF_ELEMENTS; i++)
{
infile.read((char *)&msread2.elements[i], sizeof(uint32_t));
}
infile.close();
However, this runs into the problem of byte alignment... there is 4 bytes of padding after var1 in the struct (and in the file). Reading var1 then var2 doesn't skip this pad.
I could use #pragma pack(1) both for writing and for reading. However I would like the reader to be compatible with old files which were created with the padding.
I could manually have a seek of 4 bytes (or read a dummy 4 byte variable) after reading var1. But I feel there's probably better ways.
I could put var1 and var2 in their own struct within myStruct1 or myStruct2, and read them together, maybe this is a bit cleaner for io but then accessing them would have an extra step eg ms1.headvars.var1 instead of just ms1.var1. (more changes throughout the codebase)
Any recommendations on a nicer solution?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您不能简单地将矢量二进制文件写入磁盘;它在内部包含已使用的大小以及指向动态分配的实际数据的指针(以及编译器制造商喜欢放在那里的任何其他内容;以任何顺序)。您最多只能得到写入文件的指针值。
相反,您需要编写大小,然后分两步编写其内容:
v.size()
和v.data()
。读回它们时,首先读取大小,准备一个向量,然后读回数据:
v.resize(size);
,然后读入v.data() [是的,你可以写入
v.data()
!]You cannot simply write a vector binary to disk; it contains internally the size, used, and a pointer to the real data which is dynamically allocated (and whatever else the compiler-maker enjoyed to put there; in any sequence). You would get at best the pointer value written into your file.
Instead, you need to write the size, and then its content in two steps:
v.size()
, andv.data()
.When reading them back, you read first the size, prepare a vector, and then read the data back:
v.resize(size);
, then read intov.data()
[yes, you can write intov.data()
!]