Frage

Warum hat der sizeof Operator zurückgeben eine Größe größer für eine Struktur als die Summe der Größen der Mitglieder der Struktur?

War es hilfreich?

Lösung

Dies ist wegen des padding hinzugefügt Ausrichtungseinschränkungen zu erfüllen. Datenstruktur Ausrichtung Auswirkungen sowohl Leistung und Korrektheit von Programmen:

  • Mis-ausgerichteten Zugriff könnte einen harten Fehler sein (oft SIGBUS).
  • Mis-ausgerichteter Zugriff könnte ein Soft-Fehler sein.
    • Entweder in Hardware korrigiert, für einen bescheidenen Performance-Abbau.
    • oder durch Emulation in Software korrigiert, für einen schweren Performance-Abbau.
    • Zusätzlich Unteilbarkeit und andere Concurrency-Garantien gebrochen werden könnte, zu subtilen Fehlern führen.

Hier ist ein Beispiel mit typischen Einstellungen für einen x86-Prozessor (all verwendeten 32- und 64-Bit-Modus):

struct X
{
    short s; /* 2 bytes */
             /* 2 padding bytes */
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 3 padding bytes */
};

struct Y
{
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
    short s; /* 2 bytes */
};

struct Z
{
    int   i; /* 4 bytes */
    short s; /* 2 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
};

const int sizeX = sizeof(struct X); /* = 12 */
const int sizeY = sizeof(struct Y); /* = 8 */
const int sizeZ = sizeof(struct Z); /* = 8 */

kann man die Größe von Strukturen minimiert, indem Mitglieder durch Ausrichtung (Sortieren nach Größe genügt, dass in Grundtypen) (wie Struktur Z in dem obigen Beispiel).

Sortieren

WICHTIGER HINWEIS: Sowohl die C- und C ++ Standards besagen, dass Strukturausrichtung ist die Implementierung definiert. Daher kann jeder Compiler wählt Daten unterschiedlich auszurichten, in unterschiedlichen und inkompatiblen Daten Layouts ergibt. Aus diesem Grunde, wenn sie mit Bibliotheken zu tun, das von verschiedenen Compilern verwendet werden soll, ist es wichtig zu verstehen, wie die Compiler Daten auszurichten. Einige Compiler haben Befehlszeileneinstellungen und / oder spezielle #pragma Aussagen über die Struktur-Alignment-Einstellungen zu ändern.

Andere Tipps

Pack- und Byte-Ausrichtung, wie in dem C-FAQ beschrieben hier :

  

Es ist für die Ausrichtung. Viele Prozessoren können nicht auf 2- und 4-Byte   Mengen (z ints und langen Ints), wenn sie in vollgestopft sind   every-die-Wege.

     

Angenommen, Sie diese Struktur haben:

struct {
    char a[3];
    short int b;
    long int c;
    char d[3];
};
     

Nun könnte man denken, dass es möglich sein sollte, diese zu packen   Struktur in den Speicher wie folgt aus:

+-------+-------+-------+-------+
|           a           |   b   |
+-------+-------+-------+-------+
|   b   |           c           |
+-------+-------+-------+-------+
|   c   |           d           |
+-------+-------+-------+-------+
     

Aber es ist viel, viel einfacher, auf dem Prozessor, wenn der Compiler anordnet   es wie folgt:

+-------+-------+-------+
|           a           |
+-------+-------+-------+
|       b       |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           |
+-------+-------+-------+
     

In der gepackten Version darauf, wie es ist zumindest ein wenig hart für   Sie und ich zu sehen, wie die b und c Felder umschlingen? In einer Nussschale,   es ist schwer für den Prozessor auch. Daher sind die meisten Compiler-Pad   die Struktur (wie mit zusätzlichen, unsichtbaren Felder) wie folgt aus:

+-------+-------+-------+-------+
|           a           | pad1  |
+-------+-------+-------+-------+
|       b       |     pad2      |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           | pad3  |
+-------+-------+-------+-------+

Wenn Sie die Struktur wollen mit GCC zum Beispiel eine bestimmte Größe haben, verwenden __attribute__((packed)) .

Unter Windows können Sie die Ausrichtung auf ein Byte festgelegt, wenn die cl.exe Compier mit dem / Zp Option .

Normalerweise ist es einfacher für die CPU auf Daten zuzugreifen, die ein Vielfaches von 4 (oder 8), je Plattform ist und auch auf der Compiler.

Es ist also eine Frage der Ausrichtung grundsätzlich.

Sie müssen gute Gründe haben, um es zu ändern.

Dies kann darauf zurückzuführen sein, Byte-Ausrichtung und Polsterung, so dass die Struktur auf eine gerade Anzahl von Bytes herauskommt (oder Wörter) auf Ihrer Plattform. Zum Beispiel in C unter Linux, die folgenden drei Strukturen:

#include "stdio.h"


struct oneInt {
  int x;
};

struct twoInts {
  int x;
  int y;
};

struct someBits {
  int x:2;
  int y:6;
};


int main (int argc, char** argv) {
  printf("oneInt=%zu\n",sizeof(struct oneInt));
  printf("twoInts=%zu\n",sizeof(struct twoInts));
  printf("someBits=%zu\n",sizeof(struct someBits));
  return 0;
}

Haben Mitglieder, die Größen ist (in Bytes) sind 4 Bytes (32 Bits), 8 Byte (2x 32 Bit) und 1 Byte (2 + 6 Bits), die jeweils. Das obige Programm (unter Linux mit gcc) druckt die Größen als 4, 8 und 4 -., Wo die letzte Struktur aufgefüllt, so dass es ein einzelne Wort (4 x 8 Bit-Bytes auf meiner 32-Bit-Plattform) ist

oneInt=4
twoInts=8
someBits=4

Siehe auch:

für Microsoft Visual C:

http://msdn.microsoft .com / en-us / library / 2e70t5y1% 28v = vs.80% 29.aspx

und GCC Anspruch Kompatibilität mit Microsoft-Compiler.

http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking -Pragmas.html

Zusätzlich zu den bisherigen Antworten, bitte beachten Sie, dass unabhängig von der Verpackung, gibt es keine Mitglieder-order-Garantie in C ++ . Compiler kann (und sicherlich tun) in virtuellen Tabellenzeiger und Basisstrukturen der Mitglieder der Struktur. Auch die Existenz der virtuellen Tabelle wird nicht durch den Standard gewährleistet (virtuelle Mechanismus Implementierung ist nicht festgelegt), und daher kann man, dass eine solche Garantie schließen ist einfach unmöglich.

Ich bin ganz sicher, Mitglied Ordnung ist garantiert in C , aber ich würde nicht auf sie zählen, wenn eine plattformübergreifende oder Cross-Compiler Schreibprogramm .

Die Größe einer Struktur größer ist als die Summe seiner Teile aufgrund dessen, was Verpackung genannt wird. Ein besonderer Prozessor hat eine bevorzugte Datengröße, die es funktioniert mit. Die meisten modernen Prozessoren bevorzugte Größe, wenn 32-Bit (4 Byte). Zugriff auf den Speicher, wenn Daten auf diese Art von Grenze ist effizienter als die Dinge, die diese Größe Grenze überspannen.

Zum Beispiel. Betrachten Sie die einfache Struktur:

struct myStruct
{
   int a;
   char b;
   int c;
} data;

Wenn die Maschine ist eine 32-Bit-Maschine und die Daten werden ausgerichtet, auf einer 32-Bit-Grenze, so sehen wir ein unmittelbares Problem (keine Struktur Ausrichtung angenommen wird). In diesem Beispiel nehmen wir an, dass die Struktur-Daten bei der Adresse 1024 beginnt (0x400 - beachten, dass die niedrigsten 2 Bits Null sind, so werden die Daten in einer 32-Bit-Grenze ausgerichtet). Der Zugriff auf Daten.Verfahren wird funktionieren, weil es an einer Grenze beginnt - 0x400. Der Zugriff auf data.b wird auch gut funktionieren, weil es an der Adresse 0x404 ist - eine andere 32-Bit-Grenze. Aber eine nicht ausgerichtete Struktur an der Adresse setzt data.c würde 0x405. Die 4 Bytes data.c sind bei 0x405, 0x406, 0x407, 0x408. Auf einer 32-Bit-Maschine, würde das System data.c während eines Speicherzyklus gelesen, aber nur 3 des 4 Bytes erhalten würde (das vierte Byte ist auf der nächsten Grenze). So würde das System einen zweiten Speicherzugriff tun, um die vierte Byte zu bekommen,

Nun, wenn statt data.c an der Adresse das Setzens 0x405, der Compiler die Struktur von 3 Bytes aufgefüllt und legt data.c an der Adresse 0x408, dann würde das System braucht nur 1 Zyklus zum Lesen der Daten, Zugriffszeit zu schneiden daß Datenelement um 50%. Padding tauscht Speichereffizienz für Verarbeitungseffizienz. Da Computer riesige Mengen an Speicher (viele Gigabyte) haben, fühlen sich die Compiler, dass die Swap (Geschwindigkeit über Größe) ist ein vernünftiges.

Leider wird dieses Problem ein Killer, wenn Sie Strukturen zu senden über ein Netzwerk versuchen, oder auch die binären Daten in eine binäre Datei schreiben. Die Polsterung, die zwischen Elementen einer Struktur oder Klasse kann die Daten unterbrechen, um die Datei oder das Netzwerk gesendet. Um portablen Code (eine, die auf mehrere verschiedene Compiler gehen) zu schreiben, werden Sie wahrscheinlich jedes Element der Struktur zugreifen müssen separat die richtige „Verpackung“ zu gewährleisten.

Auf der anderen Seite, verschiedene Compiler haben unterschiedliche Fähigkeiten Datenstruktur Verpackung zu verwalten. Zum Beispiel, in Visual C / C ++ unterstützen die Compiler den #pragma pack-Befehl. Dies ermöglicht es Ihnen, Daten Verpackung und Ausrichtung anzupassen.

Zum Beispiel:

#pragma pack 1
struct MyStruct
{
    int a;
    char b;
    int c;
    short d;
} myData;

I = sizeof(myData);

Ich möchte nun die Länge von 11. Ohne die Pragma haben, konnte ich etwas 11 bis 14 (und für einige Systeme, so viel wie 32), abhängig von der Standard-Verpackung des Compilers.

Es kann so tun, wenn Sie implizit oder explizit die Ausrichtung der Struktur festgelegt. Eine Struktur, die 4 ausgerichtet ist immer ein Vielfaches von 4 Bytes sein, auch wenn die Größe ihrer Mitglieder etwas wäre, die nicht ein Vielfaches von 4 Bytes ist.

Auch eine Bibliothek mit 32-Bit-Ints unter x86 kompiliert werden und Sie können seine Komponenten auf einem 64-Bit-Prozess werden verglichen würden Sie ein anderes Ergebnis geben würde, wenn Sie diese von Hand taten.

C99 N1256 Standardentwurf

http: //www.open-std .org / JTC1 / SC22 / WG14 / www / docs / n1256.pdf

6.5.3.4 Der Operator sizeof :

  

3 Wenn auf einen Operanden angewendet, die Struktur oder Vereinigung Typ hat,   das Ergebnis ist die Gesamtzahl der Bytes in einem solchen Objekt,   einschließlich der internen und hintere Polsterung.

6.7.2.1 Struktur und Vereinigung Bezeich :

  

13 ... Es kann sein, unnamed   Polsterung in einem Strukturobjekt, aber nicht am Anfang.

und

  

15 Es am Ende einer Struktur oder Union unbenannte padding sein kann.

Die neue C99 flexible Array-Member-Funktion (struct S {int is[];};) kann auch Auswirkungen auf padding:

  

16 Als Sonderfall, das letzte Element einer Struktur mit mehr als einem benannten Mitglied kann   hat einen unvollständigen Array-Typen; Dies ist ein flexible Anordnungs-Element bezeichnet. In den meisten Fällen,   die flexible Element-Array wird ignoriert. Insbesondere ist die Größe der Struktur, als ob die   flexible Anordnungselemente weggelassen wurden, außer, dass es mehr nachlauf padding kann als   die Unterlassung würde bedeuten.

Anhang J Portabilität Probleme bekräftigt:

  

Die folgenden sind nicht näher bezeichnet: ...

     
      
  • Der Wert des Füllbytes, wenn Werte in Strukturen oder Vereinigungen (6.2.6.1)
  • Speicher   

C ++ 11 N3337 Standardentwurf

http://www.open -std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf

5.3.3 Sizeof :

  

2 Bei der Anwendung   das Ergebnis zu einer Klasse, die Anzahl der Bytes in einem Objekt dieser Klasse einschließlich padding erforderlich für   Platzieren Objekte dieses Typs in einem Array.

9.2 Klassenmitglieder :

  

Ein Zeiger auf eine struct Objekt Standard-Layout, in geeigneter Weise eine reinterpret_cast umgewandelt verwendet, weist auf seinen   Ausgangselement (oder, wenn das Element ein Bitfeld, dann an die Einheit, in der er sich befindet), und vice versa. [ Hinweis:   Es besteht daher unbenannte padding innerhalb eines Standard-Layout-Struktur-Objekt sein könnte, aber nicht am Anfang,   wie erforderlich, geeignete Ausrichtung zu erreichen. - Endnote]

Ich weiß nur genug C ++ die Notiz zu verstehen: -)

Zusätzlich zu den anderen Antworten, eine Struktur kann (aber in der Regel nicht) haben virtuelle Funktionen, wobei in diesem Fall die Größe der Struktur wird auch den Raum für die vtbl umfasst.

C-Sprache lässt Compiler eine gewisse Freiheit über die Lage der Strukturelemente im Speicher:

  • Speicherlöcher können zwischen zwei beliebigen Komponenten, und nach der letzten Komponente erscheinen. Es war aufgrund der Tatsache, dass bestimmte Arten von Objekten auf dem Zielcomputer können durch die Grenzen der Adressierung
  • begrenzt
  • „Speicherlöcher“ Größe im Ergebnis von sizeof Operator enthalten. Der sizeof enthält nicht nur Größe der flexiblen Anordnung, die in C / C ++
  • verfügbar ist
  • Einige Implementierungen der Sprache ermöglicht es Ihnen, das Speicherlayout von Strukturen durch die Pragma und Compiler-Optionen zu steuern

Die Sprache C bietet eine gewisse Sicherheit für den Programmierer der Elemente Layout in der Struktur:

  • Compiler benötigt, um eine Folge von Adressen Komponenten Erhöhung Speicher zuzuweisen
  • Adresse der ersten Komponente zusammenfällt mit der Startadresse der Struktur
  • unnamed Bitfelder in der Struktur an die gewünschte Adresse Ausrichtungen benachbarter Elemente
  • aufgenommen werden

Probleme der Elemente Ausrichtung bezogen werden:

  • Verschiedene Computer zeichnen die Kanten von Objekten auf unterschiedliche Weise
  • Verschiedene Beschränkungen für die Breite des Bitfeldes
  • Computer unterscheiden, wie der Bytes zu speichern, in einem Wort (Intel 80x86 und Motorola 68000)

Wie Ausrichtung funktioniert:

  • Das Volumen durch die Struktur eingenommen wird, wenn die Größe des ausgerichteten Einzelelement einer Anordnung von solchen Strukturen berechnet. Die Struktur sollte Ende, so dass das erste Element der nächstfolgende Struktur nicht die verletzen Anforderungen der Ausrichtung

P. S Ausführlichere Informationen finden Sie hier: "Samuel P.Harbison, Guy L.Steele C Referenz (5.6.2 - 5.6.7)"

Die Idee ist, dass für die Geschwindigkeit und Cache-Überlegungen, Operanden von Adressen zu ihrer natürlichen Größe ausgerichtet gelesen werden sollen. Um dies zu ermöglichen, die Compiler-Pads Strukturelemente, so dass die folgende Mitglied oder folgende Struktur wird ausgerichtet werden.

struct pixel {
    unsigned char red;   // 0
    unsigned char green; // 1
    unsigned int alpha;  // 4 (gotta skip to an aligned offset)
    unsigned char blue;  // 8 (then skip 9 10 11)
};

// next offset: 12

Die x86-Architektur ist immer in der Lage gewesen, falsch ausgerichtete Adressen zu holen. Es ist jedoch langsamer, und wenn der Versatz zwei verschiedene Cache-Zeilen überlappt, dann verwirft es zwei Cache-Zeilen, wenn ein ausgerichteter Zugriff nur eine evict würde.

Einige Architekturen tatsächlich zu stoppen haben auf Fehlstellungen liest und schreibt, und frühe Versionen der ARM-Architektur (die, die in alle heutigen mobilen CPUs entwickelt) ... na ja, sie tatsächlich sind gerade schlechte Daten für diejenigen. (Sie ignorierten die niederwertigen Bits).

Schließlich ist zu beachten, dass die Cache-Zeilen beliebig groß sein können, und der Compiler nicht versucht, an denen zu erraten oder einen Raum-vs-Speed-Kompromiss machen. Stattdessen sind die Ausrichtung Entscheidungen Teil des ABI und repräsentieren die minimale Ausrichtung, die eine Cache-Zeile schließlich gleichmäßig auffüllt.

TL; DR: Ausrichtung ist wichtig

.
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top