Skip to content

Intermediate · Structs, unions, enums and files · topic 33 of 60 · 25 min

Unions, enums and memory layout

Explanation

An enum gives names to integer constants (enum Status { PENDING, PAID, FAILED }), making code readable and switch statements clear. A union lets several members share the same memory, so it is as big as its largest member and only one member is meaningful at a time; a tagged union adds an enum to record which. The compiler inserts padding inside structs for alignment, so field order changes sizeof.

enum

c
enum OrderStatus { PENDING, PAID, SHIPPED, DELIVERED, CANCELLED };   /* 0, 1, 2, 3, 4 */
enum Http { OK = 200, NOT_FOUND = 404, SERVER_ERROR = 500 };          /* explicit values */

enum OrderStatus s = PAID;
switch (s) {
    case PENDING:   puts("Waiting for payment"); break;
    case PAID:      puts("Packing your order"); break;
    case SHIPPED:   puts("On the way"); break;
    case DELIVERED: puts("Delivered"); break;
    case CANCELLED: puts("Cancelled"); break;
}
gcc -Wall warns if a switch on an enum misses a constant (-Wswitch).

Enum constants are just ints, so nothing stops s = 42; and printing s prints a number. Keep an array of names indexed by the enum if you need text: const char *status_name[] = {"PENDING", "PAID", …};.

union

c
union Value {
    int i;
    float f;
    char text[8];
};                                  /* sizeof(union Value) is 8: the largest member */

union Value v;
v.i = 42;
v.f = 3.5f;                         /* overwrites the same bytes: v.i is no longer meaningful */

A union saves memory when a variable holds one of several kinds of data at a time, for example a configuration value that can be a number OR a string, or a message from a device that has different formats. Reading a member other than the one last written gives the raw bytes reinterpreted; C allows it, but it is platform-specific.

Tagged unions

c
typedef enum { PAY_UPI, PAY_CARD, PAY_COD } PayKind;

typedef struct {
    PayKind kind;                    /* the tag: which member is valid */
    union {
        char vpa[50];                /* for UPI */
        struct { char last4[5]; char network[12]; } card;
    } as;
} Payment;

void describe(const Payment *p) {
    switch (p->kind) {
        case PAY_UPI:  printf("UPI %s\n", p->as.vpa); break;
        case PAY_CARD: printf("%s card ending %s\n", p->as.card.network, p->as.card.last4); break;
        case PAY_COD:  printf("Cash on delivery\n"); break;
    }
}

Padding and alignment

Processors read data fastest (and on some, only) at addresses that are multiples of the data's size: a 4-byte int at a multiple of 4. To keep members aligned, the compiler inserts unused padding bytes inside structs, and at the end so arrays of the struct stay aligned. So sizeof a struct can be larger than the sum of its members.

output
struct Bad  { char a; int b; char c; };   /* typical x86-64: 12 bytes */
offset: a@0, [3 padding], b@4..7, c@8, [3 padding] → 12

struct Good { int b; char a; char c; };   /* typical x86-64: 8 bytes */
offset: b@0..3, a@4, c@5, [2 padding] → 8
Ordering members from largest to smallest usually minimises padding.
Careful: Because of padding, never assume sizeof(struct) equals the sum of its fields, and never write structs directly to a file or network and expect another compiler, platform or version to read them identically (topic 5). Bit-fields (int flag : 1;) also have implementation-defined layout, so they are not portable for file formats or hardware registers either.
Think of it like this: Padding is like a parking lot where cars must start at marked bays: a scooter parked before a car leaves an empty gap so the car can start at the next bay line. Parking the cars first and the scooters together at the end wastes less space.
Tip: Use the offsetof macro from <stddef.h> and printf("%zu", sizeof(struct X)) to inspect layouts while learning; you can also use _Static_assert(sizeof(struct Header) == 16, "…") to catch unexpected layout changes at compile time.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Unions, enums and memory layout · 1/6

Enums, unions, layout

In this lesson you will use enums, unions, and see how structs are laid out in memory.

Prefer a video by a teacher? Find free videos on “c enum union structure padding alignment explained” on YouTube ↗

In real life: A smart-meter message

A smart electricity meter sends either a reading, an alarm or a firmware version. The receiving software stores each message in a tagged union, saving memory on a small gateway device, and switches on the tag to process it.

c
typedef enum { MSG_READING, MSG_ALARM, MSG_VERSION } MsgKind;
typedef struct {
    MsgKind kind;
    union {
        struct { uint32_t meter_id; uint32_t units_wh; } reading;
        uint16_t alarm_code;
        char version[12];
    } body;
} MeterMsg;

Practice (0/3 done)

Task 1 · Question

enum Level { LOW, MEDIUM = 5, HIGH }; What is HIGH?

Task 2 · Question

On a typical x86-64 system, sizeof(struct { char a; double d; char b; }) is…

Task 3 · Practice

Model a payment in C as a tagged union (UPI with a VPA string, card with last 4 digits and network, cash on delivery with nothing extra). Write describe(const Payment *p) with a switch on the tag, create one of each kind, and print sizeof(Payment). Then reorder a padded struct of your own to make it smaller and print both sizes.

Quick quiz

Question 1

1.What is the size of a union?

Question 2

2.What are enum constants in C?

Question 3

3.Why does the compiler add padding to structs?

Question 4

4.What is a tagged union?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What is the size of a union?

Answer: At least the size of its largest member

Members overlap (plus possible padding).

What are enum constants in C?

Answer: Named int constants

They behave like ints.

Why does the compiler add padding to structs?

Answer: To keep members aligned for fast (or legal) access

Alignment.

What is a tagged union?

Answer: A struct with an enum saying which union member is valid

The tag makes it safe to use.

let status: "pending" | "paid" | "failed" = "pending"; Which assignment is an error?Related

Answer: status = "refunded";

"refunded" is not one of the three allowed literal values.

From Unions and narrowing

What makes a union "discriminated"?Related

Answer: Every member has a common field with a different literal value

The shared literal field lets TypeScript narrow precisely.

From Object types, interfaces and unions

Which is like Java's static members?Related

Answer: companion object

Companion object members are accessed through the class name.

From Data, enum and sealed classes, and objects

What can an enum variant hold in Rust?Related

Answer: Any data: none, a tuple of values, or named fields

Rust variants can be unit-like, tuple-like or struct-like.

From Enums and Option

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 2 more topics in “Structs, unions, enums and files” before its exam: Reading and writing text files, Binary files and records.
  2. Module 7 exam: Structs, unions, enums and files · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, The preprocessor, multi-file programs and make. 60 topics left in the course before the final exam.

← Previous: Pointers to structs and the arrow operator