Question bank — Enumerations
Before you start, recall the anchor fact that dissolves half these traps: an enum's values are stored in some implementation-chosen integer type — one the compiler picks that can hold every enumerator's value (it may be int, or a wider/narrower signed or unsigned integer type; the standard does not guarantee int). The enumerators themselves, though, have type int when used in expressions. Either way, at runtime nothing exists but an integer; the names are compile-time labels. See Integer Types in C for what "an integer type" means — its signedness and size are platform-dependent, which matters for portability.
The number line below is the mental model: names are just sticky-notes on integer positions, and the counting rule walks rightward by one.

True or false — justify
printf("%d", RED) for enum { RED, GREEN } prints the word "RED".
0. There is no name-lookup table like Python's repr; to print names you build your own const char* names[].An enum introduces a brand-new type that the compiler refuses to mix with plain int.
int, so enum Color c = 99; compiles with no error. The "type" is documentation, not enforcement — that safety exists in C++, not C.Two enumerators in the same enum are allowed to hold the same integer value.
enum { WARN = 5, FATAL = 5 } is perfectly legal because two sticky-notes can label the same locker.You must write Color.RED to reach an enumerator, like a struct field.
RED. This is exactly why two enums in one scope cannot reuse a name — unlike a struct in C, whose members are reached as s.field and are scoped inside the struct.Once you assign an explicit value, all later enumerators must also be explicit.
A = 5 an unassigned B becomes 6. Explicit values only restart the count, they don't disable it.An enum constant can be used as a case label in a switch statement.
switch's requirement that each case be a constant expression — which is exactly why enums and switch pair so naturally.Enum values are stored as strings so they take more memory than ints.
enum Weekday { MON, TUE }; uses the same memory whether you write MON or 0.
MON is 0 after compilation; they are byte-for-byte identical at runtime. The name only changes what a human reading the source sees.The field in struct S { int x; } and the RED in enum { RED } live in the same kind of scope.
x is a member, reached only via s.x; the enum's RED is a top-level identifier reachable bare. That difference is the whole reason two enums can't share a name but two structs can both have a member x.Spot the error
enum Color { RED, GREEN, BLUE } then if (c == "RED") — what's wrong?
c is an integer and "RED" is a string literal (a char* address), so you're comparing an int against a pointer — meaningless, and modern compilers warn ("comparison between pointer and integer"), which becomes a hard error under -Werror. You meant if (c == RED), comparing the int against 0.enum A { X }; enum B { X }; in the same file — why does this fail?
X in the surrounding scope, and a scope cannot hold two things with the same name. Enumerators are not namespaced by their enum, so the second X collides.switch(c) { case RED: puts("stop"); case GREEN: puts("go"); } — bug?
break after case RED causes fall-through: matching RED prints both "stop" and "go". Every intentional case in a switch normally needs its own break.enum E { A = 1, B, A = 4 }; — legal?
4, but because the name A is declared twice. Duplicate values are fine; duplicate names are the actual violation.typedef enum { RED, GREEN } Color; enum Color c; — what's wrong?
enum), so enum Color names no type at all — that's a hard compile error, not merely redundant. The typedef already made Color the type name, so you must write Color c; with no enum keyword.const char* name = RED; where RED is an enumerator — what breaks?
RED is the integer 0, so this assigns the null pointer to name (integer 0 converting to a pointer). You wanted a lookup array names[RED], not RED itself.Why questions
Why does the first un-initialized enumerator get 0 and not 1?
0, matching C's zero-based habit (like array indices). Everything after follows from the single recurrence "previous + 1".Why are duplicate enum values allowed but duplicate names forbidden?
Why prefer an enum over a #define for a set of related constants?
switch warnings; #define is raw text substitution with none of that structure. The enum documents "these belong together."Why does the trailing-COUNT trick automatically equal the number of items?
0, an item at position n has value n, so the enumerator placed after the last real item lands on the count exactly. Add another item before it and the recurrence bumps COUNT up for free — useful for sizing Arrays in C.Why can't C tell you if you assigned a nonsense value like 99 to an enum Color?
99 is a valid integer. C deliberately trades type-safety for simplicity and speed here — the burden of "is this a real color?" is on you.Why does printf("%d", GREEN) work at all if enums are "a type"?
int in expressions, and %d expects exactly an int — the match is trivial and lossless. If enums were truly strict types, this would need a cast; in C it does not.Edge cases
enum { A = -3, B, C }; — what are B and C?
B = -2 and C = -1. Negative explicit values are legal, and the "+1 over previous" rule still applies, walking upward from -3.enum { A = 5, B = 5, C }; — what is C?
C = 6. C is previous+1 from B's value 5, and the fact that A and B collide on 5 is irrelevant to the counting.enum Empty { }; — is an enum with no enumerators allowed?
enum { A = 2147483647, B }; where the chosen type is 32-bit signed — problem?
B would be A + 1, which overflows a signed 32-bit integer, and signed integer overflow in C is strictly undefined behavior (not merely implementation-defined). The recurrence is blind to limits, so you must keep values within the type's range — see Integer Types in C.enum Color c; declared but never assigned — what value does it hold?
RED. Enums get no special zero-init beyond the normal rules for their storage duration.Can an enumerator's explicit value be another enumerator, like { A = 1, B = A }?
B = A sets B to 1, and a later unassigned C becomes 2. You can build values out of earlier ones as long as they stay compile-time constants.Recall One-line summary to lock in
Enums are self-documenting integer labels: names must be unique, values need not be; the enumerators are int-typed constants stored in an implementation-chosen integer type; and C enforces almost no type safety — treat every trap above as a consequence of those facts.
The trap-killer sentence ::: "It's an integer constant with a nice name, counted from 0 by +1, restartable, and not type-checked."