Docs
Unions
Unions in C
Table of Contents
- ā¢Introduction
- ā¢Union Declaration and Definition
- ā¢Memory Layout
- ā¢Accessing Union Members
- ā¢Unions vs Structures
- ā¢Practical Use Cases
- ā¢Tagged Unions
- ā¢Unions with Structures
- ā¢Anonymous Unions
- ā¢Pointer to Union
- ā¢Best Practices
- ā¢Common Pitfalls
- ā¢Summary
Introduction
A union is a special data type in C that allows storing different data types in the same memory location. Unlike structures where each member has its own memory, all union members share the same memory space.
Key Characteristics
- ā¢All members share the same memory location
- ā¢Size of union = size of largest member
- ā¢Only one member can hold a value at a time
- ā¢Useful for memory optimization and type punning
When to Use Unions
- ā¢Memory conservation: When you need to store only one of several types
- ā¢Type conversion: For viewing data as different types
- ā¢Variant data: When a variable can be one of several types
- ā¢Hardware access: For accessing bit patterns in different ways
Union Declaration and Definition
Basic Syntax
union UnionName {
type1 member1;
type2 member2;
// ... more members
};
Examples
// Simple union
union Number {
int integer;
float decimal;
char bytes[4];
};
// Union with different types
union Data {
int i;
double d;
char str[20];
};
// Using typedef
typedef union {
unsigned char bytes[4];
unsigned int word;
} WordBytes;
Declaration and Initialization
// Declare union variable
union Number num;
// Initialize with first member
union Number num1 = {42}; // Initializes 'integer'
// Designated initializer (C99+)
union Number num2 = {.decimal = 3.14}; // Initializes 'decimal'
// Direct assignment
union Number num3;
num3.integer = 100;
Memory Layout
How Unions Store Data
All members start at the same memory address:
union Number {
int integer; // 4 bytes
float decimal; // 4 bytes
char bytes[4]; // 4 bytes
};
Memory Layout (all at same location):
Address: 0x1000
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā 4 bytes ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā integer starts here ā
ā ā decimal starts here ā
ā ā bytes[0] starts here ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
sizeof(union Number) = 4 bytes (size of largest member)
Larger Union Example
union Data {
char c; // 1 byte
int i; // 4 bytes
double d; // 8 bytes
char str[20]; // 20 bytes
};
sizeof(union Data) = 20 bytes (size of str[])
Memory:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā 20 bytes ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ācā i ā d ā str[20] ā
ā ā ā ā ā
ā All start at address 0 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Size Comparison
struct S {
char c; // 1 byte + padding
int i; // 4 bytes
double d; // 8 bytes
};
union U {
char c; // 1 byte
int i; // 4 bytes
double d; // 8 bytes
};
sizeof(struct S) = 16 bytes (or more with padding)
sizeof(union U) = 8 bytes (size of double)
Accessing Union Members
Using Dot Operator
union Number num;
// Write to different members
num.integer = 42;
printf("As integer: %d\n", num.integer);
num.decimal = 3.14;
printf("As float: %f\n", num.decimal);
num.bytes[0] = 'A';
printf("First byte: %c\n", num.bytes[0]);
Important: Only One Member Valid at a Time
union Number num;
num.integer = 42;
printf("integer: %d\n", num.integer); // OK: 42
num.decimal = 3.14;
printf("decimal: %f\n", num.decimal); // OK: 3.14
// Warning: integer value is now corrupted!
printf("integer: %d\n", num.integer); // Garbage/undefined!
Using Arrow Operator with Pointers
union Number num;
union Number *ptr = #
ptr->integer = 100;
printf("Value: %d\n", ptr->integer);
Unions vs Structures
Comparison Table
| Feature | Structure | Union |
|---|---|---|
| Memory | Each member has own space | All members share space |
| Size | Sum of all members (+padding) | Size of largest member |
| Access | All members valid simultaneously | Only one member valid at a time |
| Use Case | Group related data | Store one of several types |
Visual Comparison
struct Example { union Example {
int a; int a;
float b; float b;
char c; char c;
}; };
Memory: Memory:
āāāāāāāāāāā ā a āāāāāāāāāāā ā a, b, c
ā int a ā ā shared ā (all overlap)
āāāāāāāāāā⤠ā b ā space ā
ā float b ā āāāāāāāāāāā
āāāāāāāāāā⤠ā c Size: 4 bytes
ā char c ā
āāāāāāāāāāā
Size: 12 bytes (with padding)
Code Example
#include <stdio.h>
struct DataStruct {
int i;
float f;
char c;
};
union DataUnion {
int i;
float f;
char c;
};
int main() {
printf("struct size: %zu bytes\n", sizeof(struct DataStruct));
printf("union size: %zu bytes\n", sizeof(union DataUnion));
// Structure: all members work together
struct DataStruct ds;
ds.i = 42;
ds.f = 3.14;
ds.c = 'A';
printf("Struct: i=%d, f=%.2f, c=%c\n", ds.i, ds.f, ds.c);
// Union: only last assigned member is valid
union DataUnion du;
du.i = 42;
du.f = 3.14; // This overwrites i
printf("Union after f=3.14: i=%d (garbage), f=%.2f\n", du.i, du.f);
return 0;
}
Practical Use Cases
Use Case 1: Type Punning (Viewing Data as Different Types)
// View float as integer bits
union FloatInt {
float f;
unsigned int bits;
};
union FloatInt fi;
fi.f = 3.14f;
printf("Float 3.14 as hex: 0x%08X\n", fi.bits);
// Examine float byte by byte
union FloatBytes {
float f;
unsigned char bytes[4];
};
union FloatBytes fb;
fb.f = 1.0f;
printf("Bytes of 1.0f: ");
for (int i = 0; i < 4; i++) {
printf("%02X ", fb.bytes[i]);
}
// Output: 00 00 80 3F (little-endian IEEE 754)
Use Case 2: IP Address Representation
union IPAddress {
unsigned int address; // 32-bit address
unsigned char octets[4]; // 4 octets
};
union IPAddress ip;
ip.address = 0xC0A80001; // 192.168.0.1 in hex
printf("IP: %d.%d.%d.%d\n",
ip.octets[3], ip.octets[2],
ip.octets[1], ip.octets[0]);
// Output: 192.168.0.1
Use Case 3: Hardware Register Access
union StatusRegister {
unsigned char value;
struct {
unsigned char ready : 1;
unsigned char error : 1;
unsigned char overflow : 1;
unsigned char : 5; // unused bits
} bits;
};
union StatusRegister status;
status.value = 0x03; // Set from hardware
if (status.bits.ready) printf("Device ready\n");
if (status.bits.error) printf("Error occurred\n");
Use Case 4: Network Protocol Headers
union NetworkPacket {
char raw[64];
struct {
unsigned char version;
unsigned char type;
unsigned short length;
unsigned int source;
unsigned int dest;
char payload[52];
} header;
};
// Read raw bytes, access as structured header
union NetworkPacket packet;
// ... receive raw bytes into packet.raw ...
printf("Packet type: %d, length: %d\n",
packet.header.type, packet.header.length);
Tagged Unions
Introduction
A tagged union (or discriminated union) combines a union with a tag indicating which member is currently valid.
Implementation
#include <stdio.h>
#include <string.h>
// Tag enumeration
enum DataType {
TYPE_INT,
TYPE_FLOAT,
TYPE_STRING
};
// Tagged union structure
struct Variant {
enum DataType type; // Tag
union {
int i;
float f;
char str[20];
} data; // Union
};
// Constructor functions
struct Variant makeInt(int value) {
struct Variant v;
v.type = TYPE_INT;
v.data.i = value;
return v;
}
struct Variant makeFloat(float value) {
struct Variant v;
v.type = TYPE_FLOAT;
v.data.f = value;
return v;
}
struct Variant makeString(const char *value) {
struct Variant v;
v.type = TYPE_STRING;
strncpy(v.data.str, value, sizeof(v.data.str) - 1);
v.data.str[sizeof(v.data.str) - 1] = '\0';
return v;
}
// Print function
void printVariant(struct Variant v) {
switch (v.type) {
case TYPE_INT:
printf("Integer: %d\n", v.data.i);
break;
case TYPE_FLOAT:
printf("Float: %.2f\n", v.data.f);
break;
case TYPE_STRING:
printf("String: %s\n", v.data.str);
break;
}
}
int main() {
struct Variant values[3];
values[0] = makeInt(42);
values[1] = makeFloat(3.14);
values[2] = makeString("Hello");
for (int i = 0; i < 3; i++) {
printVariant(values[i]);
}
return 0;
}
Use Case: JSON-like Values
enum JSONType {
JSON_NULL,
JSON_BOOL,
JSON_NUMBER,
JSON_STRING,
JSON_ARRAY,
JSON_OBJECT
};
struct JSONValue {
enum JSONType type;
union {
int boolean;
double number;
char *string;
struct JSONValue *array;
// ... more types
};
};
Unions with Structures
Structure Inside Union
union Message {
struct {
int code;
char text[100];
} error;
struct {
int id;
float value;
} data;
struct {
char type;
int count;
} control;
};
Union Inside Structure (Most Common)
struct Packet {
int type; // Determines which union member to use
int length;
union {
struct { int x, y; } position;
struct { float r, g, b; } color;
char text[256];
} payload;
};
// Usage
struct Packet p;
p.type = 1; // Position type
p.payload.position.x = 100;
p.payload.position.y = 200;
p.type = 2; // Color type
p.payload.color.r = 1.0f;
p.payload.color.g = 0.5f;
p.payload.color.b = 0.0f;
Anonymous Unions
C11 Feature
Anonymous unions allow accessing members directly without union name:
struct Widget {
int type;
union { // Anonymous union
int intValue;
float floatValue;
char *stringValue;
}; // No name!
};
// Direct access without union name
struct Widget w;
w.type = 0;
w.intValue = 42; // Not: w.data.intValue
w.type = 1;
w.floatValue = 3.14; // Direct access
Before C11 Comparison
// C99 style (with union name)
struct WidgetOld {
int type;
union {
int intValue;
float floatValue;
} data; // Named
};
struct WidgetOld w;
w.data.intValue = 42; // Need 'data.'
// C11 style (anonymous)
struct WidgetNew {
int type;
union {
int intValue;
float floatValue;
}; // Anonymous
};
struct WidgetNew w;
w.intValue = 42; // Direct access
Pointer to Union
Basic Usage
union Data {
int i;
float f;
char c;
};
union Data data;
union Data *ptr = &data;
// Access with arrow operator
ptr->i = 42;
ptr->f = 3.14;
Dynamic Allocation
union Data *ptr = malloc(sizeof(union Data));
if (ptr != NULL) {
ptr->f = 2.718;
printf("Value: %.3f\n", ptr->f);
free(ptr);
}
Array of Unions
union Data *array = malloc(10 * sizeof(union Data));
if (array != NULL) {
for (int i = 0; i < 10; i++) {
array[i].i = i * 10;
}
free(array);
}
Best Practices
1. Always Track Active Member
// GOOD: Use a tag
struct SafeUnion {
enum { INT, FLOAT, STRING } type;
union {
int i;
float f;
char str[20];
} value;
};
// BAD: No way to know which member is valid
union UnsafeUnion {
int i;
float f;
};
2. Initialize Before Use
// GOOD
union Data d = {0}; // Initialize
d.i = 42;
// BAD
union Data d;
printf("%d\n", d.i); // Undefined!
3. Use Designated Initializers
// C99 designated initializer
union Number n = {.f = 3.14}; // Clear which member
// Less clear
union Number n = {3}; // Which member?
4. Document the Protocol
/*
* NetworkMessage union usage:
* - type == 1: use request member
* - type == 2: use response member
* - type == 3: use error member
*/
union NetworkMessage {
struct RequestData request;
struct ResponseData response;
struct ErrorData error;
};
5. Be Careful with Alignment
// May have padding issues
union Aligned {
char c;
double d; // 8-byte alignment
};
// sizeof is 8, not 1
Common Pitfalls
Pitfall 1: Reading Wrong Member
union Data {
int i;
float f;
};
union Data d;
d.i = 42;
printf("%f\n", d.f); // Garbage! Not the integer 42.0
Pitfall 2: Assuming Type Conversion
union Convert {
int i;
float f;
};
union Convert c;
c.i = 100;
// c.f is NOT 100.0f!
// It's the float interpretation of bit pattern 0x64
Pitfall 3: String Buffer Overflow
union Data {
char str[10];
int i;
};
union Data d;
strcpy(d.str, "This is too long!"); // Buffer overflow!
Pitfall 4: Endianness Issues
union {
unsigned int word;
unsigned char bytes[4];
} u;
u.word = 0x12345678;
// On little-endian: bytes = {0x78, 0x56, 0x34, 0x12}
// On big-endian: bytes = {0x12, 0x34, 0x56, 0x78}
Summary
Key Concepts
| Concept | Description |
|---|---|
| Union | Data type where all members share same memory |
| Size | Equal to size of largest member |
| Active Member | Only one member holds valid data at a time |
| Tagged Union | Union + type indicator for safety |
| Type Punning | Viewing same bits as different types |
Syntax Reference
// Declaration
union Name {
type1 member1;
type2 member2;
};
// Variable
union Name var;
// Initialization
union Name var = {value}; // First member
union Name var = {.member = value}; // Designated (C99)
// Access
var.member // Direct
ptr->member // Through pointer
// Size
sizeof(union Name) // Size of largest member
Quick Comparison
Structure (struct): Union (union):
āāāāāāāāāāā āāāāāāāāāāā
ā member1 ā ā All ā
āāāāāāāāāā⤠ā members ā
ā member2 ā ā share ā
āāāāāāāāāā⤠ā this ā
ā member3 ā ā space ā
āāāāāāāāāāā āāāāāāāāāāā
Size: sum of members Size: largest member
All members valid One member valid
Navigation
| Previous | Up | Next |
|---|---|---|
| Pointers to Structures | Structures and Unions | Bit Fields |