15.9 Unions
Eine weitere Möglichkeit, Daten zu strukturieren, sind Unions (auch Varianten genannt). Abgesehen von einem anderen Schlüsselwort, bestehen zwischen Unions und Strukturen keine syntaktischen Unterschiede. Der Unterschied liegt in der Art und Weise, wie mit dem Speicherplatz der Daten umgegangen wird. Hier sehen Sie ein Beispiel für die Speicherplatzbelegung einer Struktur:
struct test1 { char a; int b; double c; };
Abbildung 15.8 Speicherbelegung bei einer Struktur
Diese Struktur benötigt 16 Byte an Speicher. Jetzt folgt das gleiche Beispiel mit dem Schlüsselwort union:
union test2 { char a; int b; double c; };
Intern sieht dies mit union folgendermaßen aus:
Abbildung 15.9 Speicherbelegung einer Struktur mit dem Schlüsselwort »union«
Hier sehen Sie ein Listing, das den Unterschied demonstrieren soll:
/* union1.c */ #include <stdio.h> #include <stdlib.h> struct test1 { char a; int b; double c; }; union test2 { char a; int b; double c; }; int main(void) { printf("struct benoetigt %d Bytes\n", sizeof(struct test1)); printf("Union benoetigt %d Bytes\n", sizeof(union test2)); return EXIT_SUCCESS; }
Sie sehen richtig: Die Struktur mit dem Schlüsselwort union besitzt jetzt nur noch acht Bytes. Dies verursachen das Schlüsselwort union und das größte Element in der Struktur, double. Dieser ersparte Speicherplatz wird allerdings mit dem Nachteil erkauft, dass immer nur ein Element in dieser Struktur verwendet werden kann. Beispielsweise wird dem Strukturelement int b der Wert 100 übergeben:
text.b = 100;
Somit beträgt die Speichergröße der Struktur trotzdem acht Bytes für einen Integer, da der größte Datentyp in der union nämlich double lautet. Übergeben Sie jetzt an double c den Wert 10.55:
text.c = 10.55;
Jetzt können Sie auf den Wert von int b nicht mehr zugreifen, da dieser von double c überlappt wurde. Zwar kann es immer noch sein, dass int b weiterhin den Wert 100 ausgibt, aber dies wäre Zufall, denn der Speicherbereich wurde überdeckt. Das Verhalten ist in diesem Fall undefiniert.
Welchen Vorteil hat es, wenn immer auf ein Element einer Struktur zugegriffen werden kann? Der wesentliche Vorteil liegt in der Anwendung von Union zum Einsparen von Speicherplatz bei der Verarbeitung großer Strukturen; beispielsweise bei Strukturen, wo bestimmte Elemente niemals miteinander auftreten. Ein Beispiel wäre ein Computerspiel, bei dem immer einer gegen einen, Auge um Auge, kämpft. In dem folgenden Codeabschnitt wurde eine Struktur mit vier verschiedenen Gegnercharakteren erstellt:
struct gegner { union { struct { char name[20]; int power; unsigned char leben; unsigned int geschwindigkeit; } fighter1; struct { char name[20]; int power; unsigned char leben; unsigned int geschwindigkeit; } fighter2; struct { char name[20]; int power; unsigned char leben; unsigned int geschwindigkeit; } fighter3; struct { char name[20]; int power; unsigned char leben; unsigned int geschwindigkeit; } fighter4; }; } dat;
Damit wird immer nur auf eine Struktur zugegriffen, was in diesem Beispiel ausreichend ist, denn es sollten immer nur die Daten eines Gegners im Speicher sein – eben die, die im Augenblick nötig sind. So könnten Sie z. B. den fighter4 mit Werten initialisieren:
strcpy(dat.fighter4.name, "Superman"); dat.fighter4.power = 5; dat.fighter4.leben = 1; dat.fighter4.geschwindigkeit = NORMAL;
Der Zugriff auf die einzelnen Elemente erfolgt wie bei den normalen Strukturen.
Kommen wir zum Programm der Adressverwaltung zurück, denn dieses eignet sich auch prima für eine Union:
struct adres { union { struct { char vname[20]; char nname[20]; long PLZ; char ort[20]; int geburtsjahr; } privat; struct { char vname[20]; char nname[20]; long PLZ; char ort[20]; char sternzeichen[20]; int geburtsjahr; } geschaeftlich; }; } adressen[100];
Hiermit können 100 Adressen gespeichert werden. Mithilfe dieser Union wird das Private vom Geschäftlichen getrennt.
Natürlich können Sie mit dem Schlüsselwort union auch ein Array von Unions realisieren. Der Zugriff und die Initialisierung erfolgen genau so, wie ich bei den Strukturen bereits beschrieben habe; ein Beispiel:
/* union2.c */ #include <stdio.h> #include <stdlib.h> union number { float x; int y; }; int main(void) { union number mixed[2]; mixed[0].x = 1.123; mixed[1].y = 123; mixed[2].x = 2.345; printf("%.2f\t%d\t%.2f\n", mixed[0].x, mixed[1].y, mixed[2].x); return EXIT_SUCCESS; }
Wenn Sie eine Union initialisieren, erlaubt ANSI C nur einen Initialisierer. Standardmäßig bedeutet dies, dass immer das erste Element der Union initialisiert wird:
union 2_D { int x; int y; }; // coordinate.x hat den Wert 123 union 2_D coordinate = { 123 };
Haben Sie eine Union bereits initialisiert und weisen Sie einem anderem Union-Element einen Wert zu, wird der Wert des anderen Union-Elements überschrieben:
union 2_D { int x; int y; }; ... union 2_D coordinate; coordinate.x = 20; ... coordinate.y = 40; // Element x wird hier überschrieben.
Laut dem C99-Standard können Sie auch bei den Unions (wie bei den Strukturen) den Elementbezeichner verwenden, um festzulegen, welches Element in der Union initialisiert werden soll:
union 2_D { int x; int y; }; ... union 2_D coordinate = { .y = 99 };
Beim Zuweisen eines vorhandenen und initialisierten Union-Objektes an ein anderes wird das neue Union-Objekt mit demselben Typ initialisiert:
// wird mit dem vorhandenen Objekt desselben // Typs initialisiert union 2_D coordinate2 = coordinate;
Achtung |
Sollten Sie vorhaben, Ihre Programme auf andere Systeme zu portieren, so müssen sie sich hundertprozentig mit dem Alignment diverser Systeme auskennen, sofern Sie Unions einsetzen wollen. Ansonsten dürften Sie Probleme mit der Portierung bekommen. |
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.