Skip to content

Chapter 13 of 21

Structures and Unions

Model records, nested data, shared storage, padding, and tagged unions.

30 minutes 10 quick checksBy Subha Prasad
Lesson 13 of 21Course navigation

Lesson content

Read, practise, then check your understanding

A structure groups named members that coexist. A union overlays its members in the same storage. Both create aggregate types, but their valid-use rules differ.

Structures

struct Point {
    double x;
    double y;
};

struct Rectangle {
    struct Point origin;
    double width;
    double height;
};

struct Point p = {.x = 2.0, .y = 4.5};

Designated initializers name members and remain clear when order changes. Use object.member for an object and pointer->member through a pointer.

Structures can be assigned, passed, and returned by value; the member values, including embedded arrays, are copied. Passing a pointer can avoid large copies and permit mutation.

Layout and padding

Members appear in declaration order, but the implementation may insert padding for alignment. Therefore sizeof(struct Record) may exceed the sum of member sizes. Do not serialize a structure by dumping raw bytes: padding, byte order, type widths, and representation vary. Serialize fields into a specified format.

Reordering members from stricter to looser alignment can reduce space, but public binary layouts and clarity may matter more. Use offsetof from <stddef.h> when a legitimate low-level interface needs member offsets.

Unions

union Value {
    long integer;
    double real;
    const char *text;
};

The union is large enough and aligned for its largest member. Writing one member makes that representation active; reading a different member is constrained and often implementation-defined or invalid for the intended program.

Tagged unions

Store an enum tag alongside a union so code knows which member is valid:

enum ValueKind { VALUE_INTEGER, VALUE_REAL, VALUE_TEXT };

struct TaggedValue {
    enum ValueKind kind;
    union Value value;
};

void print_value(const struct TaggedValue *item) {
    switch (item->kind) {
        case VALUE_INTEGER: printf("%ld", item->value.integer); break;
        case VALUE_REAL: printf("%g", item->value.real); break;
        case VALUE_TEXT: printf("%s", item->value.text); break;
    }
}

Define ownership for pointer members and keep tag updates synchronized with the payload.

Knowledge check

Answer every question correctly to complete this chapter.

Which operator accesses a member through a pointer to a structure?
How do union members use storage?
Which syntax names members during initialization?
Can structure objects of the same compatible type be assigned?
Why can sizeof(struct S) exceed the sum of member sizes?
Why is dumping raw struct bytes a poor portable file format?
What determines a union’s required storage?
What makes a union safely discriminated?
What does offsetof from <stddef.h> report?
Who must define ownership when a structure contains pointers?

0 of 10 checks passed

Your progress is saved on this device.