Padding bei Klassen die ausschliesslich aus build-in Typen bestehen
-
Paddel2000 schrieb:
Kann ein Compiler da Bytes zwischenfügen? Oder könnte ich mir einen Byte-Zeiger auf ein objekt von A holen und da gefahrlos drüber iterieren?
Wenn die Klasse eine "Standard-Layout" Klasse ist, dann ist es garantiert. Sonst soweit ich weiss nicht.
Was den Begriff "Standard-Layout" angeht kannst du im Standard nachlesen, oder ein wenig googeln. Hier findest du z.B. eine ganz gute Erklärung/Übersicht:http://stackoverflow.com/questions/4178175/what-are-aggregates-and-pods-and-how-why-are-they-special
ps: Wenn die Klasse wirklich so trivial aufgebaut ist wie in deinem Beispiel, dann ist es eine Standard-Layout Klasse. Der Compiler muss also sicherstellen dass es kein Padding gibt, und dass ein Zeiger auf den Anfang der Klasse problemlos mittels
reinterpret_cast<int>()in einen Zeiger auf das erste Element verwandelt werden kann (und natürlich wieder zurück).
-
Afaik ist bei Standard Layout Types lediglich garantiert, dass es am Anfang kein Padding gibt, d.h. dass ein Zeiger auf das Objekt in einen Zeiger auf das erste Element gecastet werden kann. Weiters ist garantiert, dass alle Member entsprechend der Reihenfolge ihrer Deklaration in der Klassendefinition aufsteigende Addressen haben. Es ist allerdings nicht garantiert, dass es zwischen Membern kein Padding gibt.
-
@dot
Also ich müsste es selbst nachlesen, aber ich bin mir ziemlich sicher dass C99 garantiert dass mehrereintin einer struct genau so hintereinander liegen wie sie es in einem Array tun würden.
Bei unterschiedlichen Typen ist es natürlich was anderes, da kann beliebig Padding fällig werden zwischen den Membern.Und da C++ für Standard Layout Types soweit ich weiss garantiert dass sie gleich aussehen wie in C...
-
hier die weitere Lektüre:
1.8 The C++ object model
5 Unless it is a bit-field (9.6), a most derived object shall have a non-zero size and shall occupy one or more bytes of storage. Base class subobjects may have zero size. An object of trivially copyable or standard-layout type (3.9) shall occupy contiguous bytes of storage.3.9 Types
9 ... Scalar types, standard-layout class types (Clause 9), arrays of such types and cv-qualified versions of these types (3.9.3) are collectively called standard-layout types.9 Classes
7 A standard-layout class is a class that:
* has no non-static data members of type non-standard-layout class (or array of such types) or reference,
* has no virtual functions (10.3) and no virtual base classes (10.1),
* has the same access control (Clause 11) for all non-static data members,
* has no non-standard-layout base classes,
* either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and
* has no base classes of the same type as the first non-static data member.
10 A POD struct109 is a non-union class that is both a trivial class and a standard-layout class, and has no non-static data members of type non-POD struct, non-POD union (or array of such types).
109) The acronym POD stands for "plain old data".9.2 Class members
14 Nonstatic data members of a (non-union) class with the same access control (Clause 11) are allocated so that later members have higher addresses within a class object. The order of allocation of non-static data members with different access control is unspecified (11). Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions (10.3) and virtual base classes (10.1).
17 Two standard-layout struct (Clause 9) types are layout-compatible if they have the same number of non-static data members and corresponding non-static data members (in declaration order) have layout-compatible types (3.9).
20 A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa. [ Note: There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. ]18.2 Types
4 The macro offsetof(type, member-designator) accepts a restricted set of type arguments in this International Standard. If type is not a standard-layout class (Clause 9), the results are undefined.
-
Hab grad auch noch im C Standard nachgeschaut, dort steht im Prinzip genau das gleiche drin.

-
Für mich ist das jetzt noch nicht ganz eindeutig.
Es gilt also:
An object of trivially copyable or standard-layout type (3.9) shall occupy contiguous bytes of storage.
Aber wäre es auch "Standard-Layout" wenn man char/bool etc. mischt? Ich finde diese Einschränkung (kein mischen) in 9.7 nicht?
-
Was genau ist daran nicht eindeutig:
Nonstatic data members of a (non-union) class with the same access control (Clause 11) are allocated so that later members have higher addresses within a class object. The order of allocation of non-static data members with different access control is unspecified (11). Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions (10.3) and virtual base classes (10.1).
?
Was genau meinst du von wegen "kein Mischen"!?
-
Vlt. hapert es bei mir an den Begrifflichkeiten:
"same access control" -> alles ein Typ (z.B. nur ints) -> kein padding
"different access control" -> gemischt (z.b. int, dann bool, dann int) -> padding möglichKorrekt?
-
Access Control hat nix mit dem Typ zu tun. Same Access Control heißt, dass alle Member z.B. public oder alle Member private sein müssen.
-
Dann bleibt meine Frage, was ist ein Standard-Layout und was nicht?
Denn so wie ich das lese gilt standard-layout -> kein padding.
-
Ok ich hapere an folgenden Punkten:
9 Classes
7 A standard-layout class is a class [✓] that:
* has no non-static data members**[✓]** of type non-standard-layout class**???** (or array of such types) or reference,
* has no virtual functions (10.3)[✓] and no virtual base classes (10.1)[✓],
* has the same access control (Clause 11) for all non-static data members**[✓],
* has no non-standard-layout???** base classes**[✓],
* either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members[✓], and
* has no base classes of the same type as the first non-static data member???**.
-
Paddel2000 schrieb:
Denn so wie ich das lese gilt standard-layout -> kein padding.
Nope, padding wirst du bei structs nicht los. Weder in C noch in C++. Natürlich kannst du die Dokumentation deines Compilers lesen und dir ein struct basteln, bei dem dein jeweiliger Compiler kein Padding einbauen wird. In deinem Beispiel mit lauter ints wird z.B. kaum ein x86 Compiler padding einbauen. Aber das ist eben compilerabhängig...
-
dot schrieb:
Paddel2000 schrieb:
Denn so wie ich das lese gilt standard-layout -> kein padding.
Nope, padding wirst du bei structs nicht los. Weder in C noch in C++. Natürlich kannst du die Dokumentation deines Compilers lesen und dir ein struct basteln, bei dem dein jeweiliger Compiler kein Padding einbauen wird. In deinem Beispiel mit lauter ints wird z.B. kaum ein x86 Compiler padding einbauen. Aber das ist eben compilerabhängig...
Aber man kann auf jeden Fall einen int* benutzen, um durch das struct zu steppen. Bloß manuelle Zeigerarithmetik mit char*/void* und sizeof(int) wird eventuell gefährlich.
-
SeppJ schrieb:
dot schrieb:
Paddel2000 schrieb:
Denn so wie ich das lese gilt standard-layout -> kein padding.
Nope, padding wirst du bei structs nicht los. Weder in C noch in C++. Natürlich kannst du die Dokumentation deines Compilers lesen und dir ein struct basteln, bei dem dein jeweiliger Compiler kein Padding einbauen wird. In deinem Beispiel mit lauter ints wird z.B. kaum ein x86 Compiler padding einbauen. Aber das ist eben compilerabhängig...
Aber man kann auf jeden Fall einen int* benutzen, um durch das struct zu steppen. Bloß manuelle Zeigerarithmetik mit char*/void* und sizeof(int) wird eventuell gefährlich.
Wie soll das funktionieren, wenn nicht garantiert ist, dass es kein Padding gibt!?
-
dot schrieb:
Wie soll das funktionieren, wenn nicht garantiert ist, dass es kein Padding gibt!?
Wenn der Datentyp Alignment zwingend vorschreibt, dann weiß auch der Pointertyp davon. Ich gehe mal davon aus, dass der Compiler nicht aus reiner bosheit Padding einbaut. Es ist irgendwie schwer, ein Beispiel zu konstruieren. Die nativen Datentypen haben nämlich naturgemäß immer passendes Alignment, um hintereinander in ein Array gepackt zu werden und komplexe Datentypen erfüllen die nötigen Bedingungen nicht mehr.
-
Wir reden hier aber von einem Pointer auf einen struct Member und nicht von einem Pointer in ein Array? Du kannst damit auf den jeweiligen struct Member zugreifen, aber du kannst imo nicht einfach über das struct iterieren als wärs ein Array. Natürlich wird der Compiler kein Padding einbauen. Fakt ist aber, dass der C++ Standard nicht garantiert, dass der Compiler kein Padding einbaut...
-
dot schrieb:
Wir reden hier aber von einem Pointer auf einen struct Member und nicht von einem Pointer in ein Array?
Ja, der ganze Witz des Threads ist ja, dass ein
struct{int a,b;}layoutgleich zuint c[2]sein sollte.
-
Und all i'm trying to say is: Der Standard garantiert nicht, dass es so ist, verbietet es aber auch nicht.
-
dot schrieb:
SeppJ schrieb:
dot schrieb:
Paddel2000 schrieb:
Denn so wie ich das lese gilt standard-layout -> kein padding.
Nope, padding wirst du bei structs nicht los. Weder in C noch in C++. Natürlich kannst du die Dokumentation deines Compilers lesen und dir ein struct basteln, bei dem dein jeweiliger Compiler kein Padding einbauen wird. In deinem Beispiel mit lauter ints wird z.B. kaum ein x86 Compiler padding einbauen. Aber das ist eben compilerabhängig...
Aber man kann auf jeden Fall einen int* benutzen, um durch das struct zu steppen. Bloß manuelle Zeigerarithmetik mit char*/void* und sizeof(int) wird eventuell gefährlich.
Wie soll das funktionieren, wenn nicht garantiert ist, dass es kein Padding gibt!?
Ist es doch.
Die Alignment-Requirements der eingebauten Typen entspricht ihrer Grösse, was soll es da dann zu padden geben?
Bzw. wenn sie in einem Array "dicht" liegen können, wie kann es dann nen Alignment-Requirement geben die das verhindert? Wie also könnten sie dann jemals im Struct nicht dicht liegen?
Ich sehe da keinen Weg.
-
Ich bezweifle nicht, dass kein Compiler da Padding einbauen wird, hab nie was Anderes behauptet. Aber der Standard garantiert imo nicht, dass der Compiler da kein Padding einbaut und ganz sicher nicht, dass du einfach mit Pointerarithmetik in dem struct rumlaufen kannst. In der Praxis wird das natürlich funktionieren. Das ist dann aber eben bestenfalls implementation defined Behavior. Wenn man nicht gerade einen Betriebssystemkernel schreibt, dann macht man sowas einfach nicht...
Abgesehen davon, kann ich zumindest nach schnellem Überfliegen der offensichtlichen Stellen nicht ausmachen, wo genau im Standard stehen soll, dass die Alignment Requirements der einfachen Typen ihrer Größe entsprechen. Ich seh da lediglich Dinge wie dass alle unsigned Integer das selbe Alignment haben wie die korrespondierenden signed Typen oder dass alle char Typen das schwächste Alignment haben. Auch steht dort sogar explizit folgendes:
The alignment required for a type might be different when it is used as the type of a complete object and when it is used as the type of a subobject.