2015-07-07 19 views
28

Betrachten Sie dieses einfache ProgrammWarum funktioniert dieser Strukturfülltrick?

#include <iostream> 

struct A 
{ 
    int x1234; 
    short x56; 
    char x7; 
}; 

struct B : A 
{ 
    char x8; 
}; 

int main() 
{ 
    std::cout << sizeof(A) << ' ' << sizeof(B) << '\n'; 
    return 0; 
} 

Diese 8 12 druckt. Auch wenn B in 8 Bytes gepackt werden kann, ohne die Ausrichtungsanforderungen zu brechen, nimmt es statt dessen gierige 12 Bytes auf.

Es wäre schön, sizeof(B) == 8 zu haben, aber die Antwort auf Is the size of a struct required to be an exact multiple of the alignment of that struct? schlägt vor, dass es keinen Weg gibt.

war ich daher überrascht, als der

struct MakePackable 
{ 
}; 

struct A : MakePackable 
{ 
    int x1234; 
    short x56; 
    char x7; 
}; 

struct B : A 
{ 
    char x8; 
}; 

gedruckt 8 8 folgende.

Was geht hier vor? Ich vermute, dass Standard-Layout-Typen etwas damit zu tun haben. Wenn ja, was ist der Grund dafür, dass das obige Verhalten verursacht wird, wenn der einzige Zweck dieses Features darin besteht, die Binärkompatibilität mit C zu gewährleisten?


EDIT: Wie andere haben darauf hingewiesen, dies ist ABI oder Compiler spezifisch, so dass ich sollte hinzufügen, dass dieses Verhalten auf x86_64-unknown-linux-gnu mit den folgenden Compiler beobachtet wurde:

  • 3.6 Klirren
  • gcc 5,1

ich habe auch bemerkt etwas seltsam aus Klirren der struct dumper. Wenn wir für die Datengröße ohne Schwanz padding („dsize“) fragen,

  A B 
first  8 9 
second 7 8 

dann im ersten Beispiel erhalten wir dsize(A) == 8. Warum ist das nicht 7?

+0

Vielleicht gibt es keine anderen Gründe als Compiler-Implementierungsdetails ... – deviantfan

+2

Sie werden nach einem bestimmten ABI fragen müssen, da es fast keine Chance gibt, dass dieses Verhalten im Rahmen von C++ selbst liegt. FWIW, ich kann in Itanium (auf einen kurzen Blick) nichts finden, um dies zu erklären, obwohl ich '8 8' mit GCC 5.1 bekomme, also ...:/ –

+0

Passt das Tag [tag: sprachanwalt] für diese Frage? –

Antwort

2

Ich bin kein echte Sprache Anwalt von C++, aber was ich gefunden habe, so weit ist:

Referenzierung der Antworten in this question, eine Struktur bleibt nur ein Standardlayout POD, während es nur 1 Klasse mit nicht statische Mitglieder untereinander und ihre Elternklassen. Also unter dieser Idee A hat ein garantiertes Layout in beiden Fällen, aber B nicht in entweder Fall.

Unterstützend ist die Tatsache, dass std::is_pod gilt für A und falsch für B in beiden.

Also, wenn ich das selbst richtig zu verstehen bin, ist der Compiler einige Zimmer erlaubt zu tun, was es mit dem Layout von B will in beide Fälle. Und anscheinend fühlt es sich im zweiten Fall so an, als würde man das verwenden, was sonst das Füll-Byte von A gewesen wäre.

+0

Eine Standardlayoutklasse: _ "hat entweder keine nicht statischen Datenelemente in der abgeleiteten Klasse und höchstens eine Basisklasse mit nicht statischen Datenmembern oder keine Basisklassen mit nicht statischen Datenmembern" _ '[C++ 14: 9/7] ' –

+0

@LightnessRacesinOrbit Der Wortlaut wurde mit [diesem Vorschlag] geändert (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1813), und jetzt macht keinen Sinn für mich. – Barry

2

Dies ist ein Datenpunkt, obwohl keine vollständige Antwort.

Sagen wir (als komplette Übersetzungseinheit, nicht ein Ausschnitt) haben:

struct X {}; 

struct A 
{ 
    int x1234; 
    short x56; 
    char x7; 
} 

void func(A &dst, A const &src) 
{ 
    dst = src; 
} 

mit g ++, diese Funktion zu erstellenden:

movq (%rdx), %rax 
movq %rax, (%rcx) 

Allerdings, wenn struct A : X stattdessen verwendet wird, dann Diese Funktion ist:

movl (%rdx), %eax 
movl %eax, (%rcx) 
movzwl 4(%rdx), %eax 
movw %ax, 4(%rcx) 
movzbl 6(%rdx), %eax 
movb %al, 6(%rcx) 

Diese beiden Fälle entsprechen tatsächlich den Größen b ein 8 12 und 8 8 jeweils in OP-Beispiel.

Der Grund dafür ist ziemlich klar: A könnte als Basis für einige Klasse verwendet werden B, und dann func(b, a); der Anruf muß vorsichtig sein, nicht andere Mitglieder b zu stören, die im Polsterbereich (b.x8 in OPs befinden könnten Beispiel);

Ich kann keine bestimmte Eigenschaft von A : X im C++ - Standard sehen, die g ++ entscheiden würde, dass das Padding in wiederverwendbar ist, aber nicht in struct A. Beide A und A : X sind trivial kopierbar, Standardlayout und POD.

Ich denke, es muss nur eine Optimierung Entscheidung basierend auf der typischen Nutzung sein. Die Version ohne Wiederverwendung wird schneller zu kopieren sein. Vielleicht könnte ein g ++ ABI Designer kommentieren?

Interessanterweise zeigt dieses Beispiel, dass eine einfache Kopie nicht bedeutet, dass memcpy(&b, &a, sizeof b)b = a entspricht!

+1

Ich würde vermuten, dass dies mit C-Kompatibilität zu tun hat. Während ein C++ - Programm wissen muss, dass ein beliebiger nicht-finaler Typ abgeleitet werden kann und daher Datenelemente innerhalb der Auffüllung platziert werden, benötigt kein C-Programm diese Annahme. Wenn Sie 'A' von einem Typ ableiten, können Sie nicht exakt die gleiche' struct' in C. – dyp

+0

@dyp verwenden, die Sinn macht –

Verwandte Themen