Reputation: 541
I'm trying to better understand the C99 standard but now I'm confused about using enums as bitfields in structs and if they are treated as int or as implementation-defined type. When looking up in the final draft for C99, I found 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.
and 6.7.2.2 para. 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. ...
So I tried with this simple source code
enum e {
E0, E1
};
struct s {
enum e bitfield : 4;
};
I can compile this without warnings with gcc-5.0 and clang-3.5 using -std=c99 -Wall -Wextra -pedantic
but I get the following warning with gcc-4.8
warning: type of bit-field 'bitfield' is a GCC extension
Here start the confusion. Are enums as bitfields treated as int or implemenation-defined type? Is this a bug in GCC-4.8 or did they change their interpretation of the standard? And is it safe to use this with other C99-compiler?
Upvotes: 7
Views: 2710
Reputation: 33601
It may be safe, but don't do it. You're violating an implied design-by-contract. Sort of. You mentioned the construct, but didn't mention how you really wanted to use it. There may be a cleaner way.
If you have:
typedef enum {
E0, E1, E2
} myenum_t;
myenum_t val;
Now, if you have various switch statements, such as:
switch (val) {
case E0:
...
break;
case E1:
...
break;
case E2:
...
break;
}
They will be checked at compile time to ensure your switch
covered all the cases. If you then add an E3
to your enum definition, the compiler will flag the switch
statements as missing E3
. This is useful.
If you start bit diddling your enum value, you may have to change your switch
to:
switch (val) {
case E0:
...
break;
case E1:
...
break;
case E2:
...
break;
default:
...
break;
}
Now, if you add E3
to your enum, the compiler won't flag your switch
for the missing case
because it assumes default
will handle it. Maybe not what you want.
Adding the default
is usually there to debug errant enum values.
However, if you had:
typedef struct {
unsigned int mask:8;
unsigned int enval:8;
unsigned int ident:8;
unsigned int other:8;
} mybit_t;
mybit_t bval;
Using the following:
switch ((myenum_t) bval.enval) {
...
}
Is possibly a bit cleaner and maybe closer to what you really hope to achieve.
The "implied contract" is that [in this context] enums are intended to be a set of whole numbers. Note that you can also have:
typedef enum {
M0 = 1 << E0, M1 = 1 << E1, M2 = 1 << E2
} mymask_t;
And, it's perfectly fine to do something like: bval.mask |= M2;
. Leave the bitfield slicing and dicing to int
where you can control the size [sort of]. There's no advantage to trying to apply a semi-non-portable extension when using the standard stuff works just fine.
Upvotes: 0
Reputation: 263237
Are enums as bitfields implementation-defined types?
Yes.
What you're seeing is a change in the implementation-defined behavior of gcc.
As it says in the section of the standard you quoted, a bit field must be of type _Bool
, int
, unsigned int
, or some implementation-defined type.
A enum
type is compatible with some integer type. Experiment and a look at the gcc manual shows that for gcc, your enum e
is compatible with unsigned int
. But the standard doesn't permit a bit field to be of a type compatible with unsigned int
. It actually has to be of type unsigned int
(compatible types are not necessarily the same type). Except that it can also be of some other implementation-defined type.
According to the manual for gcc 4.8.4:
- 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.
According to the manual for 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.
So what you're seeing is a change in the behavior of gcc, allowing more types for bit fields even in "strictly conforming mode". This is not a change in gcc's interpretation of the standard; both behaviors are permitted.
Using enum
s as bit fields is not portable. A conforming C compiler may or may not support them. (I personally would have preferred it if gcc had kept the ability to get a warning for this.)
Upvotes: 7