Unions union & Bit-Fields struct

Last Updated : Jan 2026


A union looks syntax-wise identical to a struct, but with one massive operational difference: all members share the exact same memory location.

  • In a struct, the total size is at least the sum of all member sizes.
  • In a union, the total size is equal to the size of its largest single member.

Union Declaration

You declare a union using the union keyword. The syntax is identical to a struct. This creates a template but does not allocate memory yet.

union SensorData {
    int temperature;     // 4 bytes
    float humidity;      // 4 bytes
    char statusMsg[20];  // 20 bytes
};


Union Variables

Once declared, you can create union variables.

Important Initialization Rule: When initializing a union at the time of declaration, you can strictly only initialize its first member (unless you use C99 designated initializers).

// 1. Uninitialized variable
union SensorData data1;

// 2. Initializing the first member (temperature)
union SensorData data2 = { 72 };

// 3. C99 Designated Initializer (Initialize a specific member)
union SensorData data3 = { .humidity = 45.5f };


Accessing Union Members

You access union members exactly as you would with structures:

  • Use the dot operator (.) for direct union variables.
  • Use the arrow operator (->) if you have a pointer to a union.
#include <stdio.h>
#include <string.h>

union Packet {
    int id;
    float weight;
};

int main(void) {
    union Packet box;
    union Packet *ptr = &box;

    // Access via dot operator
    box.id = 1042;
    printf("ID: %d\n", box.id);

    // Access via arrow operator
    ptr->weight = 12.5f;

    // WARNING: 'id' is now corrupted because 'weight' overwrote its memory!
    printf("New Weight: %.2f\n", ptr->weight);

    return 0;
}

Union Memory Layout

A union can only hold a value for one member at a time. Writing to one member overwrites the data of all other members.

#include <stdio.h>

struct StructData {
    char c;    // 1 byte
    int i;     // 4 bytes
    double d;  // 8 bytes
}; // Size: 16 bytes (due to padding)

union UnionData {
    char c;    // 1 byte
    int i;     // 4 bytes
    double d;  // 8 bytes
}; // Size: 8 bytes (Size of largest member, 'd')

int main(void) {
    union UnionData u;

    u.i = 42;
    printf("u.i: %d\n", u.i); // Output: 42

    u.d = 3.14159; // Overwrites the memory shared with 'i' and 'c'
    printf("u.d: %f\n", u.d); // Output: 3.14159

    // Reading u.i now yields garbage because 'd' modified those shared bytes!
    printf("u.i (corrupted): %d\n", u.i);

    return 0;
}

          UNION MEMORY LAYOUT (Shared 8 Bytes)
+------------------------------+
|  char c  |                   |
+------------------------------+
|        int i   |             |
+------------------------------+
|                   double d   |
+------------------------------+

Bit-Fields inside Structs

Standard variables like int or char take up whole bytes (at minimum 8 bits). But what if you only need to store a boolean flag (1 bit) or a value between 0 and 7 (3 bits)?

Bit-fields allow you to specify the exact number of bits a structure member occupies, packing multiple fields into a single underlying integer.

#include <stdio.h>

// Standard Struct: takes 12 bytes due to alignment padding
struct StandardFlags {
    unsigned int is_active;    // Needs 1 bit (uses 32 bits)
    unsigned int is_admin;     // Needs 1 bit (uses 32 bits)
    unsigned int read_permission; // Needs 1 bit (uses 32 bits)
};

// Bit-Field Struct: packs all flags into 1 single integer!
struct CompactFlags {
    unsigned int is_active       : 1; // 1 bit (0 or 1)
    unsigned int is_admin        : 1; // 1 bit (0 or 1)
    unsigned int read_permission : 1; // 1 bit (0 or 1)
    unsigned int user_role       : 3; // 3 bits (0 to 7)
}; // Size: Only 4 bytes total!

int main(void) {
    struct CompactFlags flag = {1, 0, 1, 5};

    printf("Size of CompactFlags: %zu bytes\n", sizeof(flag));
    printf("User Role: %u\n", flag.user_role);

    return 0;
}

Bit-Field Rules & Pitfalls:

  • You cannot use the address-of operator (&) on a bit-field member (&flag.is_active) because CPU memory addresses point to bytes, not individual bits.
  • Exceeding the bit capacity (e.g., storing 10 in a 3-bit field) causes integer overflow and silent truncation.

Tagged Unions

Combining Structs & Unions, The safest production pattern for unions is a Tagged Union (or Variant). You embed a union inside a struct along with an enum "tag" that keeps track of which union member is currently active.

#include <stdio.h>

typedef enum { INT_TYPE, FLOAT_TYPE, STRING_TYPE } DataType;

struct Value {
    DataType type; // The "Tag" tracking active data
    union {
        int i_val;
        float f_val;
        char *s_val;
    } data;
};

void printValue(struct Value *v) {
    switch (v->type) {
        case INT_TYPE:    printf("Integer: %d\n", v->data.i_val); break;
        case FLOAT_TYPE:  printf("Float: %.2f\n", v->data.f_val); break;
        case STRING_TYPE: printf("String: %s\n", v->data.s_val); break;
    }
}



Liked This Tutorial

Scan QR Code To Leave A Review Or

Click Here To Leave A Review
Scan QR