枚举是位域实现定义的类型吗?
Are enums as bitfields implementation-defined types?
我试图更好地理解 C99 标准,但现在我对将枚举用作结构中的位域以及它们是被视为 int 还是实现定义的类型感到困惑。在查找 C99 的最终草案时,我找到了 6.7.2.1 para。 4
A bit-field shall have a type that is a qualified or unqualified version of _Bool, signed int, unsigned int, or some other implementation-defined type.
和 6.7.2.2 段。 4
Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type. The choice of type is implementation-defined, but shall be capable of representing the values of all the members of the enumeration. ...
所以我尝试使用这个简单的源代码
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 编译器一起使用是否安全?
Are enums as bitfields implementation-defined types?
是的。
您看到的是 gcc 的实现定义行为的变化。
正如您引用的标准部分所述,位域的类型必须为 _Bool
、int
、unsigned int
、 或 一些实现定义的类型。
A enum
类型与某些整数类型兼容。实验并查看 gcc 手册表明,对于 gcc,您的 enum e
与 unsigned int
兼容。但是该标准不允许位域是 compatible with unsigned int
类型。它实际上必须是 unsigned int
类型(兼容类型不一定是同一类型)。 除了它也可以是其他一些实现定义的类型。
- Allowable bit-field types other than
_Bool
, signed int
, and unsigned int
(C99 6.7.2.1).
No other types are permitted in strictly conforming mode.
根据 gcc-5.2.0 的手册:
- Allowable bit-field types other than
_Bool
, signed int
, and unsigned int
(C99 and C11 6.7.2.1).
Other integer types, such as long int
, and enumerated types are permitted even in strictly conforming mode.
因此,您看到的是 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;
}
它们将在编译时进行检查,以确保您的 switch
涵盖所有情况。如果随后将 E3
添加到枚举定义中,编译器会将 switch
语句标记为缺少 E3
。这个有用。
如果您开始使用枚举值,您可能需要将 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) {
...
}
可能更简洁一些,可能更接近您真正希望实现的目标。
"implied contract" 是 [在这种情况下] 枚举旨在成为一组整数。请注意,您还可以:
typedef enum {
M0 = 1 << E0, M1 = 1 << E1, M2 = 1 << E2
} mymask_t;
而且,做这样的事情是完全没问题的:bval.mask |= M2;
。将位域切片和切块留给 int
,您可以在其中控制大小 [排序]。当使用标准的东西工作得很好时,尝试应用半不可移植的扩展没有任何优势。
我试图更好地理解 C99 标准,但现在我对将枚举用作结构中的位域以及它们是被视为 int 还是实现定义的类型感到困惑。在查找 C99 的最终草案时,我找到了 6.7.2.1 para。 4
A bit-field shall have a type that is a qualified or unqualified version of _Bool, signed int, unsigned int, or some other implementation-defined type.
和 6.7.2.2 段。 4
Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type. The choice of type is implementation-defined, but shall be capable of representing the values of all the members of the enumeration. ...
所以我尝试使用这个简单的源代码
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 编译器一起使用是否安全?
Are enums as bitfields implementation-defined types?
是的。
您看到的是 gcc 的实现定义行为的变化。
正如您引用的标准部分所述,位域的类型必须为 _Bool
、int
、unsigned int
、 或 一些实现定义的类型。
A enum
类型与某些整数类型兼容。实验并查看 gcc 手册表明,对于 gcc,您的 enum e
与 unsigned int
兼容。但是该标准不允许位域是 compatible with unsigned int
类型。它实际上必须是 unsigned int
类型(兼容类型不一定是同一类型)。 除了它也可以是其他一些实现定义的类型。
- Allowable bit-field types other than
_Bool
,signed int
, andunsigned int
(C99 6.7.2.1).No other types are permitted in strictly conforming mode.
根据 gcc-5.2.0 的手册:
- Allowable bit-field types other than
_Bool
,signed int
, andunsigned int
(C99 and C11 6.7.2.1).Other integer types, such as
long int
, and enumerated types are permitted even in strictly conforming mode.
因此,您看到的是 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;
}
它们将在编译时进行检查,以确保您的 switch
涵盖所有情况。如果随后将 E3
添加到枚举定义中,编译器会将 switch
语句标记为缺少 E3
。这个有用。
如果您开始使用枚举值,您可能需要将 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) {
...
}
可能更简洁一些,可能更接近您真正希望实现的目标。
"implied contract" 是 [在这种情况下] 枚举旨在成为一组整数。请注意,您还可以:
typedef enum {
M0 = 1 << E0, M1 = 1 << E1, M2 = 1 << E2
} mymask_t;
而且,做这样的事情是完全没问题的:bval.mask |= M2;
。将位域切片和切块留给 int
,您可以在其中控制大小 [排序]。当使用标准的东西工作得很好时,尝试应用半不可移植的扩展没有任何优势。