Unions union & Bit-Fields struct
Last Updated : Jan 2026
A union looks syntax-wise identical to a struct, but with one massive operational difference: all members share the exact same memory location.
- In a struct, the total size is at least the sum of all member sizes.
- In a union, the total size is equal to the size of its largest single member.
Union Declaration
You declare a union using the union keyword. The syntax is identical to a struct. This creates a template but does not allocate memory yet.
union SensorData {
int temperature; // 4 bytes
float humidity; // 4 bytes
char statusMsg[20]; // 20 bytes
};
Union Variables
Once declared, you can create union variables.
Important Initialization Rule: When initializing a union at the time of declaration, you can strictly only initialize its first member (unless you use C99 designated initializers).
// 1. Uninitialized variable
union SensorData data1;
// 2. Initializing the first member (temperature)
union SensorData data2 = { 72 };
// 3. C99 Designated Initializer (Initialize a specific member)
union SensorData data3 = { .humidity = 45.5f };
Accessing Union Members
You access union members exactly as you would with structures:
- Use the dot operator (.) for direct union variables.
- Use the arrow operator (->) if you have a pointer to a union.
#include <stdio.h>
#include <string.h>
union Packet {
int id;
float weight;
};
int main(void) {
union Packet box;
union Packet *ptr = &box;
// Access via dot operator
box.id = 1042;
printf("ID: %d\n", box.id);
// Access via arrow operator
ptr->weight = 12.5f;
// WARNING: 'id' is now corrupted because 'weight' overwrote its memory!
printf("New Weight: %.2f\n", ptr->weight);
return 0;
}
Union Memory Layout
A union can only hold a value for one member at a time. Writing to one member overwrites the data of all other members.
#include <stdio.h>
struct StructData {
char c; // 1 byte
int i; // 4 bytes
double d; // 8 bytes
}; // Size: 16 bytes (due to padding)
union UnionData {
char c; // 1 byte
int i; // 4 bytes
double d; // 8 bytes
}; // Size: 8 bytes (Size of largest member, 'd')
int main(void) {
union UnionData u;
u.i = 42;
printf("u.i: %d\n", u.i); // Output: 42
u.d = 3.14159; // Overwrites the memory shared with 'i' and 'c'
printf("u.d: %f\n", u.d); // Output: 3.14159
// Reading u.i now yields garbage because 'd' modified those shared bytes!
printf("u.i (corrupted): %d\n", u.i);
return 0;
}
UNION MEMORY LAYOUT (Shared 8 Bytes)
+------------------------------+
| char c | |
+------------------------------+
| int i | |
+------------------------------+
| double d |
+------------------------------+
Bit-Fields inside Structs
Standard variables like int or char take up whole bytes (at minimum 8 bits). But what if you only need to store a boolean flag (1 bit) or a value between 0 and 7 (3 bits)?
Bit-fields allow you to specify the exact number of bits a structure member occupies, packing multiple fields into a single underlying integer.
#include <stdio.h>
// Standard Struct: takes 12 bytes due to alignment padding
struct StandardFlags {
unsigned int is_active; // Needs 1 bit (uses 32 bits)
unsigned int is_admin; // Needs 1 bit (uses 32 bits)
unsigned int read_permission; // Needs 1 bit (uses 32 bits)
};
// Bit-Field Struct: packs all flags into 1 single integer!
struct CompactFlags {
unsigned int is_active : 1; // 1 bit (0 or 1)
unsigned int is_admin : 1; // 1 bit (0 or 1)
unsigned int read_permission : 1; // 1 bit (0 or 1)
unsigned int user_role : 3; // 3 bits (0 to 7)
}; // Size: Only 4 bytes total!
int main(void) {
struct CompactFlags flag = {1, 0, 1, 5};
printf("Size of CompactFlags: %zu bytes\n", sizeof(flag));
printf("User Role: %u\n", flag.user_role);
return 0;
}
Bit-Field Rules & Pitfalls:
- You cannot use the address-of operator (&) on a bit-field member (&flag.is_active) because CPU memory addresses point to bytes, not individual bits.
- Exceeding the bit capacity (e.g., storing 10 in a 3-bit field) causes integer overflow and silent truncation.
Tagged Unions
Combining Structs & Unions, The safest production pattern for unions is a Tagged Union (or Variant). You embed a union inside a struct along with an enum "tag" that keeps track of which union member is currently active.
#include <stdio.h>
typedef enum { INT_TYPE, FLOAT_TYPE, STRING_TYPE } DataType;
struct Value {
DataType type; // The "Tag" tracking active data
union {
int i_val;
float f_val;
char *s_val;
} data;
};
void printValue(struct Value *v) {
switch (v->type) {
case INT_TYPE: printf("Integer: %d\n", v->data.i_val); break;
case FLOAT_TYPE: printf("Float: %.2f\n", v->data.f_val); break;
case STRING_TYPE: printf("String: %s\n", v->data.s_val); break;
}
}
