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
或是这种未定义的行为?
答案 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位。