C Unions: Shared Memory Architecture, Overlapping Layouts & Variant Types
Welcome to Phase 12 (Chapter 30): C Unions β Shared Memory Architecture, Overlapping Layouts & Variant Types Masterclass! While a struct allocates separate, independent memory locations for every member, a union forces all its members to share the exact same physical RAM memory location. The total size of a union is determined solely by its single largest member. In this exhaustive textbook-grade guide, you will master the mechanics of shared memory overlapping, analyze a side-by-side visual memory breakdown of Structs vs Unions, discover how Tagged Unions implement type-safe variant variables, explore low-level hardware register bitfields, and master pointer access to unions.
A Union is a user-defined data type in C where All members share the starting RAM address (Offset 0x0). Only one member can hold a valid value at any given point in time! Writing to one member overwrites the shared memory of all other members.
1. STRUCT Layout: struct Data { int i; float f; char str[20]; };
RAM Address: 0x1000 0x1004 0x1008...0x101C
βββββββββββββββ¬ββββββββββββββ¬βββββββββββββββββββββββββββ
Memory: β int i (4B) β float f(4B) β char str[20] (20B) β
βββββββββββββββ΄ββββββββββββββ΄βββββββββββββββββββββββββββ
Total Size = 4 + 4 + 20 = 28 Bytes (Independent Slots)
2. UNION Layout: union Data { int i; float f; char str[20]; };
RAM Address: 0x2000...0x2014 (All members START at address 0x2000!)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Memory: β int i (4B) / float f (4B) / char str[20] (20B) Sharedβ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Total Size = 20 Bytes (Size of Largest Member: char str[20])
To safely know which union member currently holds a valid value, C engineers combine an enum tag with a union inside a structure (Tagged Union / Variant Type):
#include <stdio.h>
typedef enum { TYPE_INT, TYPE_FLOAT, TYPE_STRING } DataType;
typedef struct {
DataType type;
union {
int iVal;
float fVal;
char sVal[30];
} payload; // Shared memory union payload!
} Variant;
void printVariant(const Variant *v) {
switch (v->type) {
case TYPE_INT:
printf("Integer Value: %d\n", v->payload.iVal);
break;
case TYPE_FLOAT:
printf("Float Value: %.2f\n", v->payload.fVal);
break;
case TYPE_STRING:
printf("String Value: %s\n", v->payload.sVal);
break;
}
}
int main(void) {
Variant v1, v2;
v1.type = TYPE_INT;
v1.payload.iVal = 42;
v2.type = TYPE_FLOAT;
v2.payload.fVal = 99.99f;
printVariant(&v1);
printVariant(&v2);
return 0;
}
Q1: What happens if you read a union member different from the one last written?
This is known as Type Punning. The CPU will re-interpret the binary bits of the last written value as if they belonged to the requested type (e.g. reading raw IEEE-754 float bits as an integer), which is widely used in graphics and fast math hacks!
Q2: Why are unions heavily used in Embedded Systems and Microcontrollers?
Microcontrollers have extremely limited RAM (sometimes only a few kilobytes). Unions allow sharing memory buffers between mutually exclusive peripherals (e.g. sharing a 512-byte RAM buffer between UART RX and SPI TX).
Run this union size inspector in our live GCC compiler:
#include <stdio.h>
union Packet {
int header;
double timestamp;
char payload[64];
};
int main(void) {
union Packet p;
printf("Size of union Packet: %zu bytes\n", sizeof(p));
printf("Address of header: %p\n", (void*)&p.header);
printf("Address of payload: %p (Same Address!)\n", (void*)&p.payload);
return 0;
}