Introduction
In previous lessons, you learned how to declare and use both structures and unions in C. In this lesson, we focus specifically on the differences between structs and unions, especially in terms of memory allocation, data sharing, initialization, and member access. By the end, you should be able to confidently choose between a struct and a union depending on your program’s requirements.
Purpose of Structures in C
You already know how to declare and use a struct; here we emphasize why you use a struct:
- A structure groups related data items that logically belong together.
- Each member has its own memory and can store its value independently of the others.
- Structures are ideal when you need to keep multiple pieces of related information at the same time.
Example: Modeling a 2D Point
#include <stdio.h>
struct Point {
int x;
int y;
};
int main(void) {
struct Point p1 = {10, 20}; // declare and initialize
printf("Point coordinates: x = %d, y = %d\n", p1.x, p1.y);
// modify members independently
p1.x = 30;
p1.y = 40;
printf("Updated point: x = %d, y = %d\n", p1.x, p1.y);
return 0;
}
// Output
Point coordinates: x = 10, y = 20
Updated point: x = 30, y = 40
Explanation: x and y are both active and valid at the same time. Changing x does not affect y, because each has its own memory.
Memory Allocation: Structs vs Unions
The most important difference between structs and unions is how they use memory.
Memory in Structures
- Total size of a struct is at least the sum of the sizes of its members (plus possible padding/alignment).
- Each member occupies separate storage.
Memory in Unions
- All members share the same memory location.
- The size of a union is the size of its largest member.
- At any given time, you should treat the union as if only one member holds a valid value.
Example: Comparing Sizes
#include <stdio.h>
struct SData {
int i;
float f;
char c;
};
union UData {
int i;
float f;
char c;
};
int main(void) {
printf("Size of struct SData: %zu bytes\n", sizeof(struct SData));
printf("Size of union UData: %zu bytes\n", sizeof(union UData));
return 0;
}
// Example Output (platform-dependent)
Size of struct SData: 12 bytes
Size of union UData: 4 bytes
Explanation:
- The struct reserves separate space for
i,f, andc. - The union only needs enough space for the largest member (
intorfloathere), so it uses less memory.
Declaring and Initializing Structs (with Emphasis on Differences)
You’ve already seen basic struct declarations and initializations. Here, we highlight aspects that matter when comparing to unions.
Multiple Members Active at Once
#include <stdio.h>
struct Employee {
int id;
char grade;
float salary;
};
int main(void) {
struct Employee e = {1001, 'A', 55000.50f};
printf("ID: %d\n", e.id);
printf("Grade: %c\n", e.grade);
printf("Salary: %.2f\n", e.salary);
// All three members can be safely read and written independently
e.salary += 1500.0f;
e.grade = 'A';
printf("Updated Salary: %.2f, Grade: %c\n", e.salary, e.grade);
return 0;
}
// Output
ID: 1001
Grade: A
Salary: 55000.50
Updated Salary: 56500.50, Grade: A
Key point: In a struct, it is perfectly valid and expected that all members store meaningful data at the same time.
Data Sharing in Unions
In a union, all members share the same memory. This has several consequences:
- Writing to one member overwrites the underlying bits, affecting how other members appear.
- You typically use a union to store one value out of several possible types at any time.
- Unions are useful for memory-efficient representations or type reinterpretation (with care).
Example: Demonstrating Data Sharing
#include <stdio.h>
union Number {
int i;
float f;
};
int main(void) {
union Number n;
n.i = 10; // store as int
printf("As int: %d\n", n.i);
printf("As float: %f\n", n.f); // reading via float view
n.f = 3.5f; // now store as float
printf("After writing float...\n");
printf("As int: %d\n", n.i); // reading via int view
printf("As float: %f\n", n.f);
return 0;
}
// Example Output (platform-dependent, values may look strange)
As int: 10
As float: 0.000000
After writing float...
As int: 1080033280
As float: 3.500000
Explanation:
n.iandn.foccupy the same bytes.- After you assign
n.f = 3.5f;, the bits in the union represent the float3.5. Interpreting those bits asint(n.i) produces an unrelated integer. - This illustrates that only one logical value should be considered valid at a time in a union.
Access Methods: Struct vs Union Members
Access syntax for members of structs and unions is identical, but their semantics differ.
Direct Access with the Dot Operator
#include <stdio.h>
struct SValue {
int i;
float f;
};
union UValue {
int i;
float f;
};
int main(void) {
struct SValue sv = {1, 2.5f};
union UValue uv;
uv.i = 42; // currently using uv as an int
printf("Struct: i = %d, f = %.2f\n", sv.i, sv.f);
printf("Union as int: %d\n", uv.i);
uv.f = 4.5f; // now using uv as a float
printf("Union as float: %.2f\n", uv.f);
return 0;
}
// Output
Struct: i = 1, f = 2.50
Union as int: 42
Union as float: 4.50
Comparison:
- Struct:
sv.iandsv.fare both valid and meaningful at the same time. - Union: After assigning
uv.f, the previousuv.ivalue is no longer considered valid.
Access via Pointers (Arrow Operator)
The same access rules apply with pointers, but again, validity differs.
#include <stdio.h>
struct SCoord {
int x;
int y;
};
union UCoord {
int x;
int y;
};
int main(void) {
struct SCoord sp = {5, 10};
union UCoord up;
up.x = 7; // treat as x currently
struct SCoord *sptr = &sp;
union UCoord *uptr = &up;
printf("Struct via pointer: x = %d, y = %d\n", sptr->x, sptr->y);
printf("Union via pointer (x): %d\n", uptr->x);
uptr->y = 20; // overwrites same memory as x
printf("Union via pointer (y): %d\n", uptr->y);
return 0;
}
// Example Output (platform-dependent for union semantics)
Struct via pointer: x = 5, y = 10
Union via pointer (x): 7
Union via pointer (y): 20
Explanation: With the struct pointer, both x and y are independently valid. With the union pointer, assigning to y overwrites the memory that was previously used by x.
When to Use Structs vs Unions
-
Use a struct when:
- You need to store multiple related fields simultaneously.
- Each piece of data should remain independent.
-
Use a union when:
- You need to store one of several alternatives at a time.
- Memory usage is critical and you want a compact representation.
- You have a clear way (often an additional tag or enum) to track which member is currently valid.
Summary / Key Takeaways
In this lesson on Differences between Structs and Unions, you have learned:
- Structures keep independent members, allowing all fields to store meaningful data at the same time.
- Unions share memory among members, so only one logical value should be treated as valid at a time.
- Struct memory size is at least the sum of all member sizes (plus padding), while union size matches its largest member.
- Struct initialization can assign values to multiple members at once, all of which remain independently valid.
- Union data sharing means writing to one member overwrites the underlying bits used by other members.
- Member access syntax (
.and->) is the same for structs and unions, but the meaning differs because of memory sharing in unions. - Choosing between struct and union depends on whether you need multiple fields active together (struct) or one-of-many alternatives with minimal memory (union).