Labs ICT
Pro Login

Unions

A union is a special data type where all members share the same memory location. At any given time, only one member can hold a value. The union takes up only as much space as its largest member. This is useful for memory-efficient data representations and low-level programming.

Union Basics

All members of a union overlap in memory. Writing to one member overwrites the others. The size of the union is the size of its largest member:

#include <iostream>

union Data {
  int i;
  float f;
  char c;
};

int main() {
  Data d;

  d.i = 42;
  std::cout << "As int: " << d.i << "\n";

  d.f = 3.14f;
  std::cout << "As float: " << d.f << "\n";
  std::cout << "Int is now corrupted: " << d.i << "\n";

  d.c = 'A';
  std::cout << "As char: " << d.c << "\n";

  std::cout << "Size of union: " << sizeof(d) << " bytes\n";
  return 0;
}

When we set d.f = 3.14f, the integer value is overwritten. The union size is 4 bytes (the size of int and float on most systems) because char is only 1 byte.

Size in Memory

The union size equals its largest member. Compare this to a struct, which would be the sum of all its members (plus padding). Unions save memory when you need to store one of several possible types:

#include <iostream>

union Variant {
  int i;
  double d;
  char str[32];
};

struct VariantStruct {
  int i;
  double d;
  char str[32];
};

int main() {
  std::cout << "Union size: " << sizeof(Variant) << " bytes\n";
  std::cout << "Struct size: " << sizeof(VariantStruct) << " bytes\n";
  return 0;
}

The union holds the largest member — the char[32] — so it takes 32 bytes. The struct would take at least 41 bytes (4 + 8 + 32) plus padding. That is a significant saving.

Unions with Discriminators

The problem with unions is that you never know which member is currently active. The common solution is to pair the union with a separate variable (discriminator) that tracks the active type:

#include <iostream>

enum ValueType { VAL_INT, VAL_DOUBLE, VAL_TEXT };

struct TaggedValue {
  ValueType type;
  union {
    int i;
    double d;
    char text[20];
  };
};

int main() {
  TaggedValue tv;
  tv.type = VAL_INT;
  tv.i = 42;

  if (tv.type == VAL_INT) {
    std::cout << "Integer: " << tv.i << "\n";
  }

  tv.type = VAL_DOUBLE;
  tv.d = 2.718;

  if (tv.type == VAL_DOUBLE) {
    std::cout << "Double: " << tv.d << "\n";
  }
  return 0;
}

This pattern is called a tagged union or discriminated union. Always check the discriminator before reading the union value — otherwise you will interpret raw bytes as the wrong type.