说,我有以下结构:
typedef struct my_struct{
unsigned long a;
unsigned long b;
char* c;
unsigned int d1 :1;
unsigned int d2 :4;
unsigned int d3 :4;
unsigned int d4 :23;
} my_type, *p_type;
d3
字段目前由#define
s定义,从0x00
到0x0D
。
实际上,d3
是一个枚举。所以很有可能继续前进并取而代之
unsigned int d3 :4;
通过
my_enum d3 :4;
这是安全/允许的吗?
代码必须用各种编译
显然,我可以保留d3
的定义,并在我的代码中使用枚举,将其分配给d3
等等,但这不适用于C ++。
对于C和C ++,答案会有所不同,这对于C.
在C中,位域仅限于signed int
,unsigned int
,_Bool
和int
,在这种情况下,它们可以是前两个中的任何一个。编译器实现者可以根据自己的喜好添加到该列表,但需要记录它们支持的类型。
所以要回答你的问题,如果你想绝对确保你的代码可以移植到所有C编译器,不,使用enum
类型不是一个选项。
现行标准的相应段落如下:
位字段的类型应为_Bool,signed int,unsigned int或其他实现定义类型的限定或非限定版本。它是实现定义的,是否允许原子类型。
它允许在所有支持标准的C ++编译器中使用。
C ++ 03标准9.6 / 3
位域应具有整数或枚举类型(3.9.1)。它是实现定义的是明文(既没有显式签名也没有未签名)char,short,int或long位字段是有符号还是无符号。
C ++ 03标准9.6 / 4
如果将枚举器的值存储到相同枚举类型的位字段中,并且位字段中的位数足以容纳该枚举类型的所有值,则原始枚举器值和值比特字段应比较相等。
例
enum BOOL { f=0, t=1 };
struct A {
BOOL b:1;
};
void f() {
A a;
a.b = t;
a.b == t // shall yield true
}
但你不能认为枚举有未签名的底层类型。
C ++ 03标准7.2 / 5
枚举的基础类型是一个整数类型,可以表示枚举中定义的所有枚举器值。它是实现定义的,其中整数类型用作枚举的基础类型,除了基础类型不应大于int,除非枚举器的值不能适合int或unsigned int
没有。
在编译器之间实现位字段的显着不同。如果您定义一个具有两个值的位域,零和一,并尝试使用枚举类型的位字段,那么您可能会遇到以下问题:
位字段将使用gcc和clang进行无符号,但使用VC ++进行签名。这意味着为了存储零和一,你需要一个两位的位字段(一位有符号位字段只能存储零和负一位)。
然后你必须担心包装。如果大小匹配,VC ++只会将相邻的位字段打包到同一个后备存储中。我不确定gcc和clang的规则是什么,但对于VC ++,位字段的默认后备存储是int。因此,一系列比特字段,例如,bool和enum的混合,将用VC ++打包得非常糟糕。
您可以尝试使用C ++ 11类型的枚举来解决此问题:
enum Foo:unsigned char {one,two};
但是如果你在一位位字段中使用它,gcc会抱怨:
警告:'bitfieldTest :: g'太小,无法容纳'enum Foo'的所有值[默认启用]
似乎没有胜利。
在C中它是一个未定义的行为,因为位域只能有signed int
,int
或unsigned int
类型(或cazxswpoi与C99)。
6.5.2.1 :
位字段的类型应为int,unsigned int或signed int之一的限定或非限定版本。是否将(可能合格的)“plain”int位字段的高位位置视为符号位是实现定义的。位字段被解释为由指定位数组成的整数类型。
否则,一些编译器今天接受它作为扩展(参见标准中扩展的实现定义行为)。