union
A data structure in which multiple members share the same memory region. It is used to interpret the same data as different types, or to reduce memory usage. The size of a union equals the size of its largest member.
Syntax
union UnionTagName {
type memberName1;
type memberName2;
};
// Declare a union variable.
union UnionTagName variableName;
// Access a member (same notation as struct).
variableName.memberName;
pointer->memberName;
// Initialization can only target the first member (C89).
// C99 and later allow designated initializers.
union UnionTagName variableName = {.memberName = value};
union vs. struct
| Item | struct | union |
|---|---|---|
| Memory | Allocates the total size of all members (plus padding). | Allocates only enough space for the largest member. |
| Access | All members can be accessed independently. | Only the last written member holds a valid value. |
| Use case | Used to group multiple pieces of data together. | Used to interpret the same memory as different types. |
Sample Code
sample_union.c
#include <stdio.h>
#include <stdint.h>
/* A union that interprets a number as different types. */
union Number {
int i;
float f;
unsigned char bytes[4];
};
/* Tagged union: tracks which member is active using an enum. */
typedef enum { TYPE_INT, TYPE_DOUBLE, TYPE_STRING } ValueType;
typedef struct {
ValueType type;
union {
int i_val;
double d_val;
char s_val[64];
} data;
} Value;
void print_value(const Value *v) {
switch (v->type) {
case TYPE_INT: printf("int: %d\n", v->data.i_val); break;
case TYPE_DOUBLE: printf("double: %f\n", v->data.d_val); break;
case TYPE_STRING: printf("string: %s\n", v->data.s_val); break;
}
}
int main(void) {
union Number n;
n.i = 42;
printf("int value: %d\n", n.i);
/* Writing as float invalidates the int member. */
n.f = 3.14f;
printf("float value: %f\n", n.f);
/* Check endianness via bytes member. */
n.i = 1;
printf("bytes[0] = %u\n", n.bytes[0]); /* 1 on a little-endian system */
printf("Size of union Number: %zu bytes\n", sizeof(union Number));
/* Tagged union for variant data. */
Value v1 = {TYPE_INT, {.i_val = 100}};
Value v2 = {TYPE_DOUBLE, {.d_val = 3.14}};
print_value(&v1);
print_value(&v2);
return 0;
}
Run the following command:
gcc union.c -o union ./union int value: 42 float value: 3.140000 bytes[0] = 1 Size of union Number: 4 bytes int: 100 double: 3.140000
Checking Endianness
A union lets you inspect the byte order (endianness) of an integer value — a technique commonly used in embedded and network programming.
union_endian.c
#include <stdio.h>
#include <stdint.h>
union Endian {
uint32_t value;
unsigned char bytes[4];
};
int main(void) {
union Endian e;
e.value = 0x01020304;
printf("value = 0x%08X\n", e.value);
printf("bytes: [0]=%02X [1]=%02X [2]=%02X [3]=%02X\n",
e.bytes[0], e.bytes[1], e.bytes[2], e.bytes[3]);
if (e.bytes[0] == 0x04) {
printf("Little-endian (least significant byte first)\n");
} else {
printf("Big-endian (most significant byte first)\n");
}
/* View the bit pattern of a float as uint32_t. */
union { float f; uint32_t u; } fu;
fu.f = 1.0f;
printf("Bit pattern of 1.0f: 0x%08X\n", fu.u);
return 0;
}
Run the following command:
gcc union_endian.c -o union_endian ./union_endian value = 0x01020304 bytes: [0]=04 [1]=03 [2]=02 [3]=01 Little-endian (least significant byte first) Bit pattern of 1.0f: 0x3F800000
Common Mistakes
Common Mistake: Reading a Member That Was Not Written
In a union, only the last-written member holds a valid value. Reading a different member is undefined behavior (except via an unsigned char array).
union_undef_ng.c
#include <stdio.h>
union Data {
int i;
float f;
};
int main(void) {
union Data d;
/* NG: writing float then reading int is undefined behavior */
d.f = 3.14f;
/* printf("%d\n", d.i); */ /* undefined behavior */
/* OK: read only the member you wrote */
d.i = 42;
printf("int: %d\n", d.i); /* OK: wrote i, read i */
d.f = 2.71f;
printf("float: %f\n", d.f); /* OK: wrote f, read f */
return 0;
}
Run the following command:
gcc union_undef_ng.c -o union_undef_ng ./union_undef_ng int: 42 float: 2.710000
Overview
The defining characteristic of a union is that all its members share the same memory address, allowing a single variable to be interpreted as multiple types. Unions are commonly used in low-level byte manipulation such as reading hardware registers or parsing network packets.
Reading a member other than the last one written is "undefined behavior" according to the C standard. However, reading byte representations through an unsigned char member is explicitly permitted by the standard. Use the tagged union pattern when you need to safely work with multiple types.
For a related data structure where all members are independent, use struct. To enumerate a set of related constants, see enum.
If you find any errors or copyright issues, please contact us.