uint32_t 和 uint8_t[4] 的 C 联合是否会始终以相同的方式映射到小端架构?

Will a C union of uint32_t and uint8_t[4] will always map the same way on little endian architectures?

uint32_t 和 uint8_t[4] 的 C 联合会在小端架构上始终以相同的方式映射吗?

例如与

union {
    uint32_t double_word;
    uint8_t octets[4];
} u;

u.double_word = 0x12345678;

总是导致:

u.octets[0] == 0x78
u.octets[1] == 0x56
u.octets[2] == 0x34
u.octets[3] == 0x12

或者这是未定义的行为?

在实际上 具有 这两种类型的平台上,C11 §7.20.1.1 p2 为您提供所有需要的保证(给定你知道字节序):

The typedef name uintN_t designates an unsigned integer type with width N and no padding bits. Thus, uint24_t denotes such an unsigned integer type with a width of exactly 24 bits.

这就足够了,因为没有少于 8 位的字节,所以 uint8_t 自动可用意味着一个字节恰好有 8 位。

TL;DR: 是的,代码没问题。

如前所述,它包含 implementation-defined 取决于字节顺序的行为,但除此之外,行为是 well-defined 并且代码是可移植的(在小端机器之间)。


详细答案:

有一点很重要,保证数组的分配顺序,C11 6.2.5/20:

An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type.

这意味着 4 uint8_t 的数组保证遵循 uint32_t 的分配顺序,这在小端系统上意味着最低有效字节在前。

理论上,编译器可以在联合 (C11 6.7.2.1/17) 的末尾随意插入任何填充,但这不应影响数据表示。如果您想迂腐地防止这种情况 - 或者更相关地,您希望防止以后添加更多成员时出现问题 - 您可以添加 compile-time assert:

typedef union {
    uint32_t double_word;
    uint8_t octets[4];
} u;

_Static_assert(sizeof(u) == sizeof(uint32_t), "union u: Padding detected");

至于 uintn_t 类型的表示,它保证是 2 的补码(在有符号类型的情况下),没有填充位 (C11 7.20.1.1)。

最后,关于 "type punning" 通过联合是否允许或未定义行为的问题,这在 C11 6.5.2.3 中指定得有点模糊:

A postfix expression followed by the . operator and an identifier designates a member of a structure or union object. The value is that of the named member,95) and is an lvalue if the first expression is an lvalue.

(non-normative) 注释 95 提供了说明:

If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called ‘‘type punning’’). This might be a trap representation.

并且由于我们已经排除了填充位,陷阱表示不是问题。