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
.
enum
s are arithmetic types, integer constant are arithmetic types so you're good as long as you don't cause overflow.clang -Weverything ...
switch
statements that have acase
for each enumerated value but nodefault
.