Das ist eher eine theoretische Frage. Ich bin mit der Funktionsweise von Padding und Trailing Padding vertraut.
struct myStruct{
uint32_t x;
char* p;
char c;
};
// myStruct layout will compile to
// x: 4 Bytes
// padding: 4 Bytes
// *p: 8 Bytes
// c: 1 Byte
// padding: 7 Bytes
// Total: 24 Bytes
Danach muss noch gepolstert werden x
damit *p
ausgerichtet ist, und danach muss eine nachlaufende Polsterung vorhanden sein c
sodass die gesamte Strukturgröße durch 8 teilbar ist (um die richtige Schrittlänge zu erhalten). Aber betrachten Sie dieses Beispiel:
struct A{
uint64_t x;
uint8_t y;
};
struct B{
struct A myStruct;
uint32_t c;
};
// Based on all information I read on internet, and based on my tinkering
// with both GCC and Clang, the layout of struct B will look like:
// myStruct.x: 8 Bytes
// myStruct.y: 1 Byte
// myStruct.padding: 7 Bytes
// c: 4 Bytes
// padding: 4 Bytes
// total size: 24 Bytes
// total padding: 11 Bytes
// padding overhead: 45%
// my question is, why struct A does not get "inlined" into struct B,
// and therefore why the final layout of struct B does not look like this:
// myStruct.x: 8 Bytes
// myStruct.y: 1 Byte
// padding 3 Bytes
// c: 4 Bytes
// total size: 16 Bytes
// total padding: 3 Bytes
// padding overhead: 19%
Beide Layouts erfüllen die Ausrichtungen aller Variablen. Beide Layouts haben die gleiche Reihenfolge der Variablen. In beiden Layouts struct B
hat die richtige Schrittlänge (teilbar durch 8 Bytes). Der einzige Unterschied (neben 33 % kleinerer Größe) ist das struct A
hat in Layout 2 keine korrekte Schrittlänge, aber das sollte keine Rolle spielen, da es eindeutig kein Array von gibt struct A
s.
Ich habe dieses Layout in GCC mit -O3 und -g überprüft, struct B
hat 24 Bytes.
Meine Frage ist – gibt es einen Grund, warum diese Optimierung nicht angewendet wird? Gibt es eine Layoutanforderung in C/C++, die dies verbietet? Oder fehlt mir ein Compilation-Flag? Oder ist das ein ABI-Ding?
EDIT: Beantwortet.
- Siehe Antwort von @dbush, warum der Compiler dieses Layout nicht selbst ausgeben kann.
- Das folgende Codebeispiel verwendet GCC-Pragmas
packed
undaligned
(wie von @jaskij vorgeschlagen), um das optimiertere Layout manuell zu erzwingen. StrukturB_packed
hat nur 16 Bytes statt 24 Bytes (beachten Sie, dass dieser Code Probleme verursachen kann/langsam läuft, wenn ein Array von Strukturen vorhanden istB_packed
seien Sie sich bewusst und kopieren Sie diesen Code nicht blind):
struct __attribute__ ((__packed__)) A_packed{
uint64_t x;
uint8_t y;
};
struct __attribute__ ((__packed__)) B_packed{
struct A_packed myStruct;
uint32_t c __attribute__ ((aligned(4)));
};
// Layout of B_packed will be
// myStruct.x: 8 Bytes
// myStruct.y: 1 Byte
// padding for c: 3 Bytes
// c: 4 Bytes
// total size: 16 Bytes
// total padding: 3 Bytes
// padding overhead: 19%
Punkt genommen. Ich habe die Frage mit bearbeitet
Bytes
statt SuffixB
. Das Suffix ganz wegzulassen wäre meiner Meinung nach viel verwirrender und nicht richtig.– Dom324
22. Dezember 2022 um 1:05 Uhr
Nun, das ist Ansichtssache, denke ich.
sizeof
gibt immer einen Wert zurück in Bytewenn wir also über Größen von Datentypen, Polsterung usw. sprechen, sind wir es stets Apropos Byte.– Reisfeld
22. Dezember 2022 um 1:06 Uhr
Das ist wohl ein Überbleibsel vom Studium der Elektrotechnik an der Uni, eine Zahl ohne Einheit daneben zu sehen, kommt mir wie eine Sünde vor
– Dom324
22. Dezember 2022 um 1:32 Uhr
Was passiert, wenn Sie ein Array Ihres Typs erstellen? Stellen Sie sich einen Typ vor, der ein 4-Byte hat
int
gefolgt von einem 1 Bytechar
was passiert, wenn Sie diesen Typ in ein Array einfügen?– CoffeeTableEspresso
22. Dezember 2022 um 2:32 Uhr
Beachten Sie, dass alle Ihre Größenannahmen für die 64-Bit-Architektur gelten (vielleicht speziell für x76_64). 32 Bit ist noch nicht tot und wird es noch einige Jahre geben. Verdammt, stellenweise wird 16-Bit-Zeug verwendet. Das heißt, vielleicht ist es hier eine Regel, einfach von einem modernen Laptop/Desktop/Server-Prozessor auszugehen? Was du eigentlich anschauen will ist das (etwas verfluchte)
packed
Attribut. Ich sage verflucht, weil es zu unausgerichtetem Zugriff führt. Obwohl Sie koppeln könnenpacked
undaligned
um jede gewünschte Ausrichtung zu spezifizieren.– jaskij
22. Dezember 2022 um 21:08 Uhr