enum declares a set of named integer constants, each auto-assigned a sequential int value starting at 0 unless given an explicit value. This is where the resemblance to enums in Java, Rust, or TypeScript ends: a C enum variable is really just an int underneath, with no compiler-enforced restriction to the named values at all — assigning an arbitrary integer, or a value from a completely different enum, compiles without warning under default settings.
Declaring and using an enum
Values default to 0, 1, 2... in declaration order; explicit values can be given for some or all members, and any not given one continues counting up from the previous explicit value.
enum Color { RED, GREEN, BLUE }; // RED=0, GREEN=1, BLUE=2 automatically
enum Color favorite = GREEN;
printf("%d\n", favorite); // 1 — an enum value IS just an int
enum Status { OK = 200, NOT_FOUND = 404, ERROR = 500 }; // explicit valuesThe real gap: no type safety at all
This compiles cleanly under C's default settings — there is no compile error, no warning by default, nothing stopping an enum variable from holding a value that was never one of its named constants.
enum Color { RED, GREEN, BLUE };
enum Color c = 9999; // compiles fine — 9999 is not RED, GREEN, or BLUE, but C doesn't care
enum Color c2 = (enum Color)42; // also compiles, also nonsensicalA common pattern: enums for readable switch cases
Despite the weak type safety, enums are still genuinely useful for making a switch statement's cases self-documenting, compared to raw magic numbers scattered through the code.
enum Direction { NORTH, SOUTH, EAST, WEST };
void move(enum Direction dir) {
switch (dir) {
case NORTH: printf("Moving north\n"); break;
case SOUTH: printf("Moving south\n"); break;
case EAST: printf("Moving east\n"); break;
case WEST: printf("Moving west\n"); break;
}
}