Suche…


Einführung

Standardmäßig legen C-Compiler Strukturen fest, so dass auf jedes Mitglied schnell zugegriffen werden kann, ohne dass Strafen für nicht ausgerichteten Zugriff, ein Problem mit RISC-Computern wie DEC Alpha und einigen ARM-CPUs entstehen.

Abhängig von der CPU-Architektur und dem Compiler benötigt eine Struktur möglicherweise mehr Speicherplatz als die Summe der Größen ihrer Komponenten. Der Compiler kann Padding zwischen Mitgliedern oder am Ende der Struktur hinzufügen, jedoch nicht am Anfang.

Beim Packen wird die Standardauffüllung überschrieben.

Bemerkungen

Eric Raymond hat einen Artikel über The Lost Art of C Structure Packing, der nützlich ist, um zu lesen.

Verpackungsstrukturen

Standardmäßig werden Strukturen in C aufgefüllt. Wenn Sie dieses Verhalten vermeiden möchten, müssen Sie es explizit anfordern. Unter GCC ist es __attribute__((__packed__)) . Betrachten Sie dieses Beispiel auf einem 64-Bit-Computer:

struct foo {
    char *p;  /* 8 bytes */
    char c;   /* 1 byte  */
    long x;   /* 8 bytes */
};

Die Struktur wird automatisch mit einer 8-byte Ausrichtung aufgefüllt und sieht folgendermaßen aus:

struct foo {
    char *p;     /* 8 bytes */
    char c;      /* 1 byte  */

    char pad[7]; /* 7 bytes added by compiler */

    long x;      /* 8 bytes */
};

sizeof(struct foo) gibt uns also 24 statt 17 . Dies geschah aufgrund eines 64-Bit-Compilers, der in jedem Schritt aus 8 Bytes aus dem Lese- / Schreibspeicher aus dem / in den Speicher ablegte, und offensichtlich, wenn versucht wurde, char c; zu schreiben char c; Ein Byte im Speicher, ein komplettes 8 Byte (dh Wort) wird abgerufen und verbraucht nur das erste Byte, und die sieben aufeinanderfolgenden Bytes bleiben leer und können für keine Lese- und Schreiboperation zum Strukturauffüllen verwendet werden.

Strukturverpackung

Wenn Sie das Attribut packed hinzufügen, fügt der Compiler keine Auffüllung hinzu:

struct __attribute__((__packed__)) foo {
    char *p;  /* 8 bytes */
    char c;   /* 1 byte  */
    long x;   /* 8 bytes */
};

sizeof(struct foo) gibt jetzt 17 .

Im Allgemeinen werden gepackte Strukturen verwendet:

  • Um platz zu sparen.
  • Formatieren einer Datenstruktur für die Übertragung über ein Netzwerk, ohne von der jeweiligen Architekturausrichtung jedes Knotens des Netzwerks abhängig zu sein.

Es muss berücksichtigt werden, dass einige Prozessoren wie der ARM Cortex-M0 keinen unausgerichteten Speicherzugriff zulassen. In solchen Fällen kann das Packen von Strukturen zu undefiniertem Verhalten führen und die CPU zum Absturz bringen.

Strukturpolsterung

Angenommen, diese struct wird mit einem 32-Bit-Compiler definiert und kompiliert:

struct test_32 {
    int a;      // 4 byte
    short b;    // 2 byte
    int c;      // 4 byte    
} str_32;

Wir können erwarten, dass diese struct nur 10 Bytes Speicher belegt, aber wenn Sie sizeof(str_32) , werden 12 Bytes verwendet.

Dies geschah, weil der Compiler Variablen für den schnellen Zugriff ausrichtete. Ein übliches Muster ist, dass, wenn der Basistyp N Bytes belegt (wobei N eine Potenz von 2 ist, wie 1, 2, 4, 8, 16 - und selten größer), die Variable an einer N-Byte-Grenze ausgerichtet sein sollte ( ein Vielfaches von N Bytes).

Für die Struktur, die mit sizeof(int) == 4 und sizeof(short) == 2 , ist ein allgemeines Layout:

  • int a; bei Offset 0 gespeichert; Größe 4.
  • short b; bei Offset 4 gespeichert; Größe 2.
  • unbenannte Auffüllung am Offset 6; Größe 2.
  • int c; bei Offset 8 gespeichert; Größe 4.

struct test_32 belegt somit 12 Byte Speicher. In diesem Beispiel gibt es keine nachstehende Auffüllung.

Der Compiler stellt sicher, dass alle Variablen von struct test_32 beginnend mit einer 4-Byte-Grenze gespeichert werden, sodass die Member innerhalb der Struktur für einen schnellen Zugriff ordnungsgemäß ausgerichtet sind. Speicherzuordnungsfunktionen wie malloc() , calloc() und realloc() sind erforderlich, um sicherzustellen, dass der zurückgegebene Zeiger für alle Datentypen ausreichend gut ausgerichtet ist, sodass dynamisch zugewiesene Strukturen auch ordnungsgemäß ausgerichtet werden.

Es kann zu ungewöhnlichen Situationen kommen, wie zum Beispiel auf einem 64-Bit-Prozessor Intel x86_64 (z. B. Intel Core i7 - ein Mac mit MacOS Sierra oder Mac OS X), bei dem die Compiler beim Kompilieren im 32-Bit-Modus double ausgerichtet auf einem 4-Byte-Grenze; Auf derselben Hardware setzen die Compiler beim Compilieren im 64-Bit-Modus jedoch double ausgerichtet auf eine 8-Byte-Grenze.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow