在32位和64位之间进行C++二进制写入/读取

5
如果您有一个二进制输出流,并在32位Windows计算机上向文件写入整数。那么您是否能够从64位Windows计算机上的同一文件中读取相同的整数?
我的猜测是不行。因为32位计算机上的整数是4个字节,而64位计算机上的整数是8个字节。
因此,以下代码能否正常工作,同时文件必须能够被64位和32位计算机读写,无论操作系统、计算机架构和数据类型如何。如果不能,那么该怎么做,同时文件必须以二进制形式存在。

写入

std::ofstream ofs("example.bin", std::ios::binary);

int i = 128;
ofs.write((char*) (&i), sizeof(i));

ofs.close();

阅读

std::ifstream ifs("example.bin", std::ios::binary);

int i = 0;
ifs.read((char*) (&i), sizeof(i));

ifs.close();

1
据我所知,在32位和64位Windows系统上,sizeof(int)都等于4。变化的是long和指针类型。 - Joe Z
@JoeZ 嗯,与 sizeof 有关的 这个 Stack Overflow 问题中的内容与我所看到的并不一样。 - vallentin
3
如果您正在使用C++11,只需要包含<cstdint>并使用正确的固定宽度整数类型(例如int32_t)。 - Zeta
有一个标准的解释和实现。通过在两个平台上打印sizeof(int)来检查实现要比在互联网上查找答案快得多。如果您打印sizeof(int),您会发现,正如JoeZ所说,从VS8到VS12,在两个平台上都是4。 - cup
Vallentin:从16位MS-DOS到32位确实会对“int”大小产生影响。但是从32位到64位则没有。正如@Zeta所说,您可以使用固定宽度类型来使代码中的宽度假设更清晰。另外,需要注意的另一件事是,由于不同的对齐规则,32位和64位结构布局可能会有所不同。所以要小心。 - Joe Z
2个回答

5

虽然在现代平台(32位和64位)上,int通常都是4字节,但其大小没有保证。因此,在将数据序列化到文件或其他二进制流中时,您应该优先选择头文件<cstdint>中引入的固定宽度整数类型。这些类型在C++11中引入(一些编译器支持C++03),请注意不要删除HTML标签。

#include <cstdint>

...
int32_t i = 128;
ofs.write((char*)(&i), sizeof(i));
...

另一种选择是强制某种类型具有特定的大小,例如将 int 设置为大小为 4。要确保程序在这不成立时无法编译,请使用 static_assert

...
int i = 128;
static_assert(sizeof(i) == 4, "Field i has to have size 4.");
ofs.write((char*)(&i), sizeof(i));
...

虽然我们已经有固定宽度的整数,但考虑到内容涉及到存储一整个结构体,可能会有用处。例如,从glm中读取的vec4按照文档应该包含四个浮点数,所以在将这个结构体序列化时,最好静态地检查这一点,以便捕获未来库变更(虽然不太可能,但仍有可能)。
另一个非常重要的问题是整型的字节顺序,在不同平台之间可能会有所不同。现代x86桌面平台上的大多数编译器使用小端字节序表示整型,因此我建议您在二进制文件格式中使用小端字节序;但如果平台使用大端字节序,则需要进行转换(即反转字节顺序)。

你如何控制流的字节序? - vallentin
1
你的代码部分考虑了字节序。在你发布的代码片段中,你将整数指针解释为字符指针,然后根据编译器/平台布局整数类型读取一系列字节。因此,关键是检测平台的字节序(我不知道如何做),并定义文件格式的字节序(可以选择任意一个;我建议使用小端序),如果它们不匹配,则使用辅助函数反转字节。这看起来类似于 char b[4]; b[3] = *p++; b[2] = *p++; b[1] = *p++; b[0] = *p; ofs.write(b, 4); - leemes
请看这里:https://dev59.com/PW855IYBdhLWcg3wnFxO - leemes
@Vallentin:一个好的想法是不这样做。给定 int32_t i,只需编写字符 i & 0xFF(i /256) & 0xFF(i/65536) & 0xFF(i/16777216) & 0xFF - MSalters
@MSalters 为了更好的可读性,我会使用位移而不是除法,尤其是当我们将它扩展到 int64_t ;) - leemes
显示剩余3条评论

1
在C++中,int的大小没有保证。你只知道它至少与short int一样大,但不会超过long int。编译器可以在这些限制内选择适当的大小。虽然大多数编译器会选择32位作为int的大小,但有些编译器不会。如果你知道你的类型始终是32位,则可以使用int32_t类型。
include <stdint.h>

获取这种类型。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接