Seitenleiste

Wiki

Community

Datentypen

Zählbare

Fließkomma

Datenstrukturen

Attribute

Speicherklassen

Typqualifizierer

Unions

Unions gleichen im Aufbau zunächst Strukturen. Im Gegensatz zu Strukturen, werden die Daten jedoch nicht hintereinander abgelegt, sondern alle auf die gleiche Stelle gelegt. Das bedeutet, dass ein Datensatz, den anderen überschreibt!

Das mag im ersten Moment sinnfrei klingen, es gibt jedoch sinnvolle Anwendungen für Unions.

Definition

Ein Union wird genau wie eine Struktur aufgebaut, lediglich wird statt struct das Schlüsselwort union verwendet:

union BeispielUnion
{
  Datentyp Variable;
  Datentyp2 Variable2;
  ...
};

Verwendung

Eine Union kann beliebig viele Elemente besitzen, dazu gehören auch wieder Unions oder auch Strukturen. Fragt man mit sizeof die Größe der Union ab, so ist sie immer genau so groß, wie das größte Element in ihr.

Auch der Zugriff auf ein Union entspricht dem Umgang mit Strukturen:

union BeispielUnion test;

test.Variable = 7;

Datentyp2 temp = test.Variable2;

Konvertierungen

Als Beispiel: Möchte man sich das Bitmuster einer Double-Variable ansehen, so kann man sie nicht einfach in eine Integer-Variable casten, da eine Double-Variable doppelt so groß ist, wie eine Integer-Variable. Man müsste sie gewissermaßen auf zwei Integer casten können. Und das geht mit union:

union DoubleConverter
{
  double        DoubleValue;
  unsigned int  IntArray[2];
  char          CharArray[10];
};

Nun kann man eine beliebige Double-Variable im Union speichern. Da das IntArray an genau der gleichen Stelle im Speicher beginnt, lässt sich nun der Double-Wert, welcher 8 Byte im Speicher belegt, über die zwei Integer-Werte, welche jeweils 4 Byte an der gleichen Stelle im Speicher belegen abrufen. Das Char-Array ist sogar noch etwas größer. Hier kann man die Werte Byte-Weise abrufen und sogar noch zwei weitere Chars speichern. Das Union ist damit 10 Byte groß, denn das größte Element, das Char-Array, ist 10 Byte groß.

int main( void )
{
  union DoubleConverter dc;
 
  dc.DoubleValue = 123.0;
 
  printf( "Hexadezimaldarstellung: %x | %x\n", dc.IntArray[0], dc.IntArray[1] );
};

Datenhaltung unterschiedlicher Daten

Benötigt man eine Datenstruktur, die abstrakte Gemeinsamkeiten, aber dafür unterschiedliche Daten verwenden, so lässt sich dies ebenfalls über Unions regeln. Nehmen wir als Beispiel ein grafisches Objekt, das entweder ein Kreis, eine Linie oder ein Rechteck sein soll:

#define GO_TYPE_CIRCLE 1
#define GO_TYPE_LINE   2
#define GO_TYPE_RECT   3
 
struct GraficObject
{
  /* Gemeinsam genutzte Daten */
 
  int type;
 
  int x, y;
 
  /* Bisher 3 int => 12 Bytes */
 
  union 
  {
    struct
    {
      int   width;
      int   height;
    } rectancle;          // 8 Bytes
 
    struct
    {
      int   destX;
      int   destY;
    } line;               // 8 Bytes
 
    struct
    {
      int   radius;
    } circle;             // 4 Bytes
  } data;
};

Jede dieser Strukturen vom Typ GrafikObject ist genau 20 Byte groß. Der Datenbereich, der hier im Union 'data' beschrieben ist, wird je nach Typ unterschiedlich interpretiert, je nachdem ob man ihn als data.rectancle, data.line oder data.circle anspricht.

In C++ würde man hier vermutlich Ableitungen bilden und die gemeinsam genutzten Daten in eine Oberklasse setzen. Damit wird eine Klasse 'Circle' sogar nur 16 Byte groß - man spart also sogar vier Byte. Da man nun aber nicht mehr weiß, wie groß ein GraficObject ist, kann man Linien, Rechtecke und Kreise nun nicht mehr in Arrays speichern. Überall, wo Daten serialisiert werden, zum Beispiel beim Speichern in eine Daten, verliert man die Garantie, dass das nachfolgende Grafikobjekt 20 Bytes später folgt.

Unions haben damit auch in Zeiten von Objektorientierung durchaus noch ihre Existenzberechtigung, wenn es darum geht, den Zugriff auf Strukturen zu optimieren.


Autorendiskussion