Strukturspeicherlayout in C

Lesezeit: 5 Minuten

Benutzeravatar von eonil
Äonil

Ich habe einen C#-Hintergrund. Ich bin ein absoluter Neuling in einer Low-Level-Sprache wie C.

In C#, structDer Speicher von wird standardmäßig vom Compiler angelegt. Der Compiler kann Datenfelder neu ordnen oder implizit zusätzliche Bits zwischen Feldern auffüllen. Also musste ich ein spezielles Attribut angeben, um dieses Verhalten für ein exaktes Layout zu überschreiben.

AFAIK, C ordnet oder richtet das Speicherlayout von a nicht neu struct standardmäßig. Ich habe jedoch gehört, dass es eine kleine Ausnahme gibt, die sehr schwer zu finden ist.

Wie ist das Speicherlayoutverhalten von C? Was soll neu geordnet/ausgerichtet werden und nicht?

Benutzeravatar von dan04
dan04

Es ist implementierungsspezifisch, aber in der Praxis die Regel (in Ermangelung von #pragma pack oder ähnliches) ist:

  • Strukturmitglieder werden in der Reihenfolge gespeichert, in der sie deklariert wurden. (Dies wird vom C99-Standard gefordert, wie hier bereits erwähnt.)
  • Falls erforderlich, wird vor jedem Strukturmitglied eine Auffüllung hinzugefügt, um eine korrekte Ausrichtung sicherzustellen.
  • Jeder primitive Typ T erfordert eine Ausrichtung von sizeof(T) Byte.

Also, gegeben die folgende Struktur:

struct ST
{
   char ch1;
   short s;
   char ch2;
   long long ll;
   int i;
};
  • ch1 ist bei Offset 0
  • zum Ausrichten wird ein Füllbyte eingefügt…
  • s bei Versatz 2
  • ch2 ist bei Offset 4, unmittelbar nach s
  • Zum Ausrichten werden 3 Füllbytes eingefügt…
  • ll bei Versatz 8
  • i ist bei Offset 16, direkt nach ll
  • Am Ende werden 4 Füllbytes hinzugefügt, sodass die Gesamtstruktur ein Vielfaches von 8 Bytes ist. Ich habe dies auf einem 64-Bit-System überprüft: 32-Bit-Systeme können Strukturen mit einer 4-Byte-Ausrichtung zulassen.

So sizeof(ST) ist 24.

Es kann auf 16 Bytes reduziert werden, indem die Mitglieder neu angeordnet werden, um Auffüllen zu vermeiden:

struct ST
{
   long long ll; // @ 0
   int i;        // @ 8
   short s;      // @ 12
   char ch1;     // @ 14
   char ch2;     // @ 15
} ST;

  • Gegebenenfalls wird vorher aufgefüllt … Eher nachher. Fügen Sie am besten ein Finale hinzu char Mitglied zu Ihrem Beispiel.

    – Deduplizierer

    19. Juli 2014 um 21:14 Uhr


  • Ein primitiver Typ erfordert nicht unbedingt eine Ausrichtung von sizeof(T) Byte. Zum Beispiel ein double auf gängigen 32-Bit-Architekturen 8 Bytes, erfordert aber oft nur ein 4-Byte-Alignment. Darüber hinaus polstert die Polsterung am Ende der Struktur nur auf die Ausrichtung des breitesten Strukturelements. Beispielsweise könnte eine Struktur mit 3-Zeichen-Variablen keine Auffüllung haben.

    – Matt

    21. Januar 2016 um 10:34 Uhr

  • @ dan04, wäre es eine gute Praxis, Strukturen in absteigender Reihenfolge von sizeof (T) zu gestalten. Würde es irgendwelche Nachteile geben, dies zu tun?

    – RohitMat

    27. Juni 2020 um 12:28 Uhr

Benutzeravatar von Potatoswatter
Kartoffelklatsche

In C darf der Compiler eine gewisse Ausrichtung für jeden primitiven Typ vorschreiben. Typischerweise ist die Ausrichtung die Größe des Typs. Aber es ist völlig implementierungsspezifisch.

Füllbytes werden eingeführt, damit jedes Objekt richtig ausgerichtet ist. Eine Nachbestellung ist nicht gestattet.

Möglicherweise implementiert jeder auch nur annähernd moderne Compiler #pragma pack Dies ermöglicht die Kontrolle über das Auffüllen und überlässt es dem Programmierer, die ABI einzuhalten. (Es ist jedoch streng nicht standardisiert.)

Aus C99 §6.7.2.1:

12 Jedes Nicht-Bitfeld-Member einer Struktur oder eines Vereinigungsobjekts wird in einer implementierungsdefinierten Weise ausgerichtet, die seinem Typ entspricht.

13 Innerhalb eines Strukturobjekts haben die Mitglieder, die keine Bitfelder sind, und die Einheiten, in denen sich Bitfelder befinden, Adressen, die in der Reihenfolge aufsteigen, in der sie deklariert werden. Ein geeignet umgewandelter Zeiger auf ein Strukturobjekt zeigt auf sein Anfangselement (oder, wenn dieses Element ein Bitfeld ist, dann auf die Einheit, in der es sich befindet) und umgekehrt. Innerhalb eines Strukturobjekts kann es unbenannte Auffüllungen geben, jedoch nicht an seinem Anfang.

  • Einige Compiler (z. B. GCC) implementieren denselben Effekt wie #pragma pack aber mit einer feinkörnigeren Kontrolle über die Semantik.

    – Chris Lutz

    1. Mai 2010 um 5:32 Uhr

  • C11 hat auch _Alignas.

    – idmein

    3. Januar 2017 um 12:34 Uhr

Benutzeravatar von jschmier
jschmier

Sie können mit dem Lesen beginnen Datenstruktur-Ausrichtung Wikipedia-Artikel um ein besseres Verständnis des Datenabgleichs zu erhalten.

Von dem Wikipedia-Artikel:

Datenausrichtung bedeutet, dass die Daten an einem Speicher-Offset platziert werden, der einem Vielfachen der Wortgröße entspricht, was die Systemleistung aufgrund der Art und Weise erhöht, wie die CPU den Speicher verarbeitet. Um die Daten auszurichten, kann es notwendig sein, einige bedeutungslose Bytes zwischen dem Ende der letzten Datenstruktur und dem Beginn der nächsten einzufügen, was ein Auffüllen der Datenstruktur ist.

Aus 6.54.8 Strukturpackende Pragmas der GCC-Dokumentation:

Aus Gründen der Kompatibilität mit Microsoft Windows-Compilern unterstützt GCC eine Reihe von #pragma-Direktiven, die die maximale Ausrichtung von Mitgliedern von Strukturen (außer Bitfeldern mit Nullbreite), Vereinigungen und nachfolgend definierten Klassen ändern. Der n-Wert unten muss immer eine kleine Zweierpotenz sein und gibt die neue Ausrichtung in Bytes an.

  1. #pragma pack(n) setzt einfach die neue Ausrichtung.
  2. #pragma pack() setzt die Ausrichtung auf die beim Start der Kompilierung gültige (siehe auch Befehlszeilenoption -fpack-struct[=] siehe Codegenerierungsoptionen).
  3. #pragma pack(push[,n]) schiebt die aktuelle Ausrichtungseinstellung auf einen internen Stapel und legt dann optional die neue Ausrichtung fest.
  4. #pragma pack(pop) stellt die Ausrichtungseinstellung auf diejenige wieder her, die oben im internen Stack gespeichert ist (und entfernt diesen Stack-Eintrag). Beachten Sie, dass
    #pragma pack([n]) beeinflusst diesen internen Stack nicht; so ist es möglich zu haben #pragma pack(push)
    gefolgt von mehreren #pragma pack(n)
    Instanzen und abgeschlossen durch eine einzelne
    #pragma pack(pop).

Einige Ziele, zB i386 und powerpc, unterstützen die ms_struct #pragma die eine Struktur als dokumentiert anlegt
__attribute__ ((ms_struct)).

  1. #pragma ms_struct on schaltet das Layout für deklarierte Strukturen ein.
  2. #pragma ms_struct off schaltet das Layout für deklarierte Strukturen aus.
  3. #pragma ms_struct reset kehrt zum Standardlayout zurück.

  • Danke für die Betreuung. Ich habe die Frage gemäß Ihrer Anleitung geändert.

    – Äonil

    1. Mai 2010 um 6:07 Uhr

1420850cookie-checkStrukturspeicherlayout in C

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy