Introduction
A tagged union is a programming technique that combines a union with a tag (usually an enumeration) to indicate which union member currently contains valid data.
Since a union can store only one active member at a time, the C language itself does not keep track of which member is currently in use. A tagged union solves this problem by storing an additional value that identifies the active member.
Tagged unions are widely used in compilers, operating systems, embedded systems, interpreters, network protocols, JSON parsers, and many other low-level applications.
Why Do We Need a Tagged Union?
Consider the following union:
Example
union Value
{
int integer;
float decimal;
char character;
};
Although the union can store three different data types, there is no information indicating which member currently contains valid data.
For example:
Example
union Value value;
value.integer = 100;
If another function later receives value, it has no way to determine whether it contains an integer, a floating-point number, or a character.
What Is a Tag?
A tag is an additional variable that records which union member is currently valid.
The tag is usually implemented as an enumeration.
Example
enum ValueType
{
INTEGER,
FLOAT,
CHARACTER
};
The enumeration values identify which union member should be used.
Basic Tagged Union
A tagged union combines an enumeration and a union inside the same structure.
Example
struct Value
{
enum ValueType type;
union
{
int integer;
float decimal;
char character;
};
};
The type field indicates which union member currently contains valid data.
Using a Tagged Union
Example
struct Value value;
value.type = INTEGER;
value.integer = 100;
Because type is set to INTEGER, other code knows that integer is the active member.
Complete Example
Example
#include <stdio.h>
enum ValueType
{
INTEGER,
FLOAT,
CHARACTER
};
struct Value
{
enum ValueType type;
union
{
int integer;
float decimal;
char character;
};
};
int main(void)
{
struct Value value;
value.type = FLOAT;
value.decimal = 3.14f;
if (value.type == FLOAT)
{
printf("%.2f\n", value.decimal);
}
return 0;
}
Output
3.14
Using switch with Tagged Unions
A switch statement is commonly used to process tagged unions.
Example
switch (value.type)
{
case INTEGER:
printf("%d\n", value.integer);
break;
case FLOAT:
printf("%f\n", value.decimal);
break;
case CHARACTER:
printf("%c\n", value.character);
break;
}
The tag determines which union member should be accessed.
Memory Layout
A tagged union consists of two parts:
- The tag (usually an enum).
- The shared-memory union.
A simplified memory layout looks like this:
Example
+----------------+
| enum type |
+----------------+
| |
| union |
| (shared) |
| |
+----------------+
The structure occupies more memory than the union alone because the tag requires additional storage.
Advantages of Tagged Unions
- Identifies the active union member.
- Improves program safety.
- Makes code easier to understand.
- Supports multiple data types efficiently.
- Reduces the chance of reading the wrong union member.
Disadvantages
- Requires additional memory for the tag.
- The programmer must update the tag correctly.
- The compiler does not automatically verify that the tag matches the active member.
Tagged Union vs Ordinary Union
| Tagged Union | Ordinary Union |
|---|---|
| Includes an enum tag. | No tag. |
| Knows the active member. | Active member is unknown. |
| Safer. | More error-prone. |
| Slightly larger. | Uses less memory. |
Real-World Applications
Tagged unions are widely used in:
- Abstract syntax trees (ASTs).
- Programming language interpreters.
- Compilers.
- JSON parsers.
- XML parsers.
- Protocol message handling.
- Operating systems.
- Embedded systems.
Tagged Union with typedef
Many projects use typedef to simplify tagged union declarations.
Example
typedef enum
{
INTEGER,
FLOAT
} ValueType;
typedef struct
{
ValueType type;
union
{
int integer;
float decimal;
};
} Value;
This produces cleaner declarations throughout the program.
Common Beginner Mistakes
- Forgetting to update the tag after changing the active member.
- Reading a member that does not match the current tag.
- Assuming the compiler validates the tag automatically.
- Using raw integers instead of an enumeration for the tag.
- Thinking tagged unions eliminate memory sharing.
Best Practices
- Always use an enumeration as the tag.
- Update the tag immediately before or after changing the active union member.
- Use a
switchstatement to process tagged unions. - Keep the tag and the union together in the same structure.
- Document which tag corresponds to each union member.
Summary
A tagged union combines an enumeration with a union to record which member currently contains valid data. This simple technique makes unions much safer and easier to use while preserving their memory efficiency.
| Component | Purpose |
|---|---|
| Enumeration | Identifies the active member. |
| Union | Stores one value using shared memory. |
| Structure | Keeps the tag and union together. |
| Main Benefit | Safe handling of multiple data types. |
| Typical Use | Compilers, interpreters, protocols, embedded systems. |
Tagged unions are one of the most important design patterns in C programming. They provide a safe and efficient way to represent data that may have different types while minimizing memory usage.