Skip to content

C Unions — Shared Memory, Type Punning, and Union vs Struct

DodaTech Updated 2026-06-28 8 min read

In this tutorial, you will learn about C Unions. We cover key concepts, practical examples, and best practices to help you master this topic.

C unions are user-defined types where all members share the same memory location, allowing you to store different data types in the same space, with the size determined by the largest member.

Why It Matters

Unions provide memory efficiency and enable type punning -- interpreting the same bytes as different types. They are essential in systems programming for protocol Parsing, hardware register access, and variant data structures. Understanding unions deepens your knowledge of how C maps types to memory.

Real-World Use

Network protocol parsers use unions to interpret packet headers in different ways. Hardware drivers map registers to union members for byte and word access. JSON parsers use unions for variant values (string, number, boolean). The X11 protocol uses unions for event types.

What You Will Learn

  • Declaring and using union types
  • How unions share memory between members
  • Type punning with unions
  • Union vs struct differences
  • Anonymous unions (C11+)

Learning Path

flowchart LR
  A[Structs] --> B[Unions
You are here] B --> C[Bit Fields] C --> D[Typedef] D --> E[Void Pointers] style B fill:#f90,color:#fff

What Is a Union?

A union stores all its members at the same memory address. The size is the size of the largest member. Writing to one member overwrites the others:

#include <stdio.h>

union Data {
    int i;
    float f;
    char str[20];
};

int main() {
    union Data data;
    
    printf("Size of union: %zu bytes\n", sizeof(data));
    // Size is 20 (largest member: str[20])
    
    data.i = 42;
    printf("As int: %d\n", data.i);
    
    data.f = 3.14159f;  // Overwrites data.i
    printf("As float: %f\n", data.f);
    printf("As int (corrupted): %d\n", data.i);  // Garbage
    
    return 0;
}

Expected output:

Size of union: 20 bytes
As int: 42
As float: 3.141590
As int (corrupted): 1078530011

The key insight: all members share the same bytes. When you write to data.f, you overwrite what was stored in data.i. Reading data.i after writing data.f gives you the raw bytes of the float interpreted as an integer.

Union vs Struct

#include <stdio.h>

struct StructExample {
    int i;
    float f;
    char str[20];
};

union UnionExample {
    int i;
    float f;
    char str[20];
};

int main() {
    struct StructExample s = {42, 3.14f, "Hello"};
    union UnionExample u;
    
    printf("Struct size: %zu\n", sizeof(s));
    printf("Union size: %zu\n", sizeof(u));
    
    printf("Struct members are independent:\n");
    printf("  i = %d\n", s.i);
    printf("  f = %f\n", s.f);
    printf("  str = %s\n", s.str);
    
    printf("Union members share memory:\n");
    u.i = 42;
    printf("  int = %d\n", u.i);
    u.f = 3.14f;
    printf("  float = %f\n", u.f);
    printf("  int (overwritten) = %d\n", u.i);
    
    return 0;
}

Expected output:

Struct size: 28
Union size: 20
Struct members are independent:
  i = 42
  f = 3.140000
  str = Hello
Union members share memory:
  int = 42
  float = 3.140000
  int (overwritten) = 1078523331
Aspect Struct Union
Memory layout Members stored sequentially All members at same address
Size Sum of all members (plus padding) Size of largest member
Member access All members available simultaneously Only last written member is valid
Use case Grouping related data Variant types, type punning

Type Punning with Unions

Type punning means interpreting the same bytes as different types. Unions provide a standard-compliant way to do this:

#include <stdio.h>

union FloatInt {
    float f;
    unsigned int u;
};

int main() {
    union FloatInt fi;
    
    fi.f = 3.14159f;
    
    printf("Float: %f\n", fi.f);
    printf("As hex: 0x%08X\n", fi.u);
    
    // Inspect individual bytes
    unsigned char *bytes = (unsigned char*)&fi;
    printf("Bytes: ");
    for (int i = 0; i < sizeof(float); i++) {
        printf("%02X ", bytes[i]);
    }
    printf("\n");
    
    // Modify bits via int access
    fi.u = 0x40490FDB;  // Pi in IEEE 754
    printf("Reinterpreted as float: %f\n", fi.f);
    
    return 0;
}

Expected output:

Float: 3.141590
As hex: 0x40490FDB
Bytes: DB 0F 49 40
Reinterpreted as float: 3.141590

Why Type Punning Matters

  • Debugging: inspect the binary representation of floating-point numbers
  • Networking: interpret raw bytes as protocol fields
  • Graphics: extract color components from packed integers
  • Compression: manipulate header bits directly

Unions with Struct Members

Unions become powerful when combined with structs:

#include <stdio.h>
#include <string.h>

// Variant type: can hold different "types" of value
typedef struct {
    int type;  // 0 = int, 1 = float, 2 = string
    union {
        int i;
        float f;
        char str[50];
    } value;
} Variant;

void print_variant(Variant v) {
    switch (v.type) {
        case 0:
            printf("Int: %d\n", v.value.i);
            break;
        case 1:
            printf("Float: %f\n", v.value.f);
            break;
        case 2:
            printf("String: %s\n", v.value.str);
            break;
    }
}

int main() {
    Variant v1 = {0, .value.i = 42};
    Variant v2 = {1, .value.f = 3.14f};
    Variant v3 = {2};
    strcpy(v3.value.str, "Hello Unions");
    
    print_variant(v1);
    print_variant(v2);
    print_variant(v3);
    
    return 0;
}

Expected output:

Int: 42
Float: 3.140000
String: Hello Unions

This pattern is common in dynamic languages, configuration systems, and protocol parsers where the type of data varies at runtime.

Anonymous Unions (C11+)

C11 introduced anonymous unions that do not require a member name:

#include <stdio.h>

typedef struct {
    int type;
    union {           // Anonymous union
        int i;
        float f;
        char *s;
    };                // No member name needed
} Value;

int main() {
    Value v;
    v.type = 0;
    v.i = 42;         // Direct access, no intermediate name
    
    printf("Value: %d\n", v.i);
    
    v.type = 1;
    v.f = 2.718f;
    printf("Value: %f\n", v.f);
    
    return 0;
}

Anonymous unions eliminate the extra dot in member access. GCC and Clang support them; check your compiler for C11 Compliance.

Checking Endianness with Unions

Unions can determine the byte order (endianness) of your system:

#include <stdio.h>

union EndianCheck {
    unsigned int word;
    unsigned char bytes[4];
};

int main() {
    union EndianCheck ec;
    ec.word = 0x12345678;
    
    printf("Word: 0x%08X\n", ec.word);
    printf("Bytes: ");
    for (int i = 0; i < 4; i++) {
        printf("0x%02X ", ec.bytes[i]);
    }
    printf("\n");
    
    if (ec.bytes[0] == 0x78) {
        printf("System: Little-Endian\n");
    } else if (ec.bytes[0] == 0x12) {
        printf("System: Big-Endian\n");
    }
    
    return 0;
}

Expected output on x86 (Little-Endian):

Word: 0x12345678
Bytes: 0x78 0x56 0x34 0x12
System: Little-Endian

Common Mistakes

1. Reading Wrong Union Member

union { int i; float f; } u;
u.f = 3.14f;
printf("%d", u.i);  // Reads float bits as int -- usually wrong

Only read the member that was last written. Use a discriminator (tag) to track the active member.

2. Assuming Union Clears Other Members

u.i = 42;
u.f = 3.14f;  // u.i is now corrupted

Writing to one member overwrites all others. Initialize the union when you change the active member.

3. Using Union for Large Type Punning (Undefined Behavior)

float f = 3.14f;
int i = *(int*)&f;  // Strict aliasing violation!

Unions provide the only standard-compliant way to type-pun in C.

4. Forgetting About Padding

union { char a; int b; } u;
// Size is sizeof(int) = 4, not 1

The union size is the largest member's size, including its padding.

5. Not Using Discriminator (Tag) Field

union Value { int i; float f; };
void process(union Value v) {
    // How do we know if v holds int or float?
}

Always pair a union with a tag field that tracks the active member type.

Practice Questions

  1. What is the size of a union? The size of its largest member, including any padding required.

  2. How are union members stored in memory? All members start at the same memory address. They overlap in memory.

  3. What is the difference between a struct and a union? Struct members are stored sequentially in memory. Union members overlap at the same address.

  4. What is a tagged union? A union combined with a discriminator (tag) field that tracks which member is currently active.

  5. Challenge: Write a program that uses a union to store different shape types (circle, rectangle, triangle) with appropriate data for each.

Mini Project: Shape Union

#include <stdio.h>
#include <math.h>

typedef enum { CIRCLE, RECTANGLE, TRIANGLE } ShapeType;

typedef struct {
    ShapeType type;
    union {
        struct { double radius; } circle;
        struct { double width, height; } rectangle;
        struct { double a, b, c; } triangle;
    } shape;
} Shape;

double area(Shape s) {
    switch (s.type) {
        case CIRCLE:
            return M_PI * s.shape.circle.radius * s.shape.circle.radius;
        case RECTANGLE:
            return s.shape.rectangle.width * s.shape.rectangle.height;
        case TRIANGLE: {
            double p = (s.shape.triangle.a + s.shape.triangle.b
                        + s.shape.triangle.c) / 2.0;
            return sqrt(p * (p - s.shape.triangle.a)
                       * (p - s.shape.triangle.b)
                       * (p - s.shape.triangle.c));
        }
    }
    return 0;
}

int main() {
    Shape shapes[3];
    
    shapes[0] = (Shape){CIRCLE, .shape.circle = {5.0}};
    shapes[1] = (Shape){RECTANGLE, .shape.rectangle = {4.0, 6.0}};
    shapes[2] = (Shape){TRIANGLE, .shape.triangle = {3.0, 4.0, 5.0}};
    
    for (int i = 0; i < 3; i++) {
        printf("Shape %d area: %.2f\n", i + 1, area(shapes[i]));
    }
    
    return 0;
}

FAQ

Is type punning with unions legal in C?

Yes. The C standard explicitly allows accessing any union member, even if it was not the last one written, for type punning purposes.

When should I use a union instead of a struct?

When you need to store one of several possible types at any given time. Unions save memory compared to structs that allocate space for all members simultaneously.

Can a union contain a struct?

Yes. Unions and structs can be nested. A union member can be a struct, and a struct member can be a union.

What happens if I write to one member and read another?

You get the raw bytes interpreted as the read type. This is type punning and is valid in C, but the resulting value is usually meaningless.

Do unions support padding?

Yes. The union size includes padding for the largest member. The union itself is aligned to the strictest alignment among its members.

What is Next

Now that you understand unions, proceed to Bit Fields to learn how to pack data into individual bits within structs.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro

Home Browse C