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.
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#
#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;
}paid (1)
delivered (3)- Enum constants are integers, numbered from 0 by default.
- With
-Wall, GCC warns when aswitchon 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:
enum HttpStatus { OK = 200, CREATED, NOT_FOUND = 404, SERVER_ERROR = 500 };
printf("%d %d %d\n", OK, CREATED, NOT_FOUND);200 201 404Enums 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:
#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;
}int: 42
double: 3.75
size: 16The 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:
#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;
}12.57
13.50Always 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_tanduint32_tare 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
#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;
}RED GREEN YELLOW RED GREEN YELLOW Key takeaway#
Finished this lesson?