uint32_t和uint8_t [4]的C联合是否总是在小端架构上以相同的方式映射?

时间:2018-04-03 10:29:26

标签: c endianness

uint32_t和uint8_t [4]的C联合是否总是在小端架构上以相同的方式映射?

e.g。与

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

或是这种未定义的行为?

2 个答案:

答案 0 :(得分:5)

TL; DR:是的,代码很好。

如上所述,它包含依赖于endianess的实现定义的行为,但除此之外,行为是明确定义的,代码是可移植的(在小端机器之间)。

详细解答:

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

  

数组类型描述了具有特定成员对象类型的连续分配的非空对象集,称为元素类型。

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

理论上,编译器可以自由地在联合的末尾抛出任何填充(C11 6.7.2.1/17),但这不应该影响数据表示。如果你想迂腐地防止这种情况 - 或者更相关的是,你希望防止以后添加更多成员的问题 - 你可以添加一个编译时断言:

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)。

最后,关于是否通过联合“打字”的问题是允许的还是未定义的行为,这在C11 6.5.2.3中有点含糊地说明:

  

后缀表达式后跟.运算符,标识符指定结构或联合对象的成员。该值是指定成员的值, 95),如果第一个表达式是左值,则为左值。

(非规范性)注释95提供澄清:

  

如果用于读取union对象内容的成员与上次使用的成员不同   在对象中存储一个值,该值的对象表示的相应部分被重新解释   作为6.2.6中描述的新类型中的对象表示(有时称为''类型的过程)   双关语””)。这可能是陷阱表示。

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

答案 1 :(得分:3)

在实际 这两种类型的平台上,C11 §7.20.1.1p2为您提供所有必需的保证(假设您知道字节顺序):

  

typedef名称uintN_t指定宽度为 N 的无符号整数类型,不   填充位。因此,uint24_t表示宽度为的无符号整数类型   正好是24位。

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