Chapter 19Intermediate

Unions in C

Unions are like structures but all members share the same memory. Useful for saving memory when you only need one field at a time.

15 min readUpdated 2024-12-16
unionshared memoryvariant typeunion vs structmemory efficiency

What You Will Learn

  • Understand how unions share memory
  • Know when to use union vs struct
  • Calculate union size (largest member)
  • Use unions for variant types

01What is a Union?

A union is similar to a structure, but with one key difference: all members share the same memory location. This means a union can only hold one member's value at a time.

Structure vs Union Memory

Structure (Separate Memory)

int
4 bytes
float
4 bytes
char
1 byte

Total: 12 bytes (with padding)

Union (Shared Memory)

int / float / char

All share same space

Total: 4 bytes (size of largest)

Key Difference

  • Structure: Each member has its OWN memory — can store all values
  • Union: All members SHARE memory — only ONE value at a time

02Declaring and Using Unions

union_basic.c
C
1#include <stdio.h>
2
3// Define a union
4union Data {
5 int i;
6 float f;
7 char c;
8};
9
10int main() {
11 union Data data;
12
13 // Store an integer
14 data.i = 42;
15 printf("data.i = %d\n", data.i);
16
17 // Store a float (overwrites the integer!)
18 data.f = 3.14;
19 printf("data.f = %.2f\n", data.f);
20 printf("data.i = %d (garbage now!)\n", data.i); // Corrupted!
21
22 // Store a character (overwrites the float!)
23 data.c = 'A';
24 printf("data.c = %c\n", data.c);
25
26 // Size of union = size of largest member
27 printf("\nSize of union: %zu bytes\n", sizeof(data));
28 printf("Size of int: %zu, float: %zu, char: %zu\n",
29 sizeof(int), sizeof(float), sizeof(char));
30
31 return 0;
32}

Output:

data.i = 42
data.f = 3.14
data.i = 1078523331 (garbage now!)
data.c = A

Size of union: 4 bytes
Size of int: 4, float: 4, char: 1

🔍 How This Program Works

1

union Data data — Creates a union variable. Only 4 bytes allocated (size of largest member: int or float).

2

data.i = 42 — Stores 42 in those 4 bytes. Currently interpreted as an integer.

3

data.f = 3.14Overwrites the same 4 bytes with float representation of 3.14. Integer value is now garbage!

4

data.i now shows 1078523331 — the float's binary pattern interpreted as an integer (meaningless).

5

sizeof(data) returns 4 — always equals the largest member, not the sum of all members.

⚠️ Important!

When you write to one union member, it overwrites ALL other members. Only the last written member contains valid data!

03Union Memory Layout

All union members start at the same memory address. The union's size equals the size of its largest member.

union Data { int i; float f; char c; }

Byte 0
Byte 1
Byte 2
Byte 3
int i (4 bytes)
float f (4 bytes)
char c

All members start at address 0x1000 — they overlap completely!

union_address.c
C
1#include <stdio.h>
2
3union Data {
4 int i;
5 float f;
6 char c;
7};
8
9int main() {
10 union Data data;
11
12 // All members have the SAME address!
13 printf("Address of data: %p\n", (void*)&data);
14 printf("Address of data.i: %p\n", (void*)&data.i);
15 printf("Address of data.f: %p\n", (void*)&data.f);
16 printf("Address of data.c: %p\n", (void*)&data.c);
17
18 // All print the same address!
19
20 return 0;
21}

Union Size Rule

sizeof(union) = size of largest member (plus padding if needed)

04Structure vs Union Comparison

FeatureStructureUnion
Keywordstructunion
MemoryEach member has own spaceAll members share space
SizeSum of all members (+padding)Size of largest member
AccessAll members at onceOnly one member at a time
Use CaseGroup related dataStore one of many types
struct_vs_union.c
C
1#include <stdio.h>
2
3struct StructExample {
4 int i; // 4 bytes
5 float f; // 4 bytes
6 char c; // 1 byte (+3 padding)
7}; // Total: 12 bytes
8
9union UnionExample {
10 int i; // 4 bytes
11 float f; // 4 bytes
12 char c; // 1 byte
13}; // Total: 4 bytes (largest member)
14
15int main() {
16 printf("Size of struct: %zu bytes\n", sizeof(struct StructExample));
17 printf("Size of union: %zu bytes\n", sizeof(union UnionExample));
18
19 return 0;
20}

05Practical Use Cases

1. Variant Type (Store Different Types)

variant_type.c
C
1#include <stdio.h>
2#include <string.h>
3
4// A variant that can hold int, float, or string
5typedef struct {
6 int type; // 0 = int, 1 = float, 2 = string
7 union {
8 int i;
9 float f;
10 char str[20];
11 } value;
12} Variant;
13
14void printVariant(Variant v) {
15 switch (v.type) {
16 case 0: printf("Integer: %d\n", v.value.i); break;
17 case 1: printf("Float: %.2f\n", v.value.f); break;
18 case 2: printf("String: %s\n", v.value.str); break;
19 }
20}
21
22int main() {
23 Variant v1 = {0, .value.i = 42};
24 Variant v2 = {1, .value.f = 3.14};
25 Variant v3;
26 v3.type = 2;
27 strcpy(v3.value.str, "Hello");
28
29 printVariant(v1);
30 printVariant(v2);
31 printVariant(v3);
32
33 return 0;
34}

2. Memory-Efficient Data Structures

packet_union.c
C
1#include <stdio.h>
2
3// Network packet that can be different types
4typedef struct {
5 int packetType; // 1 = text, 2 = binary, 3 = command
6 int length;
7 union {
8 char text[256];
9 unsigned char binary[256];
10 struct {
11 int commandId;
12 int params[4];
13 } command;
14 } payload;
15} Packet;
16
17int main() {
18 Packet p;
19 p.packetType = 1;
20 p.length = 5;
21
22 // Only 256 bytes for payload, not 256+256+20!
23 printf("Packet size: %zu bytes\n", sizeof(Packet));
24
25 return 0;
26}

3. Type Punning (Access Different Representations)

type_punning.c
C
1#include <stdio.h>
2
3// View the same bytes as different types
4union FloatBits {
5 float f;
6 unsigned int bits;
7 unsigned char bytes[4];
8};
9
10int main() {
11 union FloatBits fb;
12 fb.f = 3.14f;
13
14 printf("Float value: %f\n", fb.f);
15 printf("As integer (bits): 0x%08X\n", fb.bits);
16 printf("As bytes: ");
17 for (int i = 0; i < 4; i++) {
18 printf("%02X ", fb.bytes[i]);
19 }
20 printf("\n");
21
22 return 0;
23}

06Common Mistakes

❌ Reading Wrong Member

main.c
C
union Data d;
d.f = 3.14;
printf("%d", d.i); // WRONG! You stored a float, not int
// This prints garbage/meaningless value

Always read the same member you last wrote to!

❌ Expecting All Values to Persist

main.c
C
union Data d;
d.i = 42;
d.f = 3.14;
d.c = 'A';
// WRONG assumption: all three values are stored
// Only d.c is valid now - others are overwritten!

❌ Forgetting to Track Active Member

main.c
C
// BAD: No way to know which member is valid
union Data { int i; float f; };
// GOOD: Use a struct with type indicator
struct TaggedUnion {
int type; // Track which member is active
union { int i; float f; } value;
};

07Summary

What You Learned:

  • Union: All members share the same memory location
  • Size: Equals the size of the largest member
  • One at a time: Only one member holds valid data
  • Use cases: Variant types, memory efficiency, type punning
  • Best practice: Track which member is active with a type field

08Next Steps

Continue learning about other derived data types: