Why does this:
#include <stdio.h> #include <limits.h> #include <inttypes.h> int main() { enum en_e { en_e_foo, en_e_bar = UINT64_MAX, }; enum en_e e = en_e_foo; printf("%zu\n", sizeof en_e_foo); printf("%zu\n", sizeof en_e_bar); printf("%zu\n", sizeof e); }
print 4 8 8
in C and 8 8 8
in C++ (on a platform with 4 byte ints)?
I was under the impression that the UINT64_MAX
assignment would force all the enumerations constants to at least 64 bits, but en_e_foo
remains at 32 in plain C.
What is the rationale for the discrepancy?
Enums impose a specific type onto your constants: they will have type int (or, possibly, larger signed integer type), and they will always be signed. With macros you can use constant syntax, suffixes and/or explicit type conversions to produce a constant of any type.
Enumeration or Enum in C is a special kind of data type defined by the user. It consists of constant integrals or integers that are given names by a user. The use of enum in C to name the integer values makes the entire program easy to learn, understand, and maintain by the same or even different programmer.
An enumeration type declaration gives the name of the (optional) enumeration tag. And, it defines the set of named integer identifiers (called the enumeration set, enumerator constants, enumerators, or members). A variable of the enumeration type stores one of the values of the enumeration set defined by that type.
In C, an enum
constant is of type int
. In C++, it's of the enumerated type.
enum en_e{ en_e_foo, en_e_bar=UINT64_MAX, };
In C, this is a constraint violation, requiring a diagnostic (if UINT64_MAX
exceeds INT_MAX
, which it very probably does). A C compiler may reject the program altogether, or it may print a warning and then generate an executable whose behavior is undefined. (It's not 100% clear that a program that violates a constraint necessarily has undefined behavior, but in this case the standard doesn't say what the behavior is, so that's still undefined behavior.)
gcc 6.2 doesn't warn about this. clang does. This is a bug in gcc; it incorrectly inhibits some diagnostic messages when macros from standard headers are used. Thanks to Grzegorz Szpetkowski for locating the bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71613
In C++, each enumeration type has an underlying type, which is some integer type (not necessarily int
). This underlying type must be able to represent all the constant values. So in this case, both en_e_foo
and en_e_bar
are of type en_e
, which must be at least 64 bits wide, even if int
is narrower.
That code just isn't valid C in the first place.
Section 6.7.2.2 in both C99 and C11 says that:
Constraints:
The expression that defines the value of an enumeration constant shall be an integer constant expression that has a value representable as an
int
.
A compiler diagnostic is mandatory because it is a constraint violation, see 5.1.1.3:
A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint, even if the behavior is also explicitly specified as undefined or implementation-defined.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With