C 是否将枚举定义为位字段实现类型?

C 是否将枚举定义为位字段实现类型?,c,language-lawyer,c99,C,Language Lawyer,C99,我试图更好地理解C99标准,但现在我对在结构中使用枚举作为位字段以及它们是否被视为int或实现定义的类型感到困惑。在查看C99的最终草案时,我发现了6.7.2.1段。四, 位字段的类型应为合格或不合格版本的\u Bool,有符号整数,无符号整数,或其他实现定义的类型 以及第6.7.2.2段。四, 每个枚举类型应与字符、有符号整数类型或无符号整数类型兼容。类型的选择由实现定义,但应能够表示枚举的所有成员的值 所以我尝试了这个简单的源代码 enum e { E0, E1 }; struct

我试图更好地理解C99标准,但现在我对在结构中使用枚举作为位字段以及它们是否被视为int或实现定义的类型感到困惑。在查看C99的最终草案时,我发现了6.7.2.1段。四,

位字段的类型应为合格或不合格版本的\u Bool有符号整数无符号整数,或其他实现定义的类型

以及第6.7.2.2段。四,

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

所以我尝试了这个简单的源代码

enum e {
    E0, E1
};

struct s {
    enum e bitfield : 4;
};
我可以使用gcc-5.0和clang-3.5,使用
-std=c99-Wall-Wextra-pedantic
编译此文件,而不会出现警告,但我使用gcc-4.8得到以下警告

warning: type of bit-field 'bitfield' is a GCC extension
这里开始混乱。作为位字段的枚举是否被视为int类型或实现定义的类型?这是GCC-4.8中的错误,还是他们更改了对标准的解释?与其他C99编译器一起使用是否安全

是否将枚举定义为位字段实现类型

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

正如您引用的标准部分所述,位字段的类型必须为
\u Bool
int
unsigned int
,或某种实现定义的类型

enum
类型与某些整数类型兼容。实验和gcc手册表明,对于gcc,
enum e
unsigned int
兼容。但该标准不允许位字段的类型与
无符号int
兼容。它实际上必须是类型
unsigned int
(兼容类型不一定是同一类型)。除此之外,它还可以是其他实现定义的类型

根据报告:

  • 除了
    \u Bool
    有符号整数
    无符号整数
    (C99 6.7.2.1)之外,允许的位字段类型
在严格一致的模式下,不允许使用其他类型

根据gcc-5.2.0手册:

  • 除了
    \u Bool
    有符号整数
    无符号整数
    (C99和C11 6.7.2.1)之外,允许的位字段类型
即使在严格一致的模式下,也允许使用其他整数类型,例如
long int
,以及枚举类型

因此,您看到的是gcc行为的变化,即使在“严格一致模式”下,也允许更多类型的位字段。这不是gcc对本标准解释的变更;这两种行为都是允许的


使用
enum
s作为位字段是不可移植的。一致性C编译器可能支持也可能不支持它们。(我个人更希望gcc能够获得警告。)

这可能是安全的,但不要这样做。你违反了合同中的默示设计。某种程度上。您提到了构造,但没有提到您真正想要如何使用它。也许有更干净的方法

如果您有:

typedef enum {
    E0, E1, E2
} myenum_t;

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

switch (val) {
case E0:
    ...
    break;
case E1:
    ...
    break;
case E2:
    ...
    break;
}
它们将在编译时进行检查,以确保您的
开关涵盖所有情况。如果随后将
E3
添加到枚举定义中,编译器会将
开关
语句标记为缺少
E3
。这是有用的

如果开始对枚举值进行位欺骗,则可能必须将
开关更改为:

switch (val) {
case E0:
    ...
    break;
case E1:
    ...
    break;
case E2:
    ...
    break;
default:
    ...
    break;
}
现在,如果将
E3
添加到枚举中,编译器不会为缺少的
大小写标记
开关
,因为它假定
默认值
将处理它。也许不是你想要的

添加默认值通常是为了调试错误的枚举值

但是,如果您有:

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) {
    ...
}
可能更干净一点,可能更接近你真正希望实现的目标

“隐含契约”是指[在此上下文中]枚举是一组整数。请注意,您还可以拥有:

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

M0=1@Rhymoid的可能重复项我不确定这是否真的是重复项。AFAIK是无符号字符,是无符号int的子类型,而enum可以等效于int。也可能是我误解了“与[…]整型兼容”在6.7.2.2第4段中,第一个引号表示其实现定义了允许使用哪些其他类型作为位字段