Enumerations
An enum defines a set of named integer constants. Use one whenever a value can only be one of a fixed list of choices — days, states, error codes, colours — so your code says STATUS_SHIPPED instead of a mysterious 3.
Defining an enum
#include <stdio.h> enum order_status { STATUS_PENDING, // 0 STATUS_PAID, // 1 STATUS_SHIPPED, // 2 STATUS_DELIVERED // 3 }; int main(void) { enum order_status s = STATUS_PAID; printf("status code %d\n", s); if (s == STATUS_PAID) { printf("ready to ship\n"); } s = STATUS_SHIPPED; printf("now %d\n", s); return 0; }
status code 1 ready to ship now 2
Constants are numbered from 0 unless you say otherwise. Enum constants share one namespace with all other identifiers, so prefixing them (STATUS_…) avoids clashes.
Choosing values
#include <stdio.h> enum http_status { HTTP_OK = 200, HTTP_CREATED, HTTP_NOT_FOUND = 404, HTTP_SERVER_ERROR = 500 }; enum level { LOW = 1, MEDIUM = 5, HIGH = 10 }; int main(void) { printf("%d %d %d %d\n", HTTP_OK, HTTP_CREATED, HTTP_NOT_FOUND, HTTP_SERVER_ERROR); printf("%d %d %d\n", LOW, MEDIUM, HIGH); return 0; }
200 201 404 500 1 5 10
A constant without a value is one more than the previous one, so HTTP_CREATED is 201.
Names for printing
Enums are numbers, so printing gives a number. A parallel array of strings gives readable names — with a count constant at the end to keep them in step:
#include <stdio.h> enum colour { RED, GREEN, BLUE, COLOUR_COUNT }; static const char *const colour_names[COLOUR_COUNT] = { [RED] = "red", [GREEN] = "green", [BLUE] = "blue", }; int main(void) { for (enum colour c = RED; c < COLOUR_COUNT; c++) { printf("%d = %s\n", c, colour_names[c]); } return 0; }
0 = red 1 = green 2 = blue
Designated initialisers ([RED] = "red") tie each name to its constant, so reordering the enum can't silently mismatch them.
enums and switch
Handling every enum value in a switch without a default lets the compiler warn you when a new value is added but not handled (lesson 17):
#include <stdio.h> enum direction { NORTH, EAST, SOUTH, WEST }; enum direction turn_right(enum direction d) { switch (d) { case NORTH: return EAST; case EAST: return SOUTH; case SOUTH: return WEST; case WEST: return NORTH; } return d; } int main(void) { const char *names[] = { "north", "east", "south", "west" }; enum direction facing = NORTH; for (int i = 0; i < 5; i++) { printf("%s ", names[facing]); facing = turn_right(facing); } printf("\n"); return 0; }
north east south west north
Flags with enums
Powers of two make enum constants combinable with |, as in lesson 14:
#include <stdio.h> enum text_style { STYLE_BOLD = 1 << 0, STYLE_ITALIC = 1 << 1, STYLE_UNDERLINE = 1 << 2, }; int main(void) { unsigned style = STYLE_BOLD | STYLE_UNDERLINE; printf("bold:%d italic:%d underline:%d\n", !!(style & STYLE_BOLD), !!(style & STYLE_ITALIC), !!(style & STYLE_UNDERLINE)); return 0; }
bold:1 italic:0 underline:1
What enums don't do
C enums are weakly typed: an enum variable is really an integer, and you can assign any integer to it without complaint. They don't stop invalid values, so validate data coming from outside (files, input) before converting it. C23 adds the ability to choose the underlying type, as in enum small : unsigned char { … };.
Try it yourself
Model a traffic light as an enum and step through its cycle, printing how long each light lasts.
t= 0s red for 30 s t= 30s green for 25 s t= 55s amber for 4 s t= 59s red for 30 s t= 89s green for 25 s t=114s amber for 4 s