Understanding Unions and Enums in C

Unions and enums are two essential user-defined types in C that provide memory efficiency and code clarity, respectively.

Union Declaration and Memory Layout

A union groups multiple variables of different types into a single memory location. The compiler allocates enough memory to hold the largest member. All member share the same starting address, meaning writing to one member overwrites the others.

#include <stdio.h>

union Data {
    char c;
    int i;
};

int main() {
    union Data d = {0};
    printf("%zu\n", sizeof(d)); // Output: 4 (size of int on most systems)
    return 0;
}

Shared Memory Behavior

All members of a union occupy the same memory address:

#include <stdio.h>

union Data {
    char c;
    int i;
};

int main() {
    union Data d;
    printf("%p\n", &d);     // Base address
    printf("%p\n", &d.c);   // Same as base
    printf("%p\n", &d.i);   // Same as base
    return 0;
}

Modifying one member affects the interpretation of others:

#include <stdio.h>

union Data {
    char c;
    int i;
};

int main() {
    union Data d;
    d.i = 0x11223344;
    d.c = 0x55;
    printf("%#x\n", d.i); // Output depends on endianness
    return 0;
}

Size Calculation Rules for Unions

  1. The size is at least the size of the largest member.
  2. The size is rounded up to the nearest multiple of the largest alignment requirement among its members.
#include <stdio.h>

union U1 {
    char arr[5];  // 5 bytes
    int x;        // 4 bytes, aligned to 4
};

union U2 {
    short arr[7]; // 14 bytes
    int x;        // 4 bytes, aligned to 4
};

int main() {
    printf("%zu\n", sizeof(union U1)); // 8 (aligned to 4-byte boundary)
    printf("%zu\n", sizeof(union U2)); // 16 (14 rounded up to next 4-byte multiple)
    return 0;
}

Practical Use: Memory-Efficient Data Structures

Unions help reduce memory usage when only one variant of data is active at a time. For example, in a gift inventory system:

struct Gift {
    int stock;
    double price;
    int type; // Discriminator

    union {
        struct {
            char title[20];
            char author[20];
            int pages;
        } book;

        struct {
            char design[30];
        } mug;

        struct {
            char design[30];
            int colors;
            int sizes;
        } shirt;
    } details;
};

Endianness Detection Using Union

A common trick uses a union to inspect byte order:

int is_little_endian(void) {
    union {
        int i;
        char c;
    } u;
    u.i = 1;
    return u.c == 1; // Returns 1 if little-endian
}

Enum Declaration

Enumerations define a set of named integer constants:

enum Weekday {
    MON, TUE, WED, THU, FRI, SAT, SUN
};

enum Color {
    RED = 2,
    GREEN = 4,
    BLUE = 8
};

By default, enumerators start at 0 and increment by 1. Explicit values can be assigned.

Advantages of Enums Over Macros

  • Better readability and maintainability
  • Type safety (compared to #define)
  • Debuggers can display symbolic names
  • Scoped to their declaration context
  • Define multiple related constants in one statement

Using Enum Variables

enum Color {
    RED = 1,
    GREEN = 2,
    BLUE = 4
};

int main() {
    enum Color c = GREEN; // Valid
    c = 3;                // Allowed in C (not in C++)
    return 0;
}

Practice Problems

Problem 1: What is the size of the following union?

union Example {
    short s[7]; // 14 bytes
    int n;      // 4 bytes
};

Answer: 16 bytes (14 rounded up to the next 4-byte boundary due to int alignment).

Problem 2: Predict the output:

#include <stdio.h>

int main() {
    union {
        short k;
        char i[2];
    } *ptr, var;

    ptr = &var;
    ptr->i[0] = 0x39;
    ptr->i[1] = 0x38;
    printf("%x\n", var.k);
    return 0;
}

On a little-endian machine, the output is 3839 because the least significant byte (0x39) is stored at the lower address.

Tags: C UNION Enum data-structures memory-layout

Posted on Tue, 07 Jul 2026 17:34:17 +0000 by Leveecius