Strukturspeicherlayout in C.

85

Ich habe einen C # -Hintergrund. Ich bin ein Neuling in einer einfachen Sprache wie C.

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

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

Wie verhält sich C im Speicherlayout? Was sollte nachbestellt / ausgerichtet werden und nicht?

eonil
quelle

Antworten:

110

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

Füllbytes werden eingeführt, damit jedes Objekt richtig ausgerichtet ist. Nachbestellungen sind nicht erlaubt.

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

Ab C99 §6.7.2.1:

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

13 Innerhalb eines Strukturobjekts haben die Nicht-Bitfeldelemente und die Einheiten, in denen sich Bitfelder befinden, Adressen, die in der Reihenfolge zunehmen, in der sie deklariert werden. Ein Zeiger auf ein Strukturobjekt, das entsprechend konvertiert wurde, zeigt auf sein ursprüngliches Element (oder, wenn dieses Element ein Bitfeld ist, auf die Einheit, in der es sich befindet) und umgekehrt. Innerhalb eines Strukturobjekts befindet sich möglicherweise eine unbenannte Auffüllung, jedoch nicht am Anfang.

Kartoffelklatsche
quelle
1
Einige Compiler (z. B. GCC) implementieren den gleichen Effekt wie, #pragma packjedoch mit einer genaueren Kontrolle über die Semantik.
Chris Lutz
20
Ich bin überrascht, eine Ablehnung zu sehen. Kann jemand auf den Fehler hinweisen?
Potatoswatter
2
C11 hat auch _Alignas.
idmean
116

Es ist implementierungsspezifisch, aber in der Praxis #pragma packlautet die Regel (in Abwesenheit oder dergleichen):

  • Strukturelemente werden in der Reihenfolge gespeichert, in der sie deklariert sind. (Dies ist nach dem C99-Standard erforderlich, wie hier bereits erwähnt.)
  • Bei Bedarf wird vor jedem Strukturelement eine Auffüllung hinzugefügt, um eine korrekte Ausrichtung sicherzustellen.
  • Jeder primitive Typ T erfordert eine Ausrichtung von sizeof(T)Bytes.

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 am Versatz 2
  • ch2 ist bei Offset 4, unmittelbar nach s
  • 3 Füllbytes werden eingefügt, um ...
  • ll am 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 ermöglichen möglicherweise die 4-Byte-Ausrichtung von Strukturen.

So sizeof(ST)ist 24.

Sie kann durch Neuanordnen der Elemente auf 16 Byte reduziert werden, um ein Auffüllen zu vermeiden:

struct ST
{
   long long ll; // @ 0
   int i;        // @ 8
   short s;      // @ 12
   char ch1;     // @ 14
   char ch2;     // @ 15
} ST;
dan04
quelle
3
Wenn nötig, wird die Polsterung vor ... Mehr wie nachher hinzugefügt. Fügen Sie charIhrem Beispiel am besten ein letztes Mitglied hinzu.
Deduplikator
9
Ein primitiver Typ erfordert nicht unbedingt eine Ausrichtung von sizeof(T)Bytes. Beispielsweise doublebeträgt eine übliche 32-Bit-Architektur 8 Byte, erfordert jedoch häufig nur eine 4-Byte-Ausrichtung . Darüber hinaus füllt die Polsterung am Ende der Struktur nur die Ausrichtung des breitesten Strukturelements auf. Zum Beispiel könnte eine Struktur mit 3 Zeichenvariablen keine Auffüllung haben.
Matt
1
@ dan04, wäre es eine gute Praxis, Strukturen in absteigender Reihenfolge von sizeof (T) zu gestalten. Würde dies Nachteile haben?
RohitMat
11

Sie können beginnen, indem Sie den Wikipedia-Artikel zur Datenstrukturausrichtung lesen , um ein besseres Verständnis der Datenausrichtung zu erhalten.

Aus dem Wikipedia-Artikel :

Datenausrichtung bedeutet, dass die Daten auf einen Speicherversatz gesetzt werden, der einem Vielfachen der Wortgröße entspricht, was die Systemleistung aufgrund der Art und Weise erhöht, wie die CPU mit Speicher umgeht. Um die Daten auszurichten, müssen möglicherweise einige bedeutungslose Bytes zwischen dem Ende der letzten Datenstruktur und dem Beginn der nächsten Daten eingefügt werden. Dies ist das Auffüllen der Datenstruktur.

Ab 6.54.8 Strukturverpackende 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 der Breite Null), Gewerkschaften und nachfolgend definierten Klassen ändern. Der folgende n-Wert muss immer eine kleine Zweierpotenz sein und gibt die neue Ausrichtung in Bytes an.

  1. #pragma pack(n) stellt einfach die neue Ausrichtung ein.
  2. #pragma pack() Setzt die Ausrichtung auf die Ausrichtung, die beim Start der Kompilierung wirksam war (siehe auch Befehlszeilenoption -fpack-struct [=] siehe Code-Gen-Optionen).
  3. #pragma pack(push[,n]) Verschiebt die aktuelle Ausrichtungseinstellung auf einen internen Stapel und legt dann optional die neue Ausrichtung fest.
  4. #pragma pack(pop)stellt die Ausrichtungseinstellung wieder her, die oben im internen Stapel gespeichert ist (und entfernt diesen Stapeleintrag). Beachten Sie, dass #pragma pack([n])dies keinen Einfluss auf diesen internen Stapel hat. Somit ist es möglich, dass #pragma pack(push) mehrere #pragma pack(n) Instanzen gefolgt und von einer einzigen abgeschlossen wurden #pragma pack(pop).

Einige Ziele, z. B. i386 und powerpc, unterstützen ms_struct, #pragmadas eine Struktur wie dokumentiert festlegt __attribute__ ((ms_struct)).

  1. #pragma ms_struct on Aktiviert das Layout für deklarierte Strukturen.
  2. #pragma ms_struct off Deaktiviert das Layout für deklarierte Strukturen.
  3. #pragma ms_struct reset kehrt zum Standardlayout zurück.
jschmier
quelle
Danke für die Fürsorge. Ich habe die Frage nach Ihren Wünschen geändert.
Eonil