If you’ve been learning C for a while, you’ve probably come across both struct and union.
At first glance, they look almost identical.
Both allow you to group multiple data types together. Both use similar syntax. Both are commonly used in embedded systems.
So naturally, many beginners ask:
“If structs and unions look so similar, why does C have both?”
The answer becomes clear when you understand how memory is allocated internally.
And once you start working with embedded systems, communication protocols, sensor data, or memory-constrained microcontrollers, unions suddenly become much more interesting than they first appear.
Imagine you’re building a small weather station.
For each sensor reading, you want to store:
A structure is perfect for this.
struct SensorData
{
float temperature;
float humidity;
int pressure;
};
Now you can create a variable:
struct SensorData sensor;
and access individual members:
sensor.temperature = 28.5;
sensor.humidity = 65.0;
sensor.pressure = 1013;
Simple.
Every piece of information gets its own dedicated storage location in memory.
That is the key idea behind structures:
Every member exists independently at the same time.
If you store temperature, humidity, and pressure, all three values remain available simultaneously.
Let’s look at a smaller example.
struct Example
{
char a;
int b;
};
Assume:
char = 1 byte
int = 4 bytes
The structure allocates memory for both variables.
+----+----+----+----+----+
| a | b |
+----+----+----+----+----+
Both members get their own storage.
This means:
example.a = 'A';
example.b = 100;
Both values coexist safely.
You can access either one whenever needed.
This is why structures are used so frequently in software development.
They represent real-world objects containing multiple properties.
Examples include:
Now let’s look at a union.
union Example
{
char a;
int b;
};
The syntax looks almost identical.
Many beginners assume memory works the same way.
It doesn’t.
This is where things become interesting.
Unlike structures, a union does not allocate separate memory for every member.
Instead:
All members share the same memory location.
Think of a union as one storage box that can be used in different ways.
Consider:
union Data
{
char ch;
int num;
float value;
};
The compiler examines all members.
char = 1 byte
int = 4 bytes
float = 4 bytes
The largest member is 4 bytes.
Therefore, the entire union occupies only 4 bytes.
+----+----+----+----+
| Shared Memory Area |
+----+----+----+----+
Every member uses this same memory block.
That means:
union Data d;
d.num = 100;
stores 100 in memory.
But then:
d.value = 25.5;
overwrites the same memory.
The previous integer value is gone.
Only the most recently written member remains valid.
A good mental model is:
One room per member
Temperature Room
Humidity Room
Pressure Room
All values can exist together.
One room shared by everyone
Only one member can reliably occupy the room at a time.
This single idea explains almost everything about structs and unions.
This is usually the next question.
If unions overwrite data, why use them at all?
The answer is memory efficiency.
Consider a microcontroller with:
2 KB RAM
This is common on smaller embedded devices.
Every byte matters.
Suppose a communication packet can contain either:
But never all three at the same time.
Using a structure:
struct Packet
{
float temperature;
float pressure;
int errorCode;
};
allocates memory for all members.
Even though only one is used.
A union:
union Packet
{
float temperature;
float pressure;
int errorCode;
};
stores only one active value.
This reduces memory consumption significantly.
In embedded systems, those savings add up quickly.
Suppose an ADC provides a 16-bit sensor value.
You want to access:
One solution is a union.
union ADCData
{
uint16_t value;
struct
{
uint8_t lowByte;
uint8_t highByte;
};
};
Usage:
union ADCData adc;
adc.value = 0x1234;
Now:
adc.lowByte
contains:
0x34
and:
adc.highByte
contains:
0x12
This technique is extremely common in:
Use a structure when:
Examples:
Structures are the default choice most of the time.
Use a union when:
Common embedded applications include:
One mistake appears frequently.
union Data
{
int num;
float value;
};
Data.num = 100;
Data.value = 25.5;
Then the programmer expects:
Data.num
to still contain 100.
It won’t.
The float write overwrote the same memory.
Remember:
A union stores multiple interpretations of the same memory, not multiple independent values.
Another common mistake is using unions when a structure would be simpler.
If all values need to exist together, use a structure.
Don’t use a union just because it saves memory.
Interestingly, embedded projects often combine both.
Example:
typedef union
{
uint32_t reg;
struct
{
uint32_t enable : 1;
uint32_t mode : 2;
uint32_t error : 1;
uint32_t unused : 28;
};
} ControlRegister;
This gives two views of the same memory:
control.reg
Access the complete register.
Or:
control.enable
control.mode
Access individual bits.
This pattern appears everywhere in low-level embedded development.
Structures and unions may look similar, but they solve very different problems.
A structure gives every member its own memory location.
A union makes all members share the same memory.
If you need multiple pieces of information available at the same time, use a structure.
If you need different ways to interpret the same memory while conserving RAM, use a union.
Most beginners encounter unions much later than structures because their usefulness becomes clearer when dealing with hardware, communication protocols, and memory optimization.
And once you start building embedded systems, you’ll discover that unions are one of those features that initially seem strange but become surprisingly useful in real projects.