为什么使用reinterpret_cast 将char* 转换为结构似乎可以正常工作?
Why does using reinterpret_cast to convert from char* to a structure seem to work normally?
人们说相信 reinterpret_cast
从原始数据(如 char*
)转换为结构是不好的。例如,对于结构
struct A
{
unsigned int a;
unsigned int b;
unsigned char c;
unsigned int d;
};
sizeof(A) = 16
和 __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
:
memcpy_s(sh, sizeof(A),
"\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00", sizeof(A));
则ptr->a
为1,ptr->b
为2,ptr->c
为3,ptr->d
为4。
好吧,似乎工作。正是我所期待的。
但是 ptr
指向的数据不是像 A
那样的 4 字节对齐。这可能会在 x86 或 x64 平台中导致什么问题?性能问题?
一方面,您的初始化字符串假定基础整数以小端格式存储。但是另一种体系结构可能使用大端,在这种情况下,您的字符串将产生垃圾。 (一些巨大的数字。)该架构的正确字符串是
"\x00\x00\x00\x01\x00\x00\x00\x02\x03\x00\x00\x00\x00\x00\x00\x04".
当然还有对齐的问题。
某些架构甚至不允许您将 data + 1 的地址分配给非字符指针,它们会发出 内存对齐陷阱 .
但即使是允许这样做的架构(如 x86)也会表现得很糟糕,必须为结构中的每个整数执行两次内存访问。 (有关更多信息,请参阅这个出色的答案:
)
最后,我对此并不完全确定,但我认为 C 和 C++ 甚至不能向您保证字符数组将包含以字节为单位的字符。 (我希望了解更多的人可以澄清这一点。)可以想象,可能存在完全无法处理非字对齐数据的体系结构,因此在此类体系结构中,每个字符都必须占据整个字。这意味着获取 data + 1
的地址是有效的,因为它仍然是对齐的,但是您的初始化字符串不适合预期的工作,因为它的前 4 个字符将覆盖您的整个结构, 产生 a=1, b=0, c=0 和 d=0.
问题是您无法确定此代码是否会 运行 在另一个平台上,下一个版本 Visual Studio 等。当 运行 在另一个处理器上运行时,可能会导致硬件异常。
曾几何时,您可以读取任意内存位置,但如今所有这些程序都因 "access violation" 异常而崩溃。该程序将来可能会发生类似的事情。
但是,您可以做什么,以及任何自称 "C++ standard compliant" 的编译器必须正确编译的是:
你可以 reinterpret_cast
指向其他东西的指针,然后返回到原来的类型。类型的值在前后读取时必须保持相同。
我不知道你到底想做什么,但是你可能逃脱,例如
- 分配一个
struct A
reinterpret_cast
将其转换为 char
s
- 将内存内容保存到文件
稍后恢复所有内容:
- 分配一个
struct A
reinterpret_cast
到 char
s
- 加载内容到内存
reinterpret_cast
它回一个struct A
人们说相信 reinterpret_cast
从原始数据(如 char*
)转换为结构是不好的。例如,对于结构
struct A
{
unsigned int a;
unsigned int b;
unsigned char c;
unsigned int d;
};
sizeof(A) = 16
和 __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
:
memcpy_s(sh, sizeof(A),
"\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00", sizeof(A));
则ptr->a
为1,ptr->b
为2,ptr->c
为3,ptr->d
为4。
好吧,似乎工作。正是我所期待的。
但是 ptr
指向的数据不是像 A
那样的 4 字节对齐。这可能会在 x86 或 x64 平台中导致什么问题?性能问题?
一方面,您的初始化字符串假定基础整数以小端格式存储。但是另一种体系结构可能使用大端,在这种情况下,您的字符串将产生垃圾。 (一些巨大的数字。)该架构的正确字符串是
"\x00\x00\x00\x01\x00\x00\x00\x02\x03\x00\x00\x00\x00\x00\x00\x04".
当然还有对齐的问题。
某些架构甚至不允许您将 data + 1 的地址分配给非字符指针,它们会发出 内存对齐陷阱 .
但即使是允许这样做的架构(如 x86)也会表现得很糟糕,必须为结构中的每个整数执行两次内存访问。 (有关更多信息,请参阅这个出色的答案: )
最后,我对此并不完全确定,但我认为 C 和 C++ 甚至不能向您保证字符数组将包含以字节为单位的字符。 (我希望了解更多的人可以澄清这一点。)可以想象,可能存在完全无法处理非字对齐数据的体系结构,因此在此类体系结构中,每个字符都必须占据整个字。这意味着获取 data + 1
的地址是有效的,因为它仍然是对齐的,但是您的初始化字符串不适合预期的工作,因为它的前 4 个字符将覆盖您的整个结构, 产生 a=1, b=0, c=0 和 d=0.
问题是您无法确定此代码是否会 运行 在另一个平台上,下一个版本 Visual Studio 等。当 运行 在另一个处理器上运行时,可能会导致硬件异常。
曾几何时,您可以读取任意内存位置,但如今所有这些程序都因 "access violation" 异常而崩溃。该程序将来可能会发生类似的事情。
但是,您可以做什么,以及任何自称 "C++ standard compliant" 的编译器必须正确编译的是:
你可以 reinterpret_cast
指向其他东西的指针,然后返回到原来的类型。类型的值在前后读取时必须保持相同。
我不知道你到底想做什么,但是你可能逃脱,例如
- 分配一个
struct A
reinterpret_cast
将其转换为char
s- 将内存内容保存到文件
稍后恢复所有内容:
- 分配一个
struct A
reinterpret_cast
到char
s- 加载内容到内存
reinterpret_cast
它回一个struct A