C Unions
A union in C is a user-defined type whose members occupy the same storage. A union can describe several possible representations of a value, but a program normally treats only the member most recently written as the current value.
A structure reserves storage for all its members, while a union reserves enough storage for its largest member, including any alignment-related padding. This shared-memory behavior makes unions suitable for variant data, hardware registers, protocol fields, and other cases in which alternatives are mutually exclusive.
C Union Syntax and Declaration
Use the union keyword followed by an optional tag and a member list. The declaration defines a type; it does not create an object unless a variable is also declared.
union union_name {
datatype member1;
datatype member2;
// additional members...
};
The following declaration defines union Value and then creates a variable named data:
union Value {
int whole_number;
float decimal_number;
char text[20];
};
union Value data;
Members are accessed with the dot operator for a union object and the arrow operator for a pointer to a union.
data.whole_number = 25;
union Value *ptr = &data;
ptr->decimal_number = 4.5f;
How C Unions Share Memory
All members begin at the same address. Writing a member stores its representation in the union’s shared memory. A later write through another member reuses that memory and replaces some or all of the previous representation.
- Shared storage: The members do not receive independent blocks of memory.
- Largest-member capacity: The union is large enough and suitably aligned for any of its members.
- One intended interpretation: A program should track which member currently represents the stored value.
- Overwriting behavior: Assigning one member replaces the shared representation previously written through another member.
- No automatic type tracking: A plain union does not record which member was written last.
- Possible padding: The result of
sizeofcan exceed the size of the largest member because the implementation must satisfy alignment requirements.
Reading a different member from the one most recently written interprets the stored bytes as another type. The result can depend on the C implementation and the involved types, and some representations can be invalid. Do not use an inactive member as a general conversion mechanism. Use an explicit cast for numeric conversion and memcpy when bytes must be copied into another object representation.
Checking C Union Size and Member Addresses
This program prints the union size and the addresses of its members. The addresses compare equal because every member starts at the beginning of the union.
#include <stdio.h>
union Storage {
char character;
int number;
double measurement;
};
int main(void) {
union Storage item;
printf("Union size: %zu\n", sizeof item);
printf("Union address: %p\n", (void *)&item);
printf("character address: %p\n", (void *)&item.character);
printf("number address: %p\n", (void *)&item.number);
printf("measurement address: %p\n", (void *)&item.measurement);
return 0;
}
The numerical addresses and exact size vary by compiler, target architecture, and ABI. The program should therefore demonstrate the relationship between the addresses rather than be expected to produce one universal output.
C Structure vs Union
| Characteristic | Structure | Union |
|---|---|---|
| Member storage | Each member has its own storage | All members share storage |
| Simultaneous values | All members can represent values at once | Normally one member represents the current value |
| Minimum required capacity | Must accommodate every member plus alignment padding | Must accommodate its largest member plus any required padding |
| Effect of assigning a member | Other members retain their values | The shared representation is replaced |
| Typical purpose | Group fields that belong together | Represent one of several alternatives |
| Member access | Dot or arrow operator | Dot or arrow operator |
Example 1: Basic C Union vs Structure
This example compares two integer members in a structure with two integer members in a union. The structure retains both values independently. The union members begin at the same location and have the same type, so the program displays the integer representation most recently stored in the shared memory.
C Program
#include <stdio.h>
struct test1 {
int x, y;
};
union test {
int x, y;
};
int main() {
// Structure: both members have their own memory
struct test1 t1 = {1, 2};
// Union: both members share the same memory
union test t;
t.x = 3; // t.y also becomes 3 because they share the same memory
printf("After assigning x in union: %d %d\n", t.x, t.y);
t.y = 4; // Now t.x becomes 4 as well
printf("After assigning y in union: %d %d\n", t.x, t.y);
printf("Structure values: %d %d\n", t1.x, t1.y);
return 0;
}
Explanation:
struct test1reserves independent storage forxandy.union testplacesxandyin the same storage.- Assigning
t.xstores the representation of 3 in the shared memory. - Assigning
t.yreplaces that representation with the representation of 4. - The members of
t1remain 1 and 2 because they do not share storage.
Output:
After assigning x in union: 3 3
After assigning y in union: 4 4
Structure values: 1 2
Initializing a Union in C
Without a designator, a union initializer initializes its first named member. A designated initializer can select another member explicitly.
union Reading {
int count;
double temperature;
};
union Reading first = { 12 }; /* initializes count */
union Reading second = { .temperature = 23.5 }; /* initializes temperature */
After initialization, the program must use the member selected by the initializer unless it subsequently assigns another member.
Typedef Union in C
A typedef can provide a shorter name for a union type. It does not change the union’s shared-memory behavior.
typedef union {
long order_id;
char reference[16];
} OrderKey;
int main(void) {
OrderKey key = { .order_id = 1042 };
return key.order_id == 1042 ? 0 : 1;
}
This style is convenient when the type is used repeatedly. A tagged declaration such as typedef union OrderKey { ... } OrderKey; can be used when both a union tag and a typedef name are useful.
Tagged Union in C for Tracking the Active Member
A plain C union does not remember which member contains the intended value. A tagged union pairs the union with an enumeration that records the selected alternative. This is also called a discriminated union or variant record.
#include <stdio.h>
#define LABEL_CAPACITY 24
enum ValueKind {
VALUE_INTEGER,
VALUE_DECIMAL,
VALUE_LABEL
};
struct Value {
enum ValueKind kind;
union {
int integer;
double decimal;
char label[LABEL_CAPACITY];
} data;
};
void print_value(const struct Value *value) {
switch (value->kind) {
case VALUE_INTEGER:
printf("%d\n", value->data.integer);
break;
case VALUE_DECIMAL:
printf("%.2f\n", value->data.decimal);
break;
case VALUE_LABEL:
printf("%s\n", value->data.label);
break;
}
}
int main(void) {
struct Value price = {
.kind = VALUE_DECIMAL,
.data.decimal = 149.50
};
print_value(&price);
return 0;
}
The tag and union member must be updated together. If kind says VALUE_DECIMAL, the program must have stored and should read data.decimal. Keeping this invariant makes variant data easier to validate and maintain.
Anonymous Union Inside a C Structure
An anonymous union has no member name. Its fields can be accessed directly through the enclosing structure or union. Anonymous structures and unions are standardized in C11, although some compilers supported them earlier as extensions. Check the selected language mode when portability to older C implementations is required.
Example 2: Using a Union Inside a Structure
The following program embeds an anonymous union in struct student. Either the name or the roll number is stored in the shared area, while mark has separate storage and remains available in both cases.
C Program
#include <stdio.h>
struct student {
union { // Anonymous union: no name is required
char name[10];
int roll;
};
int mark;
};
int main() {
struct student stud;
char choice;
printf("\nEnter 'y' if you want to input name, or 'n' for roll number: ");
scanf(" %c", &choice); // Notice the space before %c to consume any leftover whitespace
if (choice == 'y' || choice == 'Y') {
printf("Enter name: ");
scanf("%s", stud.name);
printf("Name: %s\n", stud.name);
} else {
printf("Enter roll number: ");
scanf("%d", &stud.roll);
printf("Roll: %d\n", stud.roll);
}
printf("Enter marks: ");
scanf("%d", &stud.mark);
printf("Marks: %d\n", stud.mark);
return 0;
}
Explanation:
- The anonymous union provides the mutually exclusive
nameandrollmembers. - The selected input branch writes and reads the same union member.
- The
markfield is outside the union, so it does not overlap either identifier. - In production input code, the name field should be read with a width limit or with
fgetsto prevent an overlong value from exceeding the array capacity.
Sample Output:
Enter 'y' if you want to input name, or 'n' for roll number: y
Enter name: john
Name: john
Enter marks: 45
Marks: 45
Structures as C Union Members
A structure can be a union member. This becomes useful when a union offers two or more alternative record layouts. If the union contains only one structure member, as in the introductory example below, it demonstrates valid nesting but does not save memory by itself.
Example 3: Structure Inside a Union
This example declares a student structure and uses it as the sole member of union details. Because the structure’s own fields have independent storage, its name, roll number, and percentage can all be used together.
C Program
#include <stdio.h>
int main() {
// Define a structure to store student details
struct student {
char name[30];
int rollno;
float percentage;
};
// Define a union that contains the student structure
union details {
struct student s1;
};
union details set;
printf("Enter student details:\n");
printf("Enter name: ");
scanf("%s", set.s1.name);
printf("Enter roll number: ");
scanf("%d", &set.s1.rollno);
printf("Enter percentage: ");
scanf("%f", &set.s1.percentage);
printf("\nThe student details are:\n");
printf("Name: %s\n", set.s1.name);
printf("Roll Number: %d\n", set.s1.rollno);
printf("Percentage: %.2f\n", set.s1.percentage);
return 0;
}
Explanation:
struct studentgroups the name, roll number, and percentage.union detailscontains that complete structure as members1.- The nested fields are accessed with expressions such as
set.s1.rollno. - A practical variant would add a second union member representing a different type of record and pair the union with a tag.
Sample Output:
Enter student details:
Enter name: alice
Enter roll number: 101
Enter percentage: 88.5
The student details are:
Name: alice
Roll Number: 101
Percentage: 88.50
C Union Bit-Fields and Hardware Registers
A union is sometimes used to view a register as a complete integer or as named bit-fields. This technique is common in embedded code, but bit-field order, allocation, padding, integer width, and byte order can be implementation-defined. A layout that works for one compiler and processor is not automatically portable to another.
#include <stdint.h>
typedef union {
uint8_t value;
struct {
unsigned ready : 1;
unsigned error : 1;
unsigned mode : 2;
unsigned : 4;
} bits;
} StatusRegister;
Use such a declaration only when the compiler, target, and register specification define the required layout. Portable code often uses masks and shifts instead:
#define STATUS_READY_MASK 0x01u
#define STATUS_ERROR_MASK 0x02u
#define STATUS_MODE_MASK 0x0Cu
unsigned ready = (status & STATUS_READY_MASK) != 0u;
unsigned mode = (status & STATUS_MODE_MASK) >> 2;
When to Use a Union in C
- Variant records: A value can be one of several types, with an enumeration identifying the current alternative.
- Embedded systems: Mutually exclusive data can share limited memory, provided the hardware and compiler requirements are understood.
- Protocol or file representations: A field can have alternative layouts defined by a separate type code. External bytes still require validation and correct handling of byte order, alignment, and representation.
- Hardware access: A register can be represented in more than one way when the target ABI explicitly supports the chosen layout.
- Parser and interpreter values: Tokens or runtime values can store integers, floating-point numbers, strings, or other alternatives under a tag.
Do not choose a union only because it appears smaller. Use a structure when the fields must remain valid simultaneously. Use a tagged union when the program needs several alternatives and must determine safely which one is present.
Common C Union Mistakes
- Reading the wrong member: Track the intended member explicitly instead of assuming that the union remembers it.
- Expecting numeric conversion: Assigning a float and reading an integer interprets bytes; it does not perform the equivalent of an integer cast.
- Assuming an exact union size: Use
sizeoffor the target implementation because alignment can add padding. - Depending on bit-field order: Confirm compiler and target rules before mapping a hardware or wire format with bit-fields.
- Ignoring object lifetime responsibilities: A union containing pointers owns no memory automatically. The program must define allocation, copying, and cleanup rules.
- Using unsafe string input: A character array inside a union has the same capacity limits as any other array.
- Letting a tag and value disagree: Update the discriminant and the corresponding union member as one logical operation.
C Union QA Checklist
- The tutorial states that union members share storage and does not imply that all members retain independent values.
- Union size is described as implementation-dependent and capable of including alignment padding.
- Examples read the intended member or clearly explain the portability limits of interpreting another member.
- Variant examples include a tag that identifies the currently valid union member.
- Anonymous unions are identified with an appropriate C language-version or compiler-support note.
- Bit-field examples warn that layout can depend on the compiler and target architecture.
- String members and input examples account for array capacity and null termination.
- Structure-versus-union comparisons distinguish simultaneous fields from mutually exclusive alternatives.
C Union Frequently Asked Questions
What is a union in C?
A union is a user-defined C type in which all members share the same storage. It can represent any one of its declared member types, but the program must know which member currently provides the intended interpretation.
How is a union different from a structure in C?
A structure gives each member separate storage, so all its fields can hold values simultaneously. A union overlays its members in one storage area, so writing one member replaces the shared representation used by the others.
How does a C program know which union member is active?
A plain union does not store that information. The program can maintain a separate enumeration or other discriminant alongside the union. Combining both in a structure creates a tagged union.
What is a typedef union in C?
It is a union type given an alias with typedef. The alias allows declarations such as Value item; instead of union Value item;, depending on how the type was defined.
Can a C union contain a structure or an array?
Yes. Structure and array types can be union members. Each complete member occupies the shared union storage, and the union must be large enough and correctly aligned for its largest member.
C Union Summary
- A C union overlays all its members in one storage area.
- Its size is sufficient for its largest member and can include alignment padding.
- Assigning another member replaces the representation stored in the shared memory.
- A tagged union uses a separate value, commonly an enumeration, to identify the intended member.
- Anonymous unions, bit-fields, and representation-based techniques require attention to the C version, compiler, target, and portability requirements.
TutorialKart.com