33

Say, I've got the following struct:

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;

The field d3 is currently defined by #defines that reach from 0x00 until 0x0D.

Actually, d3 is an enumeration. So it's tempting to go ahead and replace

    unsigned int        d3  :4;

by

    my_enum             d3  :4;

Is this safe/allowed?

The code has to compile with various

  • compilers (GCC, Visual Studio, embedded stuff)
  • platforms (Win32, Linux, embedded stuff)
  • configurations (compile as C, compile as C++)

Obviously, I could leave the definition of d3 as it is and use the enum in my code, assign it to d3 and so on but that's not going to work with C++.

eckes
  • 64,417
  • 29
  • 168
  • 201

4 Answers4

25

It's allowed in all C++ compilers, that supports standard.

C++03 standard 9.6/3

A bit-field shall have integral or enumeration type (3.9.1). It is implementation-defined whether a plain (neither explicitly signed nor unsigned) char, short, int or long bit-field is signed or unsigned.

C++03 standard 9.6/4

If the value of an enu- merator is stored into a bit-field of the same enumeration type and the number of bits in the bit-field is large enough to hold all the values of that enumeration type, the original enumerator value and the value of the bit-field shall compare equal.

example

enum BOOL { f=0, t=1 };

struct A {
    BOOL b:1;
};

void f() {
    A a;
    a.b = t;
    a.b == t // shall yield true
}

But you can't consider that enum has unsigned underlying type.

C++03 standard 7.2/5

The underlying type of an enumeration is an integral type that can represent all the enumerator values defined in the enumeration. It is implementation-defined which integral type is used as the underlying type for an enumeration except that the underlying type shall not be larger than int unless the value of an enu- merator cannot fit in an int or unsigned int

sheeldotme
  • 2,237
  • 14
  • 27
ForEveR
  • 55,233
  • 2
  • 119
  • 133
  • This is an old answer, so I'll just point out that since C++11 you scan specify an (integral) base type for your enums. – Spencer Mar 21 '22 at 12:38
17

Answer will be different for C and C++, this is one for C.

In C bitfields are restricted to signed int, unsigned int, _Bool and int which in this context can be any of the first two. Compiler implementors can add to that list to their liking but are required to document the types that they support.

So to answer your question, if you want to be absolutely sure that your code is portable to all C compilers, no, using an enum type is not an option.

The corresponding paragraph from the current standard reads:

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. It is implementation-defined whether atomic types are permitted.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • 2
    I usually go with `#ifdef __GNUC__ #define ENUMBF(type) __extension__ type #else #define ENUMBF(type) unsigned #endif`. And later put `ENUMBF(myenum) field:x;` into my structs. The code is less nice but you don't miss important gcc warnings like when your bitfield is too small because a coworker added more cases to the enum... – MatzeBraun Nov 25 '14 at 07:58
8

No.

Bit fields are implemented significantly differently between compilers. If you define a bit-field with two values, zero and one, and try to have an enum typed bit field then you may hit these problems:

The bit field will be unsigned with gcc and clang, but signed with VC++. This means that in order to store zero and one you need a two-bit bit field (a one-bit signed bit field can only store zero and negative one).

Then you have to worry about packing. VC++ will only pack adjacent bit fields into the same backing store if their sizes match. I'm not sure what the rules are for gcc and clang, but for VC++ the default backing store for a bit field is an int. So, a series of bit fields that are, for instance, a mixture of bool and enum will pack extremely poorly with VC++.

You could try to solve this with C++ 11 typed enums:

enum Foo : unsigned char { one, two };

but then gcc complains if you use this in a one-bit bit field:

warning: ‘bitfieldTest::g’ is too small to hold all values of ‘enum Foo’ [enabled by default]

It seems there is no winning.

Bruce Dawson
  • 3,284
  • 29
  • 38
  • It's interesting that for `enum class` it is much better for some reason: all compilers default to `int`, underlying type can be changed without warning in gcc. The only problem is that gcc had a bug where it used to give the same erroneous warnings in all the cases for `enum class` for quite a while. – Predelnik Sep 24 '20 at 14:56
  • 2
    This is out of date now; [gcc fixed the bug that does this](https://godbolt.org/z/xz7fKzchs). You can put a two-value enum in a one-bit bitfield now. – Spencer Mar 21 '22 at 12:51
-1

In C it is an undefined behavior, because a bit-field can only have signed int, int or unsigned int types (or _Bool with C99).

6.5.2.1 :

A bit-field shall have a type that is a qualified or unqualified version of one of int, unsigned int, or signed int. Whether the high-order bit position of a (possibly qualified) “plain” int bit-field is treated as a sign bit is implementation-defined. A bit-field is interpreted as an integral type consisting of the specified number of bits.

Otherwise, some compilers accept it today as an extension (cf. implementation-defined behavior of the extensions in the standard).

md5
  • 23,373
  • 3
  • 44
  • 93
  • 4
    This is not undefined behavior, but a constraint violation. It is part of a section labled "constraint" and the use of "shall" generaly marks constraints in the standard. So a compiler is required to issue a diagnostic if the type is not ok. Second you are citing seemingly an obsolete version of the standard. See my answer for the correct version (I edited it in) which definitively allows implementation specific types to occur. – Jens Gustedt Aug 16 '12 at 09:11
  • Actually ISO/IEC 9899:1999 (E) shows: 4. Conformance 1 In this International Standard, ‘‘shall’’ is to be interpreted as a requirement on an implementation or on a program; conversely, ‘‘shall not’’ is to be interpreted as a prohibition 6.7.2.1 ... 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. So, implementation-defined types are allowed. `typedef enum {...} mytype_t;` would fit that. Still, the compiler I am using accepts that, but ignores the bit-field size for it. – le_top Feb 01 '21 at 16:48
  • In addition to the above, J.3.9 from the standard referenced above implies that "Allowable bit-field types other than _Bool, signed int, and unsigned int" have "Implementation-defined behavior". – le_top Feb 01 '21 at 16:58