Skip to main content

C Unions

A union is a special data type in C that allows you to store different data types in the same memory location. Unlike structures where each member has its own storage location, all members of a union share the same memory.

What is a Union?

A union in C is defined using the union keyword, similar to how structures are defined with the struct keyword.

c
union tag_name {
data_type member1;
data_type member2;
/* ... */
data_type memberN;
};

Declaring Union Variables

You can declare a union variable in several ways:

c
// Method 1: Define and declare separately
union Data {
int i;
float f;
char str[20];
};

union Data data;

// Method 2: Define and declare together
union Data {
int i;
float f;
char str[20];
} data;

// Method 3: Anonymous union
union {
int i;
float f;
char str[20];
} data;

Memory Allocation in Unions

The key difference between unions and structures is memory allocation:

  • In a structure, memory is allocated for all members individually.
  • In a union, memory is allocated based on the size of the largest member.

For example:

c
union Data {
int i; // 4 bytes
float f; // 4 bytes
char str[20]; // 20 bytes
};

The total size of the union would be 20 bytes (the size of the largest member, str).

tip

Use sizeof() operator to check the size of your union:

c
printf("Size of union: %lu bytes\n", sizeof(union Data));

Accessing Union Members

You access union members using the dot (.) notation or arrow (->) notation with pointers, just like structures:

c
union Data data;

data.i = 10; // Accessing using dot notation
printf("%d\n", data.i);

union Data *ptr = &data;
ptr->f = 220.5; // Accessing using arrow notation
printf("%.1f\n", ptr->f);

Important Characteristics of Unions

1. Shared Memory

The most significant aspect of unions is that all members share the same memory location:

c
union Data {
int i;
float f;
char str[20];
};

union Data data;

data.i = 10;
printf("data.i: %d\n", data.i);

data.f = 220.5;
printf("data.f: %.1f\n", data.f);
// The value of data.i is now corrupted because data.f overwrote
// the same memory location
printf("data.i: %d\n", data.i); // Will print a garbage value

2. One Active Member at a Time

Since all members share the same memory, only one member can contain a valid value at any time:

c
union Data data;

// Only one of these will contain the correct value
data.i = 10;
data.f = 220.5;
strcpy(data.str, "C Programming");

// The last assignment overwrites the memory, so only data.str is valid
printf("data.str: %s\n", data.str);

Use Cases for Unions

  1. Memory Conservation: When you need to store one of several data types but will only use one at a time.

  2. Type Punning: Viewing the same data in different ways.

c
union {
float f;
unsigned int i;
} u;

u.f = 1.5;
printf("Float: %f\n", u.f);
printf("As int bits: %08X\n", u.i); // View float bit pattern
  1. Variant Types: Creating variables that can hold different types of data.
c
typedef enum { INT_TYPE, FLOAT_TYPE, STRING_TYPE } ValueType;

typedef struct {
ValueType type;
union {
int i;
float f;
char str[20];
} value;
} Variant;

Variant v;
v.type = INT_TYPE;
v.value.i = 42;

Unions vs. Structures

FeatureUnionStructure
Memory allocationSize of the largest memberSum of all members' sizes
Member valuesOnly one member active at a timeAll members active simultaneously
UsageWhen only one property is needed at a timeWhen multiple properties are needed together

Common Pitfalls

Accessing Inactive Members

Reading a union member after writing to a different member yields unpredictable results:

c
union Data data;

data.i = 10;
printf("data.f: %f\n", data.f); // Undefined behavior!

Forgetting About Byte Order

When using unions for type punning, be aware of endianness (byte order) on different platforms.

Practical Example: Tagged Union

A common pattern is to use a "tagged union" where an enum indicates which union member is currently active:

c
enum DataType { INTEGER, FLOAT, STRING };

struct Value {
enum DataType type;
union {
int i;
float f;
char str[20];
} data;
};

struct Value val;
val.type = FLOAT;
val.data.f = 3.14;

// Safe way to access the value
switch (val.type) {
case INTEGER:
printf("Integer: %d\n", val.data.i);
break;
case FLOAT:
printf("Float: %f\n", val.data.f);
break;
case STRING:
printf("String: %s\n", val.data.str);
break;
}

Summary

  • Unions allow different data types to share memory space.
  • The size of a union is determined by its largest member.
  • Only one member of a union can be accessed safely at a time.
  • Unions are useful for memory optimization and type punning.
  • Always keep track of which union member is currently active to avoid unexpected behavior.
caution

Accessing a union member after writing to a different member can lead to unpredictable results. Always be mindful of which member was last assigned a value.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)