在C99中的结构中强制执行枚举值的宽度

时间:2014-06-24 22:32:24

标签: c struct enums c99 bit-fields

我有一个打算使用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,问题就会消失,但不能假设这种行为。

3 个答案:

答案 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)仅保证在位字段声明中支持intsigned intunsigned 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);