Menu

Enum in C: Named Constants, Auto-Numbering, and Enum to String

How to define an enum in C - automatic numbering, explicit values, using enums in a switch so the compiler catches missing cases, converting an enum to a string, and the naming conventions that keep them readable.

This page includes runnable editors - edit, run, and see output instantly.

Code full of bare numbers is code nobody can read. if (state == 2) tells you nothing; if (state == STATE_RUNNING) tells you everything. An enum is C's way of giving a small, fixed set of integers proper names - and unlike a pile of #defines, they come grouped into one type the compiler knows about.

Defining an Enum

The declaration names a tag and lists the enumerators inside braces:

Two things to note straight away. First, numbering starts at 0 and increases by one, so RED is 0, GREEN is 1, BLUE is 2. Second, the enumerator names are visible everywhere in the enclosing scope - you write GREEN, not Color.GREEN. They live in the same namespace as your variables, which is why C code conventionally SHOUTS them or prefixes them by type (COLOR_RED) to avoid collisions.

Printing an enum uses %d, because an enumeration value is an integer value.

Explicit Values

Assign any enumerator an explicit constant, and the ones after it continue from there:

Values do not have to be unique or ordered - two enumerators may share a number, which is occasionally useful for aliases (COLOR_DEFAULT = COLOR_BLACK). They must be compile-time integer constants, so you cannot compute one from a variable.

A common idiom uses this to record the count:

enum Suit {
    CLUBS, DIAMONDS, HEARTS, SPADES,
    SUIT_COUNT           /* automatically 4 - stays correct if you add a suit */
};

SUIT_COUNT is not a real suit; it is the number of them, maintained for free by the auto-numbering. Loops and array sizes can use it and never fall out of date.

typedef enum

As with structs, the full type name in C is enum Color, and a typedef drops the keyword:

Because enumerators are ordinary integers starting at 0, they double as array indices - dx[DIR_EAST] works without any conversion. That is the single most useful property of a zero-based enum.

Enums in a switch

Pairing an enum with a switch is where the type starts paying for itself. Compile with -Wall and the compiler will tell you when you forget a case.

Add a STATE_FAILED to the enum and GCC warns: enumeration value 'STATE_FAILED' not handled in switch. That warning is the closest thing C has to an exhaustiveness check, and it is a strong reason to write these switches without a default: case - a default silences the warning and lets new states slip through unhandled. Put the fallback after the switch instead, as describe does.

Converting an Enum to a String

C keeps no names at runtime; printf("%s", GREEN) cannot work because GREEN is simply 1. Two patterns solve it.

The switch version, which the compiler checks:

And the lookup table version, shorter but unchecked - if the array and the enum drift apart, nothing warns:

The designated array indices ([RED] = "RED") at least keep the names attached to their slots, so reordering the enum cannot silently shuffle the strings. The bounds check matters because nothing stops a caller passing a value outside the enum - see the next section.

Enums Are Not Airtight

An enum variable is an integer underneath, and C does not police the range:

Arithmetic on enumerators works, comparisons with plain ints work, and an out-of-range cast is accepted. So treat an enum as excellent documentation and a good compiler hint, not as a guarantee. Validate any value that arrives from outside your program - a file, an argument, a network message - before switching on it.

Size and Storage

In C17, every enumerator must fit in an int - enum Huge { BIG = 3000000000 }; is a constraint violation that a strict compiler (-pedantic-errors) rejects, even though gcc and clang accept it as an extension in their default mode. The enumerators themselves have type int, while the enum type's size is implementation-defined (in practice, int):

(C23 lifts the restriction: an enumerator may exceed int range and you can even pick the underlying type with enum E : unsigned long { ... }. Until your toolchain targets C23, keep enumerators inside int.)

Because the exact choice is implementation-defined, never write an enum directly into a binary file or a network packet and expect another machine to read it back. Convert to a fixed-width type such as uint8_t or uint32_t from <stdint.h> at the boundary.

Enum vs #define

Both give a name to a number. The enum is generally better:

enum#define
Handled bythe compilerthe preprocessor
Groupingrelated values share a typeeach constant is independent
Auto-numberingyesno, you number by hand
Visible in a debuggeroften, by nameno, only the number
switch exhaustiveness warningyesno
Scoperespects block scopetextual, until #undef

Reach for #define when the constant is not an integer (a string, a float) or when it must be usable by the preprocessor itself, as in conditional compilation. For a set of related integer states, flags, or kinds, use an enum.

Naming Conventions

Enums collide easily because their names sit in the ordinary scope. The conventions that keep large C codebases sane:

  • Prefix each enumerator with the type: COLOR_RED, STATE_IDLE, HTTP_OK. Unprefixed RED in a graphics program will eventually meet someone else's RED.
  • SHOUT_CASE the enumerators, matching how other constants look.
  • Name the type in singular PascalCase or snake_case_t: Color, State, or color_t, consistently across the project.
  • Add a _COUNT sentinel last when you will loop or index with the enum.

Frequently Asked Questions

How do you define an enum in C?

enum Color { RED, GREEN, BLUE }; defines a type whose values are the three named constants. By default they number from 0, so RED is 0, GREEN is 1, and BLUE is 2. Declare a variable with enum Color c = GREEN;, or add a typedef so you can write just Color c.

Can you assign specific values to an enum in C?

Yes: enum Status { OK = 200, NOT_FOUND = 404, ERROR = 500 };. You can set some and let others follow - any name without an explicit value continues from the one before it, so enum { A = 10, B, C }; gives B = 11 and C = 12.

How do you convert an enum to a string in C?

C has no built-in way; enumerator names do not exist at runtime. The usual approach is a small function with a switch returning a string literal per value, or an array of strings indexed by the enum. The switch version is safer because -Wall warns when a new enumerator has no case.

What is the size of an enum in C?

Implementation-defined, but in practice sizeof(int) - 4 bytes on most systems. The compiler picks an integer type able to hold every enumerator. Do not assume a specific size when laying out a binary file or network packet; use a fixed-width type from <stdint.h> there.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED