如果我将字节数组转换为 __attribute__((packed, aligned(2))) 结构会发生啥?

Posted

技术标签:

【中文标题】如果我将字节数组转换为 __attribute__((packed, aligned(2))) 结构会发生啥?【英文标题】:What will happen if I cast a byte array to an __attribute__((packed, aligned(2))) struct?如果我将字节数组转换为 __attribute__((packed, aligned(2))) 结构会发生什么? 【发布时间】: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 和这个链接:***.com/questions/58320316/stdbit-cast-with-stdarray。 (“重新解释”数据的“C”风格转换无论如何都是 UB) 除非询问两种语言之间的差异或交互,否则不要同时标记 C 和 C++。两种语言的别名规则不同,对其中一种语言的回答不会为正在寻找另一种语言信息的人提供服务。 【参考方案1】:
    避免指针双关语,因为它几乎总是违反严格的别名规则。 结构的对齐方式无关紧要,因为您的字节数组不必对齐 2 个字节。

使用memcpy

IcmpHdr header;
memcpy(&header, byte_array, sizeof(header));

如果您使用现代优化编译器,则不太可能调用 memcpy

https://godbolt.org/z/6P5M333dv

【讨论】:

@ColonelThirtyTwo 不,您可以通过 char 访问其他类型,但不能通过其他类型访问 char。这很好。你没看懂链接 @PepijnKramer 你在哪里看到我的代码中有任何强制转换?这条评论是关于什么的?

以上是关于如果我将字节数组转换为 __attribute__((packed, aligned(2))) 结构会发生啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何将字节数组转换为 boost::multiprecision::uint128_t?

__attribute__系列之aligned

Scala字节数组转换为数字

将 uint8_t 数组转换为 C 中的 uint16_t 值

GCC 的 __attribute__((__packed__)) 是不是保留原始顺序?

[C++11]_[初级]_[十六进制字符串转换为字节数组]