25

A simple question: is enum { a } e = 1; valid?

In other words: does assigning a value, which isn't present in the set of values of enumeration constants, lead to well-defined behavior?

Demo:

$ gcc t0.c -std=c11 -pedantic -Wall -Wextra -c
<nothing>

$ clang t0.c -std=c11 -pedantic -Wall -Wextra -c
<nothing>

$ icc t0.c -std=c11 -pedantic -Wall -Wextra -c
t0.c(1): warning #188: enumerated type mixed with another type
# note: the same warning for enum { a } e = 0;

$ cl t0.c /std:c11 /Za /c
<nothing>
8
  • 1
    C11 6.5.16.1 states: "arithmetic type = arithmetic type" is OK... enums are arithmetic types, integer constant are arithmetic types so you're good as long as you don't cause overflow.
    – pmg
    Commented Feb 4, 2022 at 15:29
  • 6.7.2.2p4 - Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type.
    – Eugene Sh.
    Commented Feb 4, 2022 at 15:30
  • 1
    You might like clang -Weverything ...
    – pmg
    Commented Feb 4, 2022 at 15:33
  • 1
    This sort of coding will bite hard in switch statements that have a case for each enumerated value but no default. Commented Feb 4, 2022 at 15:57
  • 1
    @pmor: C11 6.2.5p17: "The type char, the signed and unsigned integer types, and the enumerated types are collectively called integer types." and next point (6.2.5p18): "Integer and floating types are collectively called arithmetic types."
    – pmg
    Commented Feb 4, 2022 at 16:56

4 Answers 4

31

From the C18 standard in 6.7.2.2:

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 yes enum { a } e = 1; is valid. e is a 'integer' type so it can take the value 1. The fact that 1 is not present as an enumeration value is no issue. The enumeration members only give handy identifiers for some of possible values.

4
  • 3
    "The enumeration members only give handy identifiers for some of possible values" -- per the language semantics, yes, but this is not usually the programmer's intended usage model. Programmers using enums need to bear in mind this disconnection between their mental model and the actual language semantics. Commented Feb 4, 2022 at 15:44
  • 7
    Even if it is 'allowed', it is bad style and to be avoided.
    – koder
    Commented Feb 4, 2022 at 15:53
  • 4
    it's pretty common in flags though. you don't name every combinations. Commented Feb 6, 2022 at 5:12
  • Despite the many upvotes, I have a doubt that this clause makes the statelent valid. Indeed, "shall be compatible with" means that there could be a conversion from the one to the other, but it does not guarantee that all char or int values will be legal in such a conversion. Moreover "shall be capable of representing the values of all the members of the enumeration" just guarantees that the type chosen shall be capable of holding any of the defined member values (in OP's example it's just 0). It doesn't guarantee the contrary.
    – Christophe
    Commented Feb 9, 2022 at 17:52
6

Valid in C

This is valid in C as per e.g. the 202x working draft:

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

particularly due to the "compatible type" requirement:

6.2.7/1 Two types have compatible type if their types are the same. Additional rules [...]


... implementation-defined and possible unspecified/undefined behavior in C++14 and earlier/C++17 and later

Whilst out of scope for this Q&A (C tag, not C++), it may be interesting to point out that the same does not hold for C++, where "C style" unscoped enums with no fixed underlying type have subtle differences from C. For details about the C++ case, see the following Q&A:

3
  • 7
    The question was always tagged C, so the discussion of C++ behavior is not particularly relevant here. I'd suggest posting a new self-answered question that's basically the same as this one, but for C++. (You can link to this Q and ask "this is what it does in C, what about C++?")
    – cigien
    Commented Feb 4, 2022 at 19:08
  • @cigien Fair point, I'll look into that later this weekend.
    – dfrib
    Commented Feb 4, 2022 at 23:49
  • 1
    @cigien Broke out C++ part, thanks for the prompt.
    – dfrib
    Commented Feb 8, 2022 at 10:46
5

I can find no reference in this Draft C11 Standard (in terms of "constraints") that prohibits an assignment to a enum type such as yours (and the C18 Standard quoted in this other answer uses essentially identical wording).

However, that C11 Draft does provide this, in Annexe I – Common Warnings:

1     An implementation may generate warnings in many situations, none of which are specified as part of this International Standard. The following are a few of the more common situations.

2

   —    A value is given to an object of an enumerated type other than by assignment of an enumeration constant that is a member of that type, or an enumeration object that has the same type, or the value of a function that returns the same enumerated type (6.7.2.2).

But that suggested warning could equally well apply to an assignment like enum { a } e = 0;, where the value of the RHS corresponds to a valid enumeration constant but it is actually neither an enumeration constant of the type nor an object of that enumerated type.

9
  • 1
    I think your reason why enum { a } e = 0; may generate a warning is wrong. The reason should be because 0 is not an enumeration constant (even though it has the same type and value as the enumeration constant a that is a member of the enumerated type).
    – Ian Abbott
    Commented Feb 4, 2022 at 16:03
  • 1
    @IanAbbott Fair comment. I'll edit, but need to think a bit ... because the RHS can be either an enumeration constant or another object of the same enumerated type. Commented Feb 4, 2022 at 16:07
  • In this case, there is precisely one object of the enumerated type, namely the variable e. Since the enumerated type has no tag, it is impossible to create further objects of the same type after the current declaration because there is no way to refer to the type (except by using extensions to the C language such as typeof(e) another_e; in GCC).
    – Ian Abbott
    Commented Feb 4, 2022 at 16:18
  • 1
    @pmor In this case, a and 0 have the same value, so both case labels cannot appear in the same switch statement. As for whether case 0: might generate a warning when the switch control expression has an enumerated type (and is not a constant expression), well Annexe I has nothing to say about that!
    – Ian Abbott
    Commented Feb 4, 2022 at 16:33
  • 1
    Further to previous discussion of compatible enumerated types, each enumerated type will be compatible with some integer type, and I guess that if two enumerated types are compatible with the same integer type, then they must be compatible with each other, assuming compatibility is a transitive property.
    – Ian Abbott
    Commented Feb 4, 2022 at 16:45
2

In short

The validity of enum { a } e = 1; is ensured for the particular value of 1, a combination of different reasons, based on the definition of enum types, conversion rules of compatible types, and compatibility guarantees.

Spoiler alert: it's much more complex in C18 than the quote of any single paragraph (see my comment under the accepted answer).

Full explanation

I'll quote the C18 standard step by step (N2176 to be precise). Highlights are from me.

First, we know that enum { a } is an integer type:

6.2.5. Types
16: An enumeration comprises a set of named integer constant values. Each distinct enumeration constitutes a different enumerated type.
17: The type char, the signed and unsigned integer types, and the enumerated types are collectively called integer types.

Then, we can demonstrate that if enum { a } is an integer type "large enough" to hold 1, the assignment would valid (because there is no explicit statement of the contrary):

6.2.7 Compatible types and composite types
1:Two types have compatible type if their types are the same. (...)

6.3. Conversions
2: Unless explicitly stated otherwise, conversion of an operand value to a compatible type causes no change to the value or the representation.

6.7.2.2 Enumeration specifiers
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.

But is the type large enough? From here we deduct that the value of a is 0. Since it's also the largest enum constant in the set, we can deduct that enum { a } is "at least" compatible with char:

6.7.2.2 Enumeration specifiers
3: (...) An enumerator with = defines its enumeration constant as the value of the constant expression. If the first enumerator has no =, the value of its enumeration constant is 0. Each subsequent enumerator with no = defines its enumeration constant as the value of the constant expression obtained by adding 1 to the value of the previous enumeration constant.

In conclusion, the assignment is legal, because the litteral 1 is also compatible with char.

Beware of implementation dependent assumptions!

But be careful, such an integer assignment is not necessarily valid for whatever integer you choose:

enum { a } = INT_MAX;  // Implementation dependent, see J.3.9 
enum { a, b=INT_MAX } = 1024;  // ok: b requires int as compatible type  

In the first case, the enum is only guaranteed to be compatible with char. But we have no guarantee that it's compatible with an int:

J.3. Implementation-defined behavior
(...) **J.3.9. Structures, unions, enumerations, and bit-fields
(...)

  • The integer type compatible with each enumerated type (6.7.2.2).

In the second case, we used specification of an inteer value to force a compatible type with int.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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