繁体   English   中英

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

或者这是未定义的行为?

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中描述(一个过程有时被称为''punning'')。 这可能是陷阱表示。

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

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

typedef名称uintN_t指定一个宽度为N且没有填充位的无符号整数类型。 因此, uint24_t表示这样的无符号整数类型,其宽度恰好为24位。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM