Skip to content
Laviyx Learn

Try

C course menu

Unions, Enums and typedef in C

Learn enumerations and unions in C: defining enum constants, using enums with switch, enum values as integers, unions that share memory between members, tagged unions for variant data, and good uses of typedef.

Intermediate8 min readUpdated
On this page

Enumerations give names to a set of related integer constants. Unions let several members share the same memory. Together with structs, they let you model data precisely.

Enumerations#

C
#include <stdio.h>

typedef enum { PENDING, PAID, SHIPPED, DELIVERED } OrderStatus;

const char *status_name(OrderStatus s) {
    switch (s) {
        case PENDING:   return "pending";
        case PAID:      return "paid";
        case SHIPPED:   return "shipped";
        case DELIVERED: return "delivered";
    }
    return "unknown";
}

int main(void) {
    OrderStatus s = PAID;
    printf("%s (%d)\n", status_name(s), s);
    s = DELIVERED;
    printf("%s (%d)\n", status_name(s), s);
    return 0;
}
Output
paid (1)
delivered (3)
  • Enum constants are integers, numbered from 0 by default.
  • With -Wall, GCC warns when a switch on an enum misses one of its values, which helps when you add a new status later.

You can choose the values yourself; unspecified ones continue from the previous value:

C
enum HttpStatus { OK = 200, CREATED, NOT_FOUND = 404, SERVER_ERROR = 500 };
printf("%d %d %d\n", OK, CREATED, NOT_FOUND);
Output
200 201 404

Enums are clearer than bare numbers and are visible in debuggers. C does not stop you from assigning any integer to an enum variable, so they are not fully type-safe.

Unions#

All members of a union share the same memory, so a union is only as big as its largest member, and only one member holds a meaningful value at a time:

C
#include <stdio.h>

union Value {
    int i;
    double d;
    char text[16];
};

int main(void) {
    union Value v;
    v.i = 42;
    printf("int: %d\n", v.i);
    v.d = 3.75;                    /* overwrites the same bytes */
    printf("double: %.2f\n", v.d);
    printf("size: %zu\n", sizeof v);
    return 0;
}
Output
int: 42
double: 3.75
size: 16

The size is 16 because text is the largest member. A struct with the same members would need room for all three at once.

Tagged unions#

A union does not remember which member is in use, so pair it with an enum tag in a struct. This is a common way to store "one of several kinds" of value:

C
#include <stdio.h>

typedef enum { SHAPE_CIRCLE, SHAPE_RECT } ShapeKind;

typedef struct {
    ShapeKind kind;
    union {
        struct { double radius; } circle;
        struct { double width, height; } rect;
    };                                   /* anonymous union (C11) */
} Shape;

double area(const Shape *s) {
    switch (s->kind) {
        case SHAPE_CIRCLE: return 3.14159 * s->circle.radius * s->circle.radius;
        case SHAPE_RECT:   return s->rect.width * s->rect.height;
    }
    return 0;
}

int main(void) {
    Shape shapes[] = {
        { .kind = SHAPE_CIRCLE, .circle = { 2.0 } },
        { .kind = SHAPE_RECT, .rect = { 3.0, 4.5 } },
    };
    for (int i = 0; i < 2; i++) printf("%.2f\n", area(&shapes[i]));
    return 0;
}
Output
12.57
13.50

Always check the tag before reading a member. Unions are also used to save memory in embedded systems and in interpreters that store values of different types.

typedef in practice#

typedef creates an alias for an existing type. Good uses:

  • Shorter names for structs, unions and enums: typedef struct Node Node;.
  • Readable function-pointer types: typedef void (*Handler)(int);.
  • Portable, meaningful names: the standard size_t and uint32_t are typedefs.

Avoid hiding pointers behind typedefs (typedef Node *NodePtr;): it makes it hard to see what is a pointer.

Practice#

Practice exercise

Define typedef enum { RED, YELLOW, GREEN } Light; and write Light next(Light l) that returns the next light in the cycle red → green → yellow → red. Print the names for six steps starting from RED.

Show solution
C
#include <stdio.h>

typedef enum { RED, YELLOW, GREEN } Light;

Light next(Light l) {
    switch (l) {
        case RED:    return GREEN;
        case GREEN:  return YELLOW;
        case YELLOW: return RED;
    }
    return RED;
}

int main(void) {
    const char *names[] = {"RED", "YELLOW", "GREEN"};
    Light l = RED;
    for (int i = 0; i < 6; i++) {
        printf("%s ", names[l]);
        l = next(l);
    }
    printf("\n");
    return 0;
}
Output
RED GREEN YELLOW RED GREEN YELLOW 

Key takeaway#

Finished this lesson?