在阅读了以下1和2 Q/As 并在具有 GCC 和 MSVC 的 x86 架构上使用了下面讨论的技术多年并且没有看到问题之后,我现在对什么应该是正确的感到非常困惑,但是也是使用 C++ 序列化然后反序列化二进制数据的重要“最有效”方式。
鉴于以下“错误”代码:
int main()
{
std::ifstream strm("file.bin");
char buffer[sizeof(int)] = {0};
strm.read(buffer,sizeof(int));
int i = 0;
// Experts seem to think doing the following is bad and
// could crash entirely when run on ARM processors:
i = reinterpret_cast<int*>(buffer);
return 0;
}
现在,据我了解,重新解释强制转换向编译器表明它可以将缓冲区中的内存视为整数,然后可以自由发出整数兼容指令,这些指令要求/假设相关数据的某些对齐 - 唯一的开销是当 CPU 检测到它试图执行面向对齐的指令的地址时,额外的读取和移位实际上没有对齐。
也就是说,就 C++ 而言,上面提供的答案似乎表明这都是未定义的行为。
假设缓冲区中将发生强制转换的位置的对齐方式不符合要求,那么解决这个问题的唯一方法是逐个复制字节,这是真的吗?是否有更有效的技术?
此外,多年来我见过许多完全由 pod 组成的结构(使用编译器特定的编译指示来删除填充)被转换为 char* 并随后写入文件或套接字,然后再读回缓冲区的情况并且缓冲区转换回原始结构的指针(忽略机器之间潜在的字节序和浮点/双精度格式问题),这种代码是否也被视为未定义行为?
下面是更复杂的例子:
int main()
{
std::ifstream strm("file.bin");
char buffer[1000] = {0};
const std::size_t size = sizeof(int) + sizeof(short) + sizeof(float) + sizeof(double);
const std::size_t weird_offset = 3;
buffer += weird_offset;
strm.read(buffer,size);
int i = 0;
short s = 0;
float f = 0.0f;
double d = 0.0;
// Experts seem to think doing the following is bad and
// could crash entirely when run on ARM processors:
i = reinterpret_cast<int*>(buffer);
buffer += sizeof(int);
s = reinterpret_cast<short*>(buffer);
buffer += sizeof(short);
f = reinterpret_cast<float*>(buffer);
buffer += sizeof(float);
d = reinterpret_cast<double*>(buffer);
buffer += sizeof(double);
return 0;
}