C Unions

A union in C is a custom data type whose members share the same memory. Unlike a structure, a union can meaningfully store only one member value at a time.

Unions are useful when a value may be one of several types, but never all of them at once. They save memory, but they also require careful tracking of which member currently holds valid data.

Union Syntax

Use the union keyword to define a union type. Its members are written much like structure members, and you access them with the dot operator.

#include <stdio.h>

union Number {
    int whole;
    double decimal;
};

int main(void)
{
    union Number value;

    value.whole = 25;
    printf("As int: %d\n", value.whole);

    value.decimal = 4.5;
    printf("As double: %.1f\n", value.decimal);

    return 0;
}

Output:

As int: 25
As double: 4.5

The variable value has two members: whole and decimal. Both members occupy the same storage area. After assigning value.decimal, the earlier integer value should no longer be treated as valid.

Shared Storage

A structure stores space for all of its members. A union stores enough space for its largest member. That is the main difference between them.

Type Memory idea Typical use
struct All members exist at the same time. A record, such as a student with a name and grade.
union Members share one storage area. A value that may be an int or a double, but not both at once.

Because union members overlap, reading a different member from the one most recently assigned can give a value that is not meaningful for your program. Beginners should avoid using unions for type conversion tricks.

Use A Tag With A Union

A union does not remember which member was last assigned. A common pattern is to store a separate tag beside the union. The tag tells the program which union member to read.

#include <stdio.h>

#define VALUE_INT 1
#define VALUE_DOUBLE 2

union Data {
    int whole;
    double decimal;
};

struct StoredValue {
    int type;
    union Data data;
};

void print_value(struct StoredValue value)
{
    if (value.type == VALUE_INT) {
        printf("Stored int: %d\n", value.data.whole);
    } else if (value.type == VALUE_DOUBLE) {
        printf("Stored double: %.2f\n", value.data.decimal);
    }
}

int main(void)
{
    struct StoredValue first = {VALUE_INT, {.whole = 42}};
    struct StoredValue second = {VALUE_DOUBLE, {.decimal = 3.75}};

    print_value(first);
    print_value(second);

    return 0;
}

Output:

Stored int: 42
Stored double: 3.75

Here, type is the tag. If type is VALUE_INT, the program reads data.whole. If type is VALUE_DOUBLE, it reads data.decimal. This keeps the union from being used blindly.

Designated Initializers

The syntax {.whole = 42} is a designated initializer. It says exactly which union member should receive the initial value. Without this syntax, an initializer for a union initializes its first member.

Designated initializers are especially helpful when a union has several members, because they make the intended active member clear.

Important Union Rules

  • Define a union type with union Name { ... };.
  • Use the dot operator, ., to access a member of a union variable.
  • All union members share the same memory.
  • A union variable should be treated as holding one valid member value at a time.
  • Use a separate tag, often inside a struct, when your program needs to know which member is active.

The key idea is that a union stores one of several possible values in the same memory space. Next, enums can make tags like VALUE_INT and VALUE_DOUBLE clearer and safer to read.