我正在努力更好地理解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 回答
是 .
您所看到的是gcc的实现定义行为的变化 .
正如您在引用的标准部分中所述,位字段必须是
_Bool
,int
,unsigned int
或某种实现定义类型 .enum
类型与某种整数类型兼容 . 实验和gcc手册的显示表明,对于gcc,enum e
与unsigned int
兼容 . 但是该标准不允许位字段与unsigned int
兼容 . 它实际上必须是unsigned int
类型(兼容类型不一定是同一类型) . 除了它也可以是一些其他实现定义的类型 .根据manual for gcc 4.8.4:
根据gcc-5.2.0的手册:
所以你所看到的是gcc行为的改变,即使在“严格符合模式”下也允许更多类型的位域 . 这不是gcc对标准的解释的变化;两种行为都是允许的 .
使用
enum
s作为位字段是不可移植的 . 符合标准的C编译器可能支持也可能不支持它们 . (如果gcc保留了对此发出警告的能力,我本人会更喜欢它 . )它可能是安全的,但不要这样做 . 你违反了隐含的 Contract 设计 . 有点 . 你提到了这个构造,但没有提到你是如何使用它的 . 可能有一种更清洁的方式 .
如果你有:
现在,如果您有各种switch语句,例如:
它们将在编译时进行检查,以确保您的
switch
涵盖所有案例 . 如果然后在枚举定义中添加E3
,则编译器会将switch
语句标记为缺少E3
. 这很有用 .如果你开始咬住你的枚举值,你可能需要将你的_2843124改为:
现在,如果将
E3
添加到枚举中,编译器将不会为缺少的case
标记switch
,因为它假定default
将处理它 . 也许不是你想要的 .添加
default
通常用于调试错误的枚举值 .但是,如果你有:
使用以下内容:
可能有点清洁,也许更接近你真正希望实现的目标 .
“隐含 Contract ”是[在这种情况下]枚举旨在成为一组整数 . 请注意,您还可以:
并且,完成这样的事情是完全可以的:
bval.mask |= M2;
. 保留位域切片和切块到int
,您可以控制大小[种类] . 当使用标准的东西工作得很好时,尝试应用半非便携式扩展没有任何优势 .