15.12 Attribute von Strukturen verändern (nicht ANSI C)
Der Speicherplatz für eine Struktur wird – wie schon bei den Arrays – lückenlos im Hauptspeicher abgelegt. Damit das System schneller auf diese Daten im Hauptspeicher zurückgreifen kann, werden diese in durch zwei oder in durch vier teilbare Adressen angeordnet. Dies hängt vom Betriebssystem ab. Dabei wird einiges an Speicherplatz verschwendet. Zur Demonstration dient das folgende Programm:
/* alignment1.c */ #include <stdio.h> #include <stdlib.h> struct speicher { char x; int z; }; int main(void) { struct speicher test; printf("%u Bytes\n",sizeof(test)); return EXIT_SUCCESS; }
Auf 32-Bit-Systemen dürfte diese Struktur acht Byte benötigen. Und dies, obwohl es eigentlich fünf Byte sein sollten (char + int = 5 Byte).
Abbildung 15.10 Speicherabbild mit einer Struktur mit unbenannten Lücken
Abbildung 15.10 stellt ein Vier-Byte-Alignment dar, wie es bei den meisten Systemen der Fall sein wird. Dabei entsteht eine Lücke von drei Byte (grau gefärbt), die ungenutzt bleibt. Es wird hier auch vom Padding (Auffüllen, Polsterung) des Speichers gesprochen.
Hinweis |
Dies ist übrigens auch der Grund, warum Sie Strukturen nicht direkt miteinander vergleichen können. Auch ein Low-Level-Vergleich, Byte für Byte, kann da nicht viel helfen, da dieser durch zufällig gesetzte Bits in den Löchern verfälscht sein könnte. In solch einem Fall müssen Sie sich mit einer eigenen Funktion behelfen, die die einzelnen Strukturelemente miteinander vergleicht. |
Viele Compiler besitzen daher einen speziellen Schalter, mit dem diese Lücke entfernt werden kann. Wobei ich gleich anmerken muss, dass dies nicht ANSI-C-konform, sondern compiler-abhängig ist.
Mit dem Schalter
__attribut__
können dem Compiler mehrere Informationen zu einer Funktion, zu Variablen oder Datentypen übergeben werden. Um damit eine lückenlose Speicherbelegung zu erreichen, könnten Sie das Attribut packed verwenden.
Sollte dieser Schalter bei Ihrem Compiler nicht funktionieren, können Sie auch das Pragma pack verwenden:
#pragma pack(n)
Für n kann hier der Wert 1, 2, 4, 8 oder 16 angegeben werden. Je nachdem, welche Angabe Sie dabei machen, wird jedes Strukturelement nach dem ersten kleineren Elementtyp oder auf n Byte abgespeichert.
Beim Testen auf verschiedenen Systemen und unterschiedlichen Compilern gab es keine Probleme mit dem #pragma pack. Die Option __attribut__ hingegen wurde nicht von jedem Compiler erkannt. Wie Sie dabei vorgehen, müssen Sie letztendlich selbst herausfinden.
Hier folgt das Beispiel dazu:
/* alignment2.c */ #include <stdio.h> #include <stdlib.h> /* Lässt sich dieses Listing nicht übersetzen, * entfernen Sie __attribute__((packed)) und * verwenden stattdessen #pragma pack(1). */ /* #pragma pack(1) */ struct speicher { char x; int z; } __attribute__ ((packed)); int main(void) { struct speicher test; printf("%u Bytes\n",sizeof(test)); return EXIT_SUCCESS; }
Damit benötigt die Struktur tatsächlich 5 Byte. Dies funktioniert bei manchen Compilern auch bei den enum-Aufzählungen:
/* alignment3.c */ #include <stdio.h> #include <stdlib.h> /* #pragma pack(1); */ enum{TRUE,FALSE}__attribute__ ((packed)); int main(void) { printf("%u Bytes\n",sizeof(TRUE)); return EXIT_SUCCESS; }
Das Beispiel funktionierte beim Testen allerdings nur mit dem gcc-Compiler. In diesem Fall wird durch packed ein 1-Byte-Alignment angelegt. Das ist gegenüber den vier Bytes ohne packed beachtlich. Diese Werte können natürlich von System zu System unterschiedlich sein.
Hinweis |
Natürlich müssen Sie sich auch darüber im Klaren sein, dass hier zwischen Performance und Speicherplatz gewählt wird. Schließlich greifen Sie ja in die geschwindigkeitsoptimierte Speicherung des Betriebssystems ein. |
Bei den Vorteilen, die mit packed oder dem Pragma pack erzielt werden, sollten Sie auch die Nachteile beachten. Wenn diese Daten (struct speicher) auf einem System mit fünf Bytes pro Struktur in einer Datei gespeichert werden, kann es passieren, dass diese Daten auf einem anderen System falsch angezeigt werden, weil das System vielleicht dort die Option packed nicht kennt und einfach ignoriert. Außerdem könnten Low-Level-Funktionen fehlschlagen, da sich die Daten wegen des Alignments nicht dort befinden, wo die Funktionen diese vermutet.
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.