2012-11-08 4 views
6

Nehmen Sie dieses BeispielIst es in C++ Standard solche Strukturen auszurichten

class A 
{ 
public: 
    int a; 
    char b; 
    int c; 
}; 

Jeder Compiler (für x86, 32 oder 64 Bit) Ich sehe weist 12 Bytes für die Klasse A, statt 9. So sind sie ausrichten b an die Integer-Grenze oder Bus-Grenze, die Sie sagen können. Meine Frage ist, ob dies in C++ Standard ist und ob es Compiler gibt, die das nicht tun.

Antwort

15

die Standard-C++ gibt an, dass:

  • Objekte eine Ausrichtungs Anforderung haben, von denen ihre Größe ein Vielfaches ist (also wenn int 4 Bytes breit ist, dann ist es eine Ausrichtung von 1, 2 oder 4 Bytes erfordert, abhängig von der Implementierung).
  • Mitgliedsobjekte (wenn sie durch Zugriffsbezeichner wie public nicht getrennt sind) sind alle in der Reihenfolge, wie sie
  • und sie respektieren Anforderungen ihrer Ausrichtung zugeordnet deklariert sind zugeordnet.

Also nein, der Standard sagt nicht genau, dass die Klasse eine Größe von 12 Bytes haben sollte.

Aber es tut sagen, dass b nach a zugewiesen werden soll, und dass c soll nach b zugeordnet werden.

Auf einer Plattform, wo int 4 Bytes breit ist, und erfordert eine 4-Byte-Ausrichtung, lässt dies 12 Bytes als die kleinste gültige Größe:

  • a nimmt den ersten 4 Bytes
  • b ein Byte dauert
  • c benötigt 4 Bytes, aber muss auf einer 4-Byte-Grenze zugewiesen werden.b endete ein Byte über so eine Grenze, so wird die nächste gültige Position c bei wird durch Einfügen von 3 Bytes Polsterung gefunden.

So ist die Gesamtgröße der Klasse endet als die Größe der Elemente bis (4 + 1 + 4 = 9) und drei Bytes von Polstern, für insgesamt 12

Es ist eine andere Regel Das hat hier keine Auswirkungen, aber was wäre von Bedeutung, wenn Sie stattdessen die Mitglieder in der Reihenfolge definiert hätten.

Die enthaltene Klasse (A) erbt die Ausrichtungsanforderung vom am strengsten ausgerichteten Element des Elements. Das heißt, da es einen int enthält, hat es die gleiche Ausrichtungsanforderung wie ein int tut. Und da die Gesamtgröße des Objekts ein Vielfaches seiner Ausrichtungsanforderung sein muss, würde eine Klasse, die die Elemente in der Reihenfolge noch enthält, 12 Byte Speicherplatz benötigen. Es würde nur die 3 Bytes der Auffüllung an das Ende der Klasse statt zwischen b und c verschieben.

In einigen anderen Fällen jedoch kann das Umordnen von Elementen in absteigender Reihenfolge der Größe manchmal die Größe einer Klasse reduzieren.

Angenommen, wir stattdessen eine Klasse wie diese gehabt hatten:

class B { 
    char a; 
    double b; 
    int c; 
}; 

Dies würde 24 Byte Speicher erforderlich ist (1 Byte für a, 8 Byte für b und 4 Bytes für c, aber dann Stellen Sie sicher, b endet auf einer 8-Byte-Grenze, würden wir Bytes der Auffüllung zwischen a und b benötigen, und um sicherzustellen, dass die gesamte Klasse endet mit einer Größe, die ein Vielfaches von 8 ist, benötigen wir weitere 4 Bytes nach c

Aber Neuordnen die Mitglieder nach Größe, wie folgt aus:

class B { 
    double b; 
    int c; 
    char a; 
}; 

Ergebnisse in einer Klasse erfordern nur 16 Byte:

das gleiche 1 + 4 + 8 Bytes für das Mitglied selbst Objekte, aber jetzt c ist bereits auf einer 4-Byte-Grenze ausgerichtet (weil es nach b kommt, die an einer 8-Byte-Grenze endet), und a braucht nie eine Ausrichtung, so dass die einzige Ausrichtung, die wir brauchen, ist sicherzustellen, dass B eine Größe hat, die ein Vielfaches ist von 8. Die Mitglieder nehmen 13 Bytes, so dass wir 3 Bytes der Auffüllung hinzufügen können, und die Klasse endet bei 16 Bytes, 33% kleiner tha n der ersten Version.

+1

Sie können auch darauf hinweisen, dass auf den meisten Architekturen der Zugriff auf ein falsch ausgerichtetes Objekt zu einer Art Busfehler führt, der das Programm zum Absturz bringt. Auf einem Intel wird das Programm nicht abstürzen, aber es wird immer noch viel langsamer laufen, wenn die Daten falsch ausgerichtet sind. –

0

Standard ermöglicht es den Compilern, bei Bedarf weitere Bytes zu paddeln. Es hängt im Wesentlichen von der Architektur des Hosts und nicht vom Compiler ab.

1

Es ist möglich, das Strukturpacken zur Kompilierungszeit mithilfe von Pragma-Direktiven zu steuern. See #Pragma Pack

Zum Beispiel kann die folgende ausrichten nicht die Mitglieder, und legt sie direkt nebeneinander.

#pragma pack(push, 1) 
struct A4 
{ 
    char a; 
    double b; 
    char c; 
}; 
#pragma pack(pop) 

Beispiel aus here.

GCC unterstützt auch Pragma-Pack. Die Richtlinie ist nicht Teil eines Standards, aber viele Compiler unterstützen sie.


Allerdings sollte es keinen Grund geben, das Obige zu tun. Der Compiler richtet sie aus, um den Zugriff auf Mitglieder zu beschleunigen, und es sollte keinen Grund geben, das zu ändern.

+0

Ist '#pragma pack' tragbar? Funktioniert es andere Compiler? –

+0

Aber ist es in C++ - Standard definiert, dass der Standardfall an der Busgrenze auszurichten wäre. – pythonic

+0

@AlessandroPezzato: http://Stackoverflow.com/a/13267797/76722 –

0

Ja, Ausrichtung wird überall in der Norm erwähnt, hauptsächlich Abschnitt 3.11 (Ausrichtung). Es ist plattformabhängig, daher ist jedes Programm, das von der tatsächlichen Größe eines Objekts abhängt, inhärent nicht portierbar.

0

In Ihrem Fall ist 'b' immer korrekt ausgerichtet. Es ist aufgefüllt zum Ausrichten c in 32-Bit-Grenzen. Obwohl für bestimmte Implementierungen ein gewisser Spielraum besteht, folgen die meisten Compiler der Regel der Ausrichtung von 2- und 4-Byte-Variablen auf 2- und 4-Byte-Grenzen.

In 32-Bit-Systemen sind auch Doppel- und Long-Ints (8 Byte) auf 4-Byte-Grenzen ausgerichtet, in 64-Bit-Systemen jedoch auf 8 Byte ausgerichtet.

+0

Wie das System verschiedene Typen ausrichtet, ist sehr abhängig von der Implementierung. Ich habe 32-Bit-Systeme verwendet, bei denen die Doppelten an 8-Byte-Grenzen ausgerichtet waren, und ich habe alle Arten von Alignments für "long double" gesehen. –

+0

Nun ja, vielleicht ist das lange Doppel die Grenze der Kompatibilität. –