C Unions

A union in C is a user-defined type whose members occupy the same storage. A union can describe several possible representations of a value, but a program normally treats only the member most recently written as the current value.

A structure reserves storage for all its members, while a union reserves enough storage for its largest member, including any alignment-related padding. This shared-memory behavior makes unions suitable for variant data, hardware registers, protocol fields, and other cases in which alternatives are mutually exclusive.

C Union Syntax and Declaration

Use the union keyword followed by an optional tag and a member list. The declaration defines a type; it does not create an object unless a variable is also declared.

</>
Copy
union union_name {
    datatype member1;
    datatype member2;
    // additional members...
};

The following declaration defines union Value and then creates a variable named data:

</>
Copy
union Value {
    int whole_number;
    float decimal_number;
    char text[20];
};

union Value data;

Members are accessed with the dot operator for a union object and the arrow operator for a pointer to a union.

</>
Copy
data.whole_number = 25;

union Value *ptr = &data;
ptr->decimal_number = 4.5f;

How C Unions Share Memory

All members begin at the same address. Writing a member stores its representation in the union’s shared memory. A later write through another member reuses that memory and replaces some or all of the previous representation.

  • Shared storage: The members do not receive independent blocks of memory.
  • Largest-member capacity: The union is large enough and suitably aligned for any of its members.
  • One intended interpretation: A program should track which member currently represents the stored value.
  • Overwriting behavior: Assigning one member replaces the shared representation previously written through another member.
  • No automatic type tracking: A plain union does not record which member was written last.
  • Possible padding: The result of sizeof can exceed the size of the largest member because the implementation must satisfy alignment requirements.

Reading a different member from the one most recently written interprets the stored bytes as another type. The result can depend on the C implementation and the involved types, and some representations can be invalid. Do not use an inactive member as a general conversion mechanism. Use an explicit cast for numeric conversion and memcpy when bytes must be copied into another object representation.

Checking C Union Size and Member Addresses

This program prints the union size and the addresses of its members. The addresses compare equal because every member starts at the beginning of the union.

</>
Copy
#include <stdio.h>

union Storage {
    char character;
    int number;
    double measurement;
};

int main(void) {
    union Storage item;

    printf("Union size: %zu\n", sizeof item);
    printf("Union address: %p\n", (void *)&item);
    printf("character address: %p\n", (void *)&item.character);
    printf("number address: %p\n", (void *)&item.number);
    printf("measurement address: %p\n", (void *)&item.measurement);

    return 0;
}

The numerical addresses and exact size vary by compiler, target architecture, and ABI. The program should therefore demonstrate the relationship between the addresses rather than be expected to produce one universal output.

C Structure vs Union

CharacteristicStructureUnion
Member storageEach member has its own storageAll members share storage
Simultaneous valuesAll members can represent values at onceNormally one member represents the current value
Minimum required capacityMust accommodate every member plus alignment paddingMust accommodate its largest member plus any required padding
Effect of assigning a memberOther members retain their valuesThe shared representation is replaced
Typical purposeGroup fields that belong togetherRepresent one of several alternatives
Member accessDot or arrow operatorDot or arrow operator

Example 1: Basic C Union vs Structure

This example compares two integer members in a structure with two integer members in a union. The structure retains both values independently. The union members begin at the same location and have the same type, so the program displays the integer representation most recently stored in the shared memory.

C Program

</>
Copy
#include <stdio.h>

struct test1 {
    int x, y;
};

union test {
    int x, y;
};

int main() {
    // Structure: both members have their own memory
    struct test1 t1 = {1, 2};

    // Union: both members share the same memory
    union test t;
    t.x = 3;  // t.y also becomes 3 because they share the same memory
    printf("After assigning x in union: %d %d\n", t.x, t.y);

    t.y = 4;  // Now t.x becomes 4 as well
    printf("After assigning y in union: %d %d\n", t.x, t.y);

    printf("Structure values: %d %d\n", t1.x, t1.y);
    return 0;
}

Explanation:

  1. struct test1 reserves independent storage for x and y.
  2. union test places x and y in the same storage.
  3. Assigning t.x stores the representation of 3 in the shared memory.
  4. Assigning t.y replaces that representation with the representation of 4.
  5. The members of t1 remain 1 and 2 because they do not share storage.

Output:

After assigning x in union: 3 3
After assigning y in union: 4 4
Structure values: 1 2

Initializing a Union in C

Without a designator, a union initializer initializes its first named member. A designated initializer can select another member explicitly.

</>
Copy
union Reading {
    int count;
    double temperature;
};

union Reading first = { 12 };                 /* initializes count */
union Reading second = { .temperature = 23.5 }; /* initializes temperature */

After initialization, the program must use the member selected by the initializer unless it subsequently assigns another member.

Typedef Union in C

A typedef can provide a shorter name for a union type. It does not change the union’s shared-memory behavior.

</>
Copy
typedef union {
    long order_id;
    char reference[16];
} OrderKey;

int main(void) {
    OrderKey key = { .order_id = 1042 };
    return key.order_id == 1042 ? 0 : 1;
}

This style is convenient when the type is used repeatedly. A tagged declaration such as typedef union OrderKey { ... } OrderKey; can be used when both a union tag and a typedef name are useful.

Tagged Union in C for Tracking the Active Member

A plain C union does not remember which member contains the intended value. A tagged union pairs the union with an enumeration that records the selected alternative. This is also called a discriminated union or variant record.

</>
Copy
#include <stdio.h>

#define LABEL_CAPACITY 24

enum ValueKind {
    VALUE_INTEGER,
    VALUE_DECIMAL,
    VALUE_LABEL
};

struct Value {
    enum ValueKind kind;
    union {
        int integer;
        double decimal;
        char label[LABEL_CAPACITY];
    } data;
};

void print_value(const struct Value *value) {
    switch (value->kind) {
        case VALUE_INTEGER:
            printf("%d\n", value->data.integer);
            break;
        case VALUE_DECIMAL:
            printf("%.2f\n", value->data.decimal);
            break;
        case VALUE_LABEL:
            printf("%s\n", value->data.label);
            break;
    }
}

int main(void) {
    struct Value price = {
        .kind = VALUE_DECIMAL,
        .data.decimal = 149.50
    };

    print_value(&price);
    return 0;
}

The tag and union member must be updated together. If kind says VALUE_DECIMAL, the program must have stored and should read data.decimal. Keeping this invariant makes variant data easier to validate and maintain.

Anonymous Union Inside a C Structure

An anonymous union has no member name. Its fields can be accessed directly through the enclosing structure or union. Anonymous structures and unions are standardized in C11, although some compilers supported them earlier as extensions. Check the selected language mode when portability to older C implementations is required.

Example 2: Using a Union Inside a Structure

The following program embeds an anonymous union in struct student. Either the name or the roll number is stored in the shared area, while mark has separate storage and remains available in both cases.

C Program

</>
Copy
#include <stdio.h>

struct student {
    union {  // Anonymous union: no name is required
        char name[10];
        int roll;
    };
    int mark;
};

int main() {
    struct student stud;
    char choice;

    printf("\nEnter 'y' if you want to input name, or 'n' for roll number: ");
    scanf(" %c", &choice);  // Notice the space before %c to consume any leftover whitespace

    if (choice == 'y' || choice == 'Y') {
        printf("Enter name: ");
        scanf("%s", stud.name);
        printf("Name: %s\n", stud.name);
    } else {
        printf("Enter roll number: ");
        scanf("%d", &stud.roll);
        printf("Roll: %d\n", stud.roll);
    }

    printf("Enter marks: ");
    scanf("%d", &stud.mark);
    printf("Marks: %d\n", stud.mark);

    return 0;
}

Explanation:

  1. The anonymous union provides the mutually exclusive name and roll members.
  2. The selected input branch writes and reads the same union member.
  3. The mark field is outside the union, so it does not overlap either identifier.
  4. In production input code, the name field should be read with a width limit or with fgets to prevent an overlong value from exceeding the array capacity.

Sample Output:


Enter 'y' if you want to input name, or 'n' for roll number: y
Enter name: john
Name: john
Enter marks: 45
Marks: 45

Structures as C Union Members

A structure can be a union member. This becomes useful when a union offers two or more alternative record layouts. If the union contains only one structure member, as in the introductory example below, it demonstrates valid nesting but does not save memory by itself.

Example 3: Structure Inside a Union

This example declares a student structure and uses it as the sole member of union details. Because the structure’s own fields have independent storage, its name, roll number, and percentage can all be used together.

C Program

</>
Copy
#include <stdio.h>

int main() {
    // Define a structure to store student details
    struct student {
        char name[30];
        int rollno;
        float percentage;
    };

    // Define a union that contains the student structure
    union details {
        struct student s1;
    };

    union details set;

    printf("Enter student details:\n");
    printf("Enter name: ");
    scanf("%s", set.s1.name);
    printf("Enter roll number: ");
    scanf("%d", &set.s1.rollno);
    printf("Enter percentage: ");
    scanf("%f", &set.s1.percentage);

    printf("\nThe student details are:\n");
    printf("Name: %s\n", set.s1.name);
    printf("Roll Number: %d\n", set.s1.rollno);
    printf("Percentage: %.2f\n", set.s1.percentage);

    return 0;
}

Explanation:

  1. struct student groups the name, roll number, and percentage.
  2. union details contains that complete structure as member s1.
  3. The nested fields are accessed with expressions such as set.s1.rollno.
  4. A practical variant would add a second union member representing a different type of record and pair the union with a tag.

Sample Output:


Enter student details:
Enter name: alice
Enter roll number: 101
Enter percentage: 88.5

The student details are:
Name: alice
Roll Number: 101
Percentage: 88.50

C Union Bit-Fields and Hardware Registers

A union is sometimes used to view a register as a complete integer or as named bit-fields. This technique is common in embedded code, but bit-field order, allocation, padding, integer width, and byte order can be implementation-defined. A layout that works for one compiler and processor is not automatically portable to another.

</>
Copy
#include <stdint.h>

typedef union {
    uint8_t value;
    struct {
        unsigned ready : 1;
        unsigned error : 1;
        unsigned mode  : 2;
        unsigned       : 4;
    } bits;
} StatusRegister;

Use such a declaration only when the compiler, target, and register specification define the required layout. Portable code often uses masks and shifts instead:

</>
Copy
#define STATUS_READY_MASK 0x01u
#define STATUS_ERROR_MASK 0x02u
#define STATUS_MODE_MASK  0x0Cu

unsigned ready = (status & STATUS_READY_MASK) != 0u;
unsigned mode = (status & STATUS_MODE_MASK) >> 2;

When to Use a Union in C

  • Variant records: A value can be one of several types, with an enumeration identifying the current alternative.
  • Embedded systems: Mutually exclusive data can share limited memory, provided the hardware and compiler requirements are understood.
  • Protocol or file representations: A field can have alternative layouts defined by a separate type code. External bytes still require validation and correct handling of byte order, alignment, and representation.
  • Hardware access: A register can be represented in more than one way when the target ABI explicitly supports the chosen layout.
  • Parser and interpreter values: Tokens or runtime values can store integers, floating-point numbers, strings, or other alternatives under a tag.

Do not choose a union only because it appears smaller. Use a structure when the fields must remain valid simultaneously. Use a tagged union when the program needs several alternatives and must determine safely which one is present.

Common C Union Mistakes

  • Reading the wrong member: Track the intended member explicitly instead of assuming that the union remembers it.
  • Expecting numeric conversion: Assigning a float and reading an integer interprets bytes; it does not perform the equivalent of an integer cast.
  • Assuming an exact union size: Use sizeof for the target implementation because alignment can add padding.
  • Depending on bit-field order: Confirm compiler and target rules before mapping a hardware or wire format with bit-fields.
  • Ignoring object lifetime responsibilities: A union containing pointers owns no memory automatically. The program must define allocation, copying, and cleanup rules.
  • Using unsafe string input: A character array inside a union has the same capacity limits as any other array.
  • Letting a tag and value disagree: Update the discriminant and the corresponding union member as one logical operation.

C Union QA Checklist

  • The tutorial states that union members share storage and does not imply that all members retain independent values.
  • Union size is described as implementation-dependent and capable of including alignment padding.
  • Examples read the intended member or clearly explain the portability limits of interpreting another member.
  • Variant examples include a tag that identifies the currently valid union member.
  • Anonymous unions are identified with an appropriate C language-version or compiler-support note.
  • Bit-field examples warn that layout can depend on the compiler and target architecture.
  • String members and input examples account for array capacity and null termination.
  • Structure-versus-union comparisons distinguish simultaneous fields from mutually exclusive alternatives.

C Union Frequently Asked Questions

What is a union in C?

A union is a user-defined C type in which all members share the same storage. It can represent any one of its declared member types, but the program must know which member currently provides the intended interpretation.

How is a union different from a structure in C?

A structure gives each member separate storage, so all its fields can hold values simultaneously. A union overlays its members in one storage area, so writing one member replaces the shared representation used by the others.

How does a C program know which union member is active?

A plain union does not store that information. The program can maintain a separate enumeration or other discriminant alongside the union. Combining both in a structure creates a tagged union.

What is a typedef union in C?

It is a union type given an alias with typedef. The alias allows declarations such as Value item; instead of union Value item;, depending on how the type was defined.

Can a C union contain a structure or an array?

Yes. Structure and array types can be union members. Each complete member occupies the shared union storage, and the union must be large enough and correctly aligned for its largest member.

C Union Summary

  1. A C union overlays all its members in one storage area.
  2. Its size is sufficient for its largest member and can include alignment padding.
  3. Assigning another member replaces the representation stored in the shared memory.
  4. A tagged union uses a separate value, commonly an enumeration, to identify the intended member.
  5. Anonymous unions, bit-fields, and representation-based techniques require attention to the C version, compiler, target, and portability requirements.