2015-11-07 64 views
4

我想更好地理解C99标准,但现在我很困惑在枚举中使用枚举作为位域,如果它们被视为int或作为实现定义的类型。在C99的最终草案中查找时,我发现6.7.2.1段。 4枚举为位域实现定义类型吗?

位字段应具有一种类型,是_Bool的合格或不合格的版本,符号int无符号整型,或其他一些实现定义类型。

和6.7.2.2段。 4

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

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

enum e { 
    E0, E1 
}; 

struct s { 
    enum e bitfield : 4; 
}; 

我可以用gcc-5.0编译这个没有警告和使用-std=c99 -Wall -Wextra -pedantic铛-3.5,但我得到的gcc-4.8以下警告

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

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

+0

可能重复[使用位域与无符号字符时警告](http://stackoverflow.com/questions/10906238/warning-when-using-bitfield-with-unsigned-char) – 2015-11-07 21:07:23

+1

@Rhymoid我不确定如果这真的是重复的。 AFAIK是无符号字符unsigned int的子类型,而枚举可以等同于int。它也可能是我误解了6.7.2.2段中的“与整数类型兼容”。 4. – fsasm

+0

第一个引用意味着它是实现定义哪些其他类型允许用作位域 –

回答

4

枚举为位域实现定义的类型吗?

是的。

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

正如你所引用的标准的部分说,有些字段必须是_Bool类型,intunsigned int一些实现定义的类型。

A enum类型与某些整数类型兼容。通过实验和gcc手册看看,对于gcc,您的enum eunsigned int兼容。但该标准不允许位域为兼容unsigned int。它实际上必须是unsigned int(兼容类型不一定是相同类型)。 外,它也可以是其他实现定义的类型。

按照manual for gcc 4.8.4

  • 允许的位字段比_Boolsigned int,和unsigned int其他类型的(C99 6.7.2.1)。

在严格符合模式下不允许其他类型。

根据手册为GCC-5.2.0:

  • 允许位字段类型以外_Boolsigned int,和unsigned int(C99和C11 6.7.2.1)。

其他整数类型,例如long int和枚举类型即使在严格符合模式下也是允许的。

因此,您所看到的是gcc行为的变化,即使在“严格符合模式”中也允许更多类型的位字段。这不是gcc对标准解释的改变;两种行为都是允许的。使用enum作为位域是不可移植的。符合的C编译器可能会支持它们,也可能不支持它们。 (如果gcc保留了为此发出警告的能力,我个人会首选它。)

0

它可能是安全的,但不要这样做。你违反了暗示的合同设计。有点。你提到了构造,但没有提到你真的想用它。可能有更干净的方法。

如果您有:

typedef enum { 
    E0, E1, E2 
} myenum_t; 

myenum_t val; 

现在,如果你有不同的开关语句,如:

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

他们将在编译时进行检查,以确保您的switch覆盖所有的案件。如果您随后将E3添加到您的枚举定义中,编译器将会将switch语句标记为缺少E3。这很有用。

如果你开始有点diddling你的枚举值,您可能需要改变你的switch到:

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

现在,如果你添加E3到您的枚举,编译器不会标志您switch为缺少case,因为它假定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) { 
    ... 
} 

可能是有点清洁,也许更接近你真正希望达到的目标。

“隐含的合约”是[在此范围内]枚举旨在为一组整数。需要注意的是,你还可以有:

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

而且,这是完全没有做这样的事情:bval.mask |= M2;。将位域切片和切块留在int,您可以在其中控制大小[排序]。当使用标准的东西时,尝试应用半不可移植的扩展并没有什么好处。