Question
In C, what is a good way to understand the difference between a struct and a union?
I understand that a struct stores enough memory for all of its members, while a union uses only enough memory for its largest member. Beyond that memory layout difference, are there any other important differences, such as operating system-level behavior?
For example, how should I think about these two declarations?
struct ExampleStruct {
int id;
float price;
char code;
};
union ExampleUnion {
int id;
float price;
char code;
};
Short Answer
By the end of this page, you will understand how struct and union differ in C, especially in memory layout, data access, and practical use. You will also learn that this is mainly a language and memory-model concept, not an operating system feature.
Concept
A struct and a union are both user-defined types in C, but they represent data in very different ways.
struct
A struct gives each member its own storage. All fields can hold valid values at the same time.
struct Point {
int x;
int y;
};
If you set x and y, both values are stored independently.
union
A union makes all members share the same memory location. Only one member's value is meaningfully stored at a time.
union Number {
int i;
float f;
};
If you write to i and then write to f, the f write reuses the same bytes and overwrites the previous contents.
Why this matters
This difference affects:
- Memory usage:
unioncan save space. - Meaning of data: with a union, the same bytes can be interpreted in different ways.
- Program design:
structis for objects with multiple fields;unionis for one value that may have different forms.
Is there an OS-level difference?
Usually, no. This is primarily a C language and compiler memory layout concept, not an operating system feature.
The compiler decides layout rules such as:
- member offsets in a
struct - size of a
union - alignment and padding
The operating system generally does not treat struct and union differently. They are just blocks of memory in your program. What matters is how the C language defines access to that memory.
Mental Model
Think of a struct as a desk with separate labeled drawers:
- one drawer for
id - one drawer for
price - one drawer for
code
Each member has its own space, so all values can exist together.
Think of a union as a single drawer with several labels stuck on it:
idpricecode
There is still only one drawer. The labels are different ways to interpret the same space. If you put something in as id and later put something in as price, the new value replaces the old bytes.
So:
struct= many storage locations grouped togetherunion= one storage location viewed in different ways
Syntax and Examples
Basic syntax
struct Person {
int age;
char initial;
};
union Value {
int i;
float f;
char c;
};
Example: struct
#include <stdio.h>
struct Person {
int age;
char initial;
};
int main(void) {
struct Person p;
p.age = 25;
p.initial = 'A';
printf("age = %d\n", p.age);
printf("initial = %c\n", p.initial);
return 0;
}
Here both fields keep their values because each member has separate storage.
Example: union
#include <stdio.h>
union Value {
int i;
float f;
char c;
};
int main(void) {
union Value v;
v.i = 42;
printf("i = %d\n", v.i);
v.f = 3.14f;
printf("f = %.2f\n", v.f);
return 0;
}
When v.f is assigned, it reuses the same memory previously used by v.i. That means the old integer value is no longer the active value.
Size example
#include <stdio.h>
struct ExampleStruct {
int id;
float price;
char code;
};
union ExampleUnion {
int id;
float price;
char code;
};
int main(void) {
printf("struct size: %zu\n", sizeof(struct ExampleStruct));
printf("union size: %zu\n", sizeof(union ExampleUnion));
return 0;
}
The exact sizes may vary because of padding and alignment, but:
- the
structis large enough for all members - the
unionis large enough for its largest member
Step by Step Execution
Consider this union example:
#include <stdio.h>
union Data {
int i;
char c;
};
int main(void) {
union Data d;
d.i = 65;
printf("d.i = %d\n", d.i);
d.c = 'B';
printf("d.c = %c\n", d.c);
return 0;
}
Step by step
-
union Data d;- Memory is reserved for one
union Datavariable. - Its size is enough for the largest member.
- Memory is reserved for one
-
d.i = 65;- The shared memory is filled with the integer representation of
65. - At this point,
iis the active member.
- The shared memory is filled with the integer representation of
-
printf("d.i = %d\n", d.i);- Prints
65.
- Prints
-
d.c = 'B';- The same shared memory is written again, now as a
char. - The previous integer bytes are no longer reliably meaningful as an
intvalue. - Now
cis the active member.
- The same shared memory is written again, now as a
-
printf("d.c = %c\n", d.c);- Prints
B.
- Prints
Compare with a struct
#include <stdio.h>
struct Data {
int i;
char c;
};
int main(void) {
struct Data d;
d.i = 65;
d.c = 'B';
printf("d.i = %d\n", d.i);
printf("d.c = %c\n", d.c);
return 0;
}
Here both values remain stored, because i and c have separate memory.
Real World Use Cases
When struct is used
struct is used far more often than union in everyday C programming.
Common uses:
- storing records such as users, points, products, or configuration data
- grouping related fields into one object
- representing packets, headers, or state objects
- passing multiple related values to functions
Example:
struct User {
int id;
char role;
float rating;
};
A user has all of these properties at the same time, so struct is the correct choice.
When union is used
union is useful when a value can be one of several types, but never all at once.
Common uses:
- memory-constrained systems
- parsers handling different token value types
- message payloads where only one payload format is active
- tagged data types combined with an enum
Example:
enum ValueType {
TYPE_INT,
TYPE_FLOAT,
TYPE_CHAR
};
struct TaggedValue {
enum ValueType type;
union {
int i;
float f;
char c;
} data;
};
This pattern lets a program store one value at a time while tracking which member is valid.
Real Codebase Usage
In real projects, developers usually use union together with a tag that tells the program which member is currently valid.
Tagged union pattern
enum EventType {
EVENT_KEY,
EVENT_MOUSE
};
struct Event {
enum EventType type;
union {
int key_code;
struct {
int x;
int y;
} mouse;
} data;
};
This is common in:
- event systems
- compilers and interpreters
- protocol handling
- embedded firmware
Why the tag matters
Without a tag, code can easily read the wrong union member.
struct usage patterns in real code
Developers use struct for:
- configuration objects
- return values with multiple fields
- state containers
- linked list and tree nodes
- API request and response models
Guarding access to unions
A common pattern is:
- validate the tag first
- read only the matching union field
- use
switchorifto branch safely
switch (value.type) {
case TYPE_INT:
printf("%d\n", value.data.i);
break;
case TYPE_FLOAT:
printf("%f\n", value.data.f);
break;
case TYPE_CHAR:
printf("%c\n", value.data.c);
break;
}
This makes union-based code safer and easier to understand.
Common Mistakes
1. Thinking a union stores all members at once
Broken assumption:
union Data {
int i;
float f;
};
union Data d;
d.i = 10;
d.f = 2.5f;
printf("%d\n", d.i); // Not a safe way to expect the old int value
Why it is wrong:
- assigning
d.foverwrites the shared memory d.iis no longer the active value
2. Using a union when a struct is needed
Broken design:
union Person {
int age;
char initial;
};
If a person needs both age and initial, use a struct, not a union.
3. Ignoring padding and alignment
Beginners often assume sizes are just the sum of member sizes.
struct Example {
char c;
int i;
};
This may be larger than 1 + 4 because of alignment rules.
4. Reading a union member without tracking what was written
Problem:
union Value v;
v.i = 100;
printf("%f\n", v.f); // Interpreting int bytes as float bytes
Even if this compiles, it is usually not what you want.
5. Assuming there is an OS-specific behavior difference
struct and union are mostly about:
- language rules
- compiler layout
- CPU alignment needs
They are not special operating-system objects.
How to avoid mistakes
- Use
structwhen all fields should exist together. - Use
unionwhen only one representation is active at a time. - Pair unions with an
enumtag. - Check
sizeof(...)when memory layout matters. - Be careful with low-level byte interpretation.
Comparisons
| Feature | struct | union |
|---|---|---|
| Memory for members | Separate memory for each member | Shared memory for all members |
| Total size | Usually enough for all members plus padding | Enough for largest member plus alignment |
| Can all members hold values at once? | Yes | No, only one meaningful active member at a time |
| Typical use | Records and grouped data | Variant data and memory saving |
| Safer for beginners | Yes | Less so |
| Common in application code | Very common | More specialized |
struct vs union in one sentence
- Use a
structfor has-a set of fields. - Use a
unionfor is-one-of-several-forms.
Example comparison
struct Coordinates {
int x;
int y;
};
A point has both x and y.
union InputValue {
int whole_number;
float decimal_number;
};
This value is either an int or a float at one time.
Cheat Sheet
struct S {
int a;
float b;
};
union U {
int a;
float b;
};
struct: each member has its own storageunion: all members share the same storagestructsize: enough for all members + paddingunionsize: enough for largest member + alignmentstruct: all fields can be used togetherunion: only one member should be treated as active- No special OS-level difference in normal C usage
- Layout details depend on compiler, target, and alignment rules
Good rule of thumb
- Need multiple fields at once? Use
struct. - Need one value that may take different forms? Use
union.
Safer union pattern
enum Kind { INT_KIND, FLOAT_KIND };
struct Value {
enum Kind kind;
union {
int i;
float f;
} data;
};
Useful operators
sizeof(struct S)
sizeof(union U)
Edge case reminder
Reading a different union member than the one most recently written can be confusing and must be done carefully.
FAQ
Is a union just a smaller struct?
No. A union is not just a space-optimized struct. Its members share the same memory, which changes how values are stored and read.
When should I use a struct instead of a union?
Use a struct when all fields are part of the object at the same time, such as a user with an id, name, and age.
When is a union useful in C?
A union is useful when a value can be one of several types, such as a token in a parser or an event payload in a system.
Does the operating system treat structs and unions differently?
Usually no. This is mainly a C language and compiler memory layout issue, not an OS-level distinction.
Why is a union often combined with an enum?
The enum acts as a tag so the program knows which union member currently contains valid data.
Can a union and struct have unexpected sizes?
Yes. Alignment and padding can change sizes, especially for structs. Always use sizeof instead of guessing.
Is reading one union member after writing another allowed?
It may compile, but it can be tricky and is often not the right design for beginners. Use a tagged union pattern when possible.
Mini Project
Description
Build a small C program that stores different kinds of values using a tagged union. This demonstrates the practical difference between struct and union: the struct holds the type tag and the union holds one active value.
Goal
Create a program that can store and print either an integer, a float, or a character safely using an enum and a union.
Requirements
- Define an
enumto represent the active value type. - Define a
structthat contains the enum tag and a union of possible values. - Create at least three example values: one integer, one float, and one character.
- Write a function that prints the stored value based on the tag.
- Compile and run the program to verify that each value is printed correctly.
Keep learning
Related questions
2D Array Loop Order and Cache Performance in C
Learn why swapping nested loops changes 2D array performance in C, using row-major memory layout, cache locality, and practical benchmarks.
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.