首页 文章

枚举是否是位域实现定义的类型?

提问于
浏览
4

我正在努力更好地理解C99标准,但现在我对使用枚举作为结构中的位域并将它们视为int或实现定义类型感到困惑 . 在查看C99的最终草案时,我找到了6.7.2.1段 . 4

位字段的类型应为_Bool,signed int,unsigned int或其他实现定义类型的限定或非限定版本 .

和6.7.2.2段 . 4

每个枚举类型应与char,有符号整数类型或无符号整数类型兼容 . 类型的选择是实现定义的,但应能够表示枚举的所有成员的值 . ...

所以我试着用这个简单的源代码

enum e {
    E0, E1
};

struct s {
    enum e bitfield : 4;
};

我可以在没有使用 -std=c99 -Wall -Wextra -pedantic 的gcc-5.0和clang-3.5的警告的情况下编译它但是我用gcc-4.8得到以下警告

warning: type of bit-field 'bitfield' is a GCC extension

这里开始混乱 . 枚举是否将位域视为int或实现定义的类型?这是GCC-4.8中的错误还是他们改变了对标准的解释?与其他C99编译器一起使用是否安全?

2 回答

  • 0

    枚举是否是位域实现定义的类型?

    是 .

    您所看到的是gcc的实现定义行为的变化 .

    正如您在引用的标准部分中所述,位字段必须是 _Boolintunsigned int 或某种实现定义类型 .

    enum 类型与某种整数类型兼容 . 实验和gcc手册的显示表明,对于gcc, enum eunsigned int 兼容 . 但是该标准不允许位字段与 unsigned int 兼容 . 它实际上必须是 unsigned int 类型(兼容类型不一定是同一类型) . 除了它也可以是一些其他实现定义的类型 .

    根据manual for gcc 4.8.4

    除_Bool,signed int和unsigned int(C99 6.7.2.1)以外的允许位字段类型 . 严格符合模式不允许其他类型 .

    根据gcc-5.2.0的手册:

    _Bool,signed int和unsigned int(C99和C11 6.7.2.1)以外的允许位字段类型 . 即使在严格一致的模式下,也允许使用其他整数类型,例如long int和枚举类型 .

    所以你所看到的是gcc行为的改变,即使在“严格符合模式”下也允许更多类型的位域 . 这不是gcc对标准的解释的变化;两种行为都是允许的 .

    使用 enum s作为位字段是不可移植的 . 符合标准的C编译器可能支持也可能不支持它们 . (如果gcc保留了对此发出警告的能力,我本人会更喜欢它 . )

  • 4

    它可能是安全的,但不要这样做 . 你违反了隐含的 Contract 设计 . 有点 . 你提到了这个构造,但没有提到你是如何使用它的 . 可能有一种更清洁的方式 .

    如果你有:

    typedef enum {
        E0, E1, E2
    } myenum_t;
    
    myenum_t val;
    

    现在,如果您有各种switch语句,例如:

    switch (val) {
    case E0:
        ...
        break;
    case E1:
        ...
        break;
    case E2:
        ...
        break;
    }
    

    它们将在编译时进行检查,以确保您的 switch 涵盖所有案例 . 如果然后在枚举定义中添加 E3 ,则编译器会将 switch 语句标记为缺少 E3 . 这很有用 .

    如果你开始咬住你的枚举值,你可能需要将你的_2843124改为:

    switch (val) {
    case E0:
        ...
        break;
    case E1:
        ...
        break;
    case E2:
        ...
        break;
    default:
        ...
        break;
    }
    

    现在,如果将 E3 添加到枚举中,编译器将不会为缺少的 case 标记 switch ,因为它假定 default 将处理它 . 也许不是你想要的 .

    添加 default 通常用于调试错误的枚举值 .

    但是,如果你有:

    typedef struct {
        unsigned int mask:8;
        unsigned int enval:8;
        unsigned int ident:8;
        unsigned int other:8;
    } mybit_t;
    
    mybit_t bval;
    

    使用以下内容:

    switch ((myenum_t) bval.enval) {
        ...
    }
    

    可能有点清洁,也许更接近你真正希望实现的目标 .

    “隐含 Contract ”是[在这种情况下]枚举旨在成为一组整数 . 请注意,您还可以:

    typedef enum {
        M0 = 1 << E0, M1 = 1 << E1, M2 = 1 << E2
    } mymask_t;
    

    并且,完成这样的事情是完全可以的: bval.mask |= M2; . 保留位域切片和切块到 int ,您可以控制大小[种类] . 当使用标准的东西工作得很好时,尝试应用半非便携式扩展没有任何优势 .

相关问题