1

Since mask values are integers we can represent them in different ways. For example, as macros:

#define ENABLE_FEATURE_A 0x1
#define ENABLE_FEATURE_B 0x2
#define ENABLE_FEATURE_C 0x4
#define ENABLE_FEATURE_D 0x8

Or enums:

typedef enum {
  FEATURE_A = 0x1,
  FEATURE_B = 0x2,
  FEATURE_C = 0x4,
  FEATURE_D = 0x8
} enabled_features_t

I argue that the enum representation is not appropriate for masks because masks are intended to be combined (with bitwise operations). C requires casting of enums to int because we aren't expected to perform operations on numbers that may not have a meaning. So I would think using macros is more appropriate for flags. Am I overthinking this?

5
  • 6
    In C and enumeration is basically a way to introduced named symbolic int constants, just like macros. They are for all intents and purposes plain int values. So with your code, there's no difference between the macro ENABLE_FEATURE_A and the enumeration symbol FEATURE_A. Commented Jul 10 at 8:53
  • Enumerators are integral constants. They don't need to be cast. Commented Jul 10 at 9:13
  • @Someprogrammerdude what do you mean by "plain int" ? They are stored in memory like an int, but the compiler doesnt give them an explicit type ?
    – polozer
    Commented Jul 10 at 9:24
  • 2
    @polozer An enumeration isn't really stored in memory at all. They are, as I said, symbolic int constants, and similar to macros they are compile-time constants that are replaced by the compiler. So assuming the enumeration defined in the question, doing int features = FEATURE_A | FEATURE_B; will be compiled as int features = 1 | 2; which is exactly what the OP seems to want. Commented Jul 10 at 9:27
  • You could even use FEATURE_AC = FEATURE_A | FEATURE_C, in your enum list...
    – Fe2O3
    Commented Jul 10 at 9:39

2 Answers 2

2

No, enumeration constants are not suitable for bit masks, for two reasons:

  • Enumeration constants are of type int which is signed, and therefore it is an unfortunate type to mix with bitwise operations in particular, since many bitwise operations like shifts invoke various forms of poorly-defined behavior when used on signed types.
  • They are too small. You can't fit the number 0x80000000 inside an enumeration constant, so it simply can't be practically used to bit mask a 32 bit number.

The appropriate solution as of today (ISO C 9899:2018) is to use #define with integer constants ending with an U/u suffix.


The upcoming C23 standard will solve this however. It will become possible to use this:

typedef enum : uint32_t
{
  FEATURE_X = 0x80000000
} enabled_features_t;

Now the equivalent type used for enum objects of this type, as well as the enumeration constant, is uint32_t.

1
  • Thanks. I was bothered by the casting, which I was wrong about, but didn't think about the int vs uint issue and the size issue.
    – Lolo
    Commented Jul 11 at 9:13
2

In C, a “member” of an enumeration is an enumeration constant of type int, per C 2018 6.2.1 1:

…A member of an enumeration is called an enumeration constant

and 2018 6.4.4.3 2:

An identifier declared as an enumeration constant has type int.

The enumeration type itself may have some other type, per C 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,…

I argue that the enum representation is not appropriate for masks because masks are intended to be combined (with bitwise operations).

Since enumeration members are int values, they may be operated on as int values. The bitwise operations work on them as they do for other int values.

It is often preferred to use an unsigned type with bitwise operations, due to issues with how the C standard defines or does not define calculations involving the sign bit in signed types. Aside from that, it is fine to use int values as bit masks.

C requires casting of enums to int because we aren't expected to perform operations on numbers that may not have a meaning.

The C standard has no such requirement. Perhaps you are thinking of some issues that arises in C++?

2
  • Thanks Eric. It seems the consensus that there is nothing wrong with enums for flags. As for the casting, I ran some experiments and didn't get any casting issue in C or C++. I must have recalled this wrong.
    – Lolo
    Commented Jul 10 at 11:42
  • @Lolo The consensus is rather that they should not be used for this purpose. For authoritative sources, see for example MISRA C:2023 rule 10.1.
    – Lundin
    Commented Jul 10 at 12:45

Not the answer you're looking for? Browse other questions tagged or ask your own question.