C++ 为什么使用reinterpret_cast将char*转换为结构似乎可以正常工作?
人们说,将原始数据(如C++ 为什么使用reinterpret_cast将char*转换为结构似乎可以正常工作?,c++,struct,reinterpret-cast,C++,Struct,Reinterpret Cast,人们说,将原始数据(如char*)转换为结构时,信任reinterpret\u cast是不好的。例如,对于结构 struct A { unsigned int a; unsigned int b; unsigned char c; unsigned int d; }; sizeof(A)=16和\u alignof(A)=4,与预期完全一致 假设我这样做: char *data = new char[sizeof(A) + 1]; A *ptr = reint
char*
)转换为结构时,信任reinterpret\u cast
是不好的。例如,对于结构
struct A
{
unsigned int a;
unsigned int b;
unsigned char c;
unsigned int d;
};
sizeof(A)=16
和\u alignof(A)=4
,与预期完全一致
假设我这样做:
char *data = new char[sizeof(A) + 1];
A *ptr = reinterpret_cast<A*>(data + 1); // +1 is to ensure it doesn't points to 4-byte aligned data
然后ptr->a
是1,ptr->b
是2,ptr->c
是3,ptr->d
是4。好的,似乎有用。正是我所期待的
但是
ptr
所指向的数据并不像A
所应该的那样是4字节对齐的。这在x86或x64平台中可能会导致哪些问题?性能问题?首先,初始化字符串假定基础整数以小端格式存储。但另一种架构可能使用big-endian,在这种情况下,字符串将产生垃圾。(一些巨大的数字)该体系结构的正确字符串是
“\x00\x00\x00\x01\x00\x00\x00\x00\x02\x03\x00\x00\x00\x00\x00\x00\x00\x04”
当然,还有对齐的问题
某些体系结构甚至不允许将数据+1的地址分配给非字符指针,它们将发出内存对齐陷阱
但是,即使是允许这样做的架构(如x86)也会执行得很糟糕,必须对结构中的每个整数执行两次内存访问。(有关更多信息,请参阅此优秀答案:
)
<>最后,我对这一点还不完全确定,但是我认为C和C++甚至不能保证一个字符数组包含字节填充的字符。(我希望了解更多的人能够澄清这一点。)可以想象,有些体系结构完全无法处理非单词对齐的数据,因此在这种体系结构中,每个字符都必须占据整个单词。这意味着采用
data+1
的地址是有效的,因为它仍然是对齐的,但是初始化字符串不适合预期的作业,因为它的前4个字符将覆盖整个结构,产生a=1,b=0,c=0和d=0。问题在于,您无法确定此代码是否将在另一个平台上运行,以及下一版本的Visual Studio等。在另一个处理器上运行时,可能会导致硬件异常
曾经有一段时间,你可以读取任意的内存位置,但现在所有这些程序都会因“访问冲突”异常而崩溃。类似的事情将来可能会发生在这个项目上
但是,您能做的,以及任何自称“符合C++标准”的编译器必须正确编译的是:您可以
重新解释\u cast
指向其他对象的指针,然后返回原始类型。在前后读取时,类型的值必须保持不变
我不知道你到底想做什么,但你可能会侥幸逃脱,例如
- 分配
结构a
将其转换为重新解释\u cast
schar
- 将内存内容保存到文件中
- 分配
结构a
将其重新解释为
schar
- 将内容加载到内存中
将其返回到结构a重新解释\u cast
“\x00\x00\x00\x01\x00\x00\x00\x00\x02\x03\x00\x00\x00\x00\x00\x04”
,因为第三个变量是字符。是的,我知道。我想知道的是,不对齐的数据是否会导致一些问题。谢谢。这就是我想知道的。据我所知,读取未对齐的连续数据会使您多读取一到两次内存和大量移位。我很肯定你不需要每个字都读两遍。@Alegnem你说得对,我的措辞很糟糕。我写的是“执行内存访问操作”,而我的意思是“执行内存访问”。当然,是CPU做的,而不是程序员。我修正了它。IIRC“byte”在C/C++中的意思是“最小的可寻址单元”,不必是8位。谢谢。如今,对于x86或x64处理器,访问未对齐的数据是否存在问题?(mis)对齐只会降低性能(在x86/64上)。真正的问题是不同CPU架构的内存访问模式和具体内存模型可能不支持您的想法,然后我认为这没关系,因为我的代码不应该在x86或更高版本的x64之外的任何其他处理器体系结构中运行。这正是我遇到这个疑问时想要做的:尝试从文件中读取结构。但是当还原时,我没有得到“reinterpret\u cast
it tochar
s”。@LHLaurini不需要从char*
转换回A*
。(1) 除非char*
是从对齐的A
中投射的,否则这是UB。(2) 在这里,它是,但它是毫无意义的!将文件加载到a
的方法-如果它是可复制的,这很重要-是分配a
&将文件作为一系列char
s复制到它上面-通过读取reinterpret\u cast(&a)
或一个单独的缓冲区,然后memcpy
,等等(标准保证TC类型可在char
buffers之间序列化)。然后阅读您已经拥有的a
无需回溯。在适当的情况下,可以将副本优化为未对齐的强制转换
memcpy_s(sh, sizeof(A),
"\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00", sizeof(A));