【发布时间】:2022-01-09 23:57:23
【问题描述】:
我有一些定义结构的 c++ 代码:
struct IcmpHdr
{
uint8_t m_type;
uint8_t m_code;
uint16_t m_chksum;
uint16_t m_id;
uint16_t m_seq;
} __attribute__((packed, aligned(2)))
我知道这个结构在分配时总是会在一个可被 2 整除的地址上对齐,因为如果需要,会在结构前面添加一个填充字节。
此结构在通过线路在接收端解包之前被强制转换为字节数组。现在,如果我将字节存储在数组char byte_array[8];
然后最终将其转换为指向我的类型的指针?
IcmpHdr* header = (IcmpHdr*)byte_array;
结构是否有 50/50 的机会错位?取消引用成员时这会导致未定义的行为吗?其他问题?
我知道我可以将数组对齐在 2 字节边界上,以避免甚至不必考虑这一点。好奇是我问的主要原因。
【问题讨论】:
-
您可能会出现未对齐的情况并且有 100% 的可能性出现严格的别名违规,从而导致未定义的行为。
-
不要,只是不要。当有一种受支持的方式来编写好代码时,没有理由尝试让坏代码工作。当接收到这样的缓冲区时,定义一个
IcmpHdr对象并将数据读入其中(将其作为缓冲区传递给将接收到的数据写入缓冲区的网络调用)。如果您正在读取数据包并且在开始检查它之前不知道它是哪种类型,您可以使用各种数据包类型的并集。另一种选择是读入字符缓冲区,然后将memcpy读入适当的IcmpHdr对象。 -
Andrew Henle 所暗示的别名规则基本上是:不要就对象的类型对编译器撒谎。现代编译器根据对象类型对代码做出各种假设。即使指针根据
IcmpHdr的需要对齐,使用IcmpHdr类型的左值访问char的数组也可能导致编译器生成的代码无法执行您想要的操作。 -
看看 std::bit_cast 和这个链接:stackoverflow.com/questions/58320316/stdbit-cast-with-stdarray。 (“重新解释”数据的“C”风格转换无论如何都是 UB)
-
除非询问两种语言之间的差异或交互,否则不要同时标记 C 和 C++。两种语言的别名规则不同,对其中一种语言的回答不会为正在寻找另一种语言信息的人提供服务。
标签: c++ arrays struct memory-alignment