我有一个打算使用32位存储的结构:
struct foo_t {
color_t color : 10
some_type_t some_field : 22;
}
,其中color_t
是定义为
typedef enum {
RED = 0,
// other values...
BLUE = 255
} color_t
请注意,color_t
值当前适合8位,但将来我们可能会添加更多值(因此我们为color
保留了10位)
在C99中,我想知道是否有任何保证编译器会尊重color
的宽度。正如this question中所讨论的,编译器可能会选择将color_t
表示为char。此时,根据C99 spec:
指定位字段宽度的表达式应为整数常量表达式,其非负值不超过将指定的类型的对象宽度,冒号和表达式被省略。
如何强制color
字段使用10位呢?请注意,如果编译器使用常规整数来表示color_t
,问题就会消失,但不能假设这种行为。
答案 0 :(得分:3)
为enum
定义添加最终标记:
typedef enum {
//...
DUMMY_FORCE_WIDTH = 0xffffffffu, // INT_MAX,
} color_t;
这有一个额外的好处,就是强迫编译器/ ABI为你的enum
提供足够的空间来增长。
当然,这预先假定您的编译器允许enum
作为位字段类型。它不需要这样做,但它必须将其诊断为违反约束:
6.7.2.1结构和联合说明符§5(约束)
位字段的类型应为_Bool的合格或非限定版本,已签名 int,unsigned int或其他一些实现定义的类型。它是 实现 - 定义是否允许原子类型。
如果要严格遵守,请定义有符号或无符号类型的位字段,而不是特殊枚举类型。尽管如此,这只能保证16位长度。
答案 1 :(得分:2)
严格地说,C语言(C99)仅保证在位字段声明中支持int
,signed int
,unsigned int
和_Bool
类型,尽管具体实现是明确的允许支持其他类型作为依赖于实现的行为。
换句话说,在这种情况下,我们正在处理特定于实现的行为,因为您使用enum
类型声明了您的位字段。最好的方法是查阅编译器文档,但是如果选择的存储单元不能容纳10位,我希望它能够满足您的10位请求或生成诊断消息。
在我的练习中,我通常使用以下方法
typedef enum color_t {
RED = 0,
// other values...
BLUE,
COLOR_COUNT
} color_t;
#define COLOR_BIT_WIDTH 8u
STATIC_ASSERT(COLOR_COUNT <= (1 << COLOR_BIT_WIDTH));
// Use your favorite implementation of `STATIC_ASSERT`
struct foo_t {
unsigned color : COLOR_BIT_WIDTH;
...
};
请注意,此实现不会尝试为将来扩展颜色类型保留其他位(您真的有理由这样做吗?)。它都不会尝试自动计算位域的宽度。相反,它只是在宽度不再足够时捕获这种情况,从而迫使代码维护者注意到问题并手动修复它。
P.S。可以添加一个额外的简单STATIC_ASSERT
检查,以便使用位字段宽度不会过大,如果您需要它并且#34;恰到好处&#34;。
答案 2 :(得分:0)
您可以将所有数据组合在stdint.h中定义的固定32位int(uint32_t)中, 并按位运算符访问值。
示例:
uint32_t data = (uint32_t)color<<22 | (uint32_t)some_field;
color=(color_t ) (data>>22);
some_field =(some_type_t ) (data & 0x3FFFFF);