Introduction
One of the defining characteristics of a union is its memory layout. Unlike a structure, where each member occupies its own storage, all members of a union share the same memory location.
Understanding how memory is organized inside a union is essential for writing efficient C programs, especially in embedded systems, operating systems, network programming, and other low-level applications.
This article explains how union memory is allocated, how members overlap, and how the size of a union is determined.
How Memory Is Allocated in a Union
When a union is created, the compiler allocates a single block of memory that is large enough to store its largest member.
Every member begins at the same memory address and shares this storage.
Syntax
union Data
{
int number;
float decimal;
char character;
};
Although the union contains three members, only one memory block is allocated.
Visualizing the Memory Layout
Suppose the implementation uses the following sizes:
| Type | Size |
|---|---|
char |
1 byte |
int |
4 bytes |
float |
4 bytes |
The memory layout of the union can be represented as:
Example
+-----------------------+
| Byte 0 |
| Byte 1 |
| Byte 2 |
| Byte 3 |
+-----------------------+
number ----+
decimal ----+--> Same memory
character ----+
All three members start at the same address and occupy the same memory region.
Every Member Has the Same Starting Address
Since all members share memory, their addresses are identical.
Example
#include <stdio.h>
union Data
{
int number;
float decimal;
char character;
};
int main(void)
{
union Data value;
printf("%p\n", (void *)&value.number);
printf("%p\n", (void *)&value.decimal);
printf("%p\n", (void *)&value.character);
return 0;
}
Typical output:
Output
0x7ffd...
0x7ffd...
0x7ffd...
Although the exact address varies, all members have the same starting location.
Writing to One Member Affects All Others
Because every member occupies the same storage, writing to one member changes the underlying bytes used by every other member.
Example
union Data
{
int number;
float decimal;
};
union Data value;
value.number = 100;
value.decimal = 3.14f;
After assigning decimal, the bytes that previously represented number have been overwritten.
Union Size
The size of a union is determined by its largest member, plus any alignment required by the implementation.
Example
#include <stdio.h>
union Data
{
char c;
int number;
double decimal;
};
int main(void)
{
printf("%zu\n", sizeof(union Data));
return 0;
}
The exact size depends on the platform, but it will always be at least large enough to hold the largest member.
Example of Size Calculation
Assume the following implementation:
| Member | Size |
|---|---|
char |
1 byte |
int |
4 bytes |
double |
8 bytes |
The union size is typically:
Example
sizeof(union Data) == 8
The smaller members reuse the same eight-byte storage allocated for the largest member.
Alignment and Padding
Like structures, unions may contain padding to satisfy alignment requirements.
For example, a union containing a double usually requires alignment suitable for double, even if smaller members need less alignment.
Comparing Union and Structure Memory Layout
| Union | Structure |
|---|---|
| Members share memory. | Members have separate memory. |
| Single starting address. | Different member addresses. |
| Size equals the largest member (plus alignment). | Size is approximately the sum of all members (plus padding). |
| One active member. | All members may contain values simultaneously. |
Inspecting Memory with sizeof
The sizeof operator can be used to determine the amount of memory occupied by a union.
Example
printf("%zu\n", sizeof(union Data));
This is useful when designing memory-efficient data structures.
Why Shared Memory Is Useful
Sharing memory makes unions valuable in situations where only one representation of data is needed at a time.
Typical applications include:
- Embedded systems.
- Hardware register mapping.
- Communication protocols.
- Binary file formats.
- Tagged unions (variant types).
Common Beginner Mistakes
- Thinking every member has separate storage.
- Assuming multiple members can safely hold independent values simultaneously.
- Expecting the union size to equal the sum of member sizes.
- Ignoring alignment requirements.
- Reading a different member after writing another without understanding the consequences.
Best Practices
- Use unions only when memory sharing is intentional.
- Keep track of which member is currently active.
- Use an accompanying enum to identify the active member.
- Use
sizeofrather than assuming memory sizes. - Be aware that alignment and padding may vary across platforms.
Summary
The memory layout of a union is based on a single shared memory block that is large enough for its largest member. Every member starts at the same address, and only one member should normally be considered active at a time.
| Property | Union |
|---|---|
| Memory Allocation | Single shared block |
| Member Addresses | All members start at the same address |
| Union Size | Largest member (plus alignment) |
| Active Member | One at a time |
| Main Benefit | Memory efficiency |
Understanding the memory layout of unions is fundamental to systems programming in C. It enables you to write memory-efficient programs while safely working with multiple representations of the same data.