Structures
-
@Joshuah
Der Compiler kennt deine Zielplattform und weiß, dass die CPU schnell auf Daten zugreifen kann, die auf einer (WORD/DWORD/QWORD) Adresse liegen, je nach Zielplattform. Also versucht er, die Member des struct auf die jeweiligen Adressen auszurichten und fügt daziwschen Padding Bytes ein. Auch wenn es im Standard keine Möglichkeit gibt das Padding zu beeinflussen bieten fast alle Compiler eine eigene Lösung dafür an. Ich habe mir angewöhnt für den Fall, dass die Strukturgröße fest sein mussstatic_asserts
einzubauen:#pragma pack( push, 1) struct MyData { int32_t a1; int8_t c; int8_t d; double v; }; #pragma pack (pop) static_assert( sizeof( Mydata ) == 14, "sizeof( MyData ) must be 14!" ); Edit: C++11 Datentypen ergänzt.
-
Ah okay! Ja, ich kenne den Nutzen von "Padding" nicht und muss zugeben, dass ich in diesem Thread das erste Mal bewusst davon gelesen habe. Das bedeutet, dass die Adressen im Speicher nach Verwendung eingeteilt werden?
Auch "pragma pack" kenne ich nicht.
Sagt mir alles noch nicht viel, ich werd mich da mal schlaumachen. Danke an alle! .Edit: Moment, wenn man die Zielplattform kennt und damit die Art, wie der Compiler structs arrangiert, kann man dann nicht die Anzahl der Paddingbytes vorhersagen?
-
@Joshuah
Klar kann man das, man muss nur seinen Compiler kennen. Unser 32 Bit Compiler richtet die Member auf DWORD Grenzen aus, aber das ist compilerabhängig und kann sich sogar von Version zu Version ändern. Oft sind solche Dinge aber auch in den Projekteinstellungen festgelegt und lassen sich dort einstellen.
-
@Joshuah sagte in Structures:
Ah okay! Ja, ich kenne den Nutzen von "Padding" nicht und muss zugeben, dass ich in diesem Thread das erste Mal bewusst davon gelesen habe. Das bedeutet, dass die Adressen im Speicher nach Verwendung eingeteilt werden?
naja im prinzip bedeutet das, dass dein 64-bit rechner intern besonders gut (schnell) auf 64-bit variablen zugreifen kann und der compiler dann quasi intern einzelne kleinere variablen vergrößert bzw. die adressen halt so anpasst. allgemein brauchst du dich da nicht weiter drum zu kümmern, aber wenn die struktur auf deinem windows-rechner 120 bytes groß und die gleiche struktur auf dem unix-rechner 126 bytes groß ist, hast du halt ein problem, wenn du die struktur in einem rutsch über das netzwerk schickst.
Edit: Moment, wenn man die Zielplattform kennt und damit die Art, wie der Compiler structs arrangiert, kann man dann nicht die Anzahl der Paddingbytes vorhersagen?
ja aber ich würde mich halt nicht darauf verlassen, dass das immer so bleibt.
-
@Joshuah Damit das funktioniert muss du neben dem bereits erwähnten Structure-Packing auch noch sicherstellen dass die Liste der Member nicht durch Access-Specifier (
private:
,protected:
,public:
) unterbrochen werden. Denn Sektionen die durch Access-Specifier "getrennt" sind darf der Compiler umordnen.Weiters ist zu beachten dass solche Konstrukte
struct s{ int a; int b; int c; int d; int e; }; void foo() { s S = ...; // assuming s has no padding int* p = &S.a; int a = *p; //OK p++; // ??? int b = *p; // ??? }
nicht unbedingt OK sind. Ich kenne den Standard dazu nicht gut genug um zu dem konkreten Beispiel was konkretes sagen zu könne. Allerdings gibt es hier zumindest ein potentielles Problem, und das ist dass es in C++ nicht reicht die richtige Adresse für irgendwas zu haben, sondern man muss die Adresse auf "legalem" Weg bekommen haben. Und ob
p++
hier legal ist... keine Ahnung.Was auf jeden Fall OK gehen sollte, ist wenn du den Umweg über
memcpy
machst:struct s{ int a; int b; int c; int d; int e; }; void foo() { s S = ...; // assuming s has no padding unsigned char* p = reinterpret_cast<unsigned char*>(&S.a); int a; memcpy(&a, p, sizeof(int)); p += sizeof(int); int b; memcpy(&b, p, sizeof(int)); }
-
@Wade1234 sagte in Structures:
naja im prinzip bedeutet das, dass dein 64-bit rechner intern besonders gut (schnell) auf 64-bit variablen zugreifen kann
Das hängt mehr von der Busbreite des Datenbuses zum Speicher ab.
Wenn der Bus 16-Bit breit ist, dann hat der nur gerade Adressen.
Wird ein Byte (8-Bit) geladen, dann greift die CPU auf alle 16-Bit zu, liest aber nur 8-Bit in das Register.Wenn jetzt aber ein Word (16-Bit) auf einer ungeraden Adresse liegt, dann muss die CPU zweimal lesen.
Adresse | LowByte | HiByte | 0000 | Byte 0 | Byte 1 | 0002 | Byte 2 | Byte 3 | 0004 | Word 0 | 0006 | Word 1 | 0008 | Byte 4 | Word 2 | 000A | Word 2 | |
Um das zu umgehen, legt der Compiler die Variablen gerne auf solche Adressen, dass die Daten mit einem Zugriff geladen werden können.
Das ist stark von der Architektur abhängig. Manche können 16-Bit Werte gar nicht von ungeraden Adressen laden.
-
Ähm @hustbaer, ich weiß ja net ob es hilft, aber ich programmiere in C. Nicht in C++, davon habe ich keine Ahnung und ich werde zumindest im nächsten halben Jahr auch nicht umsteigen. Trotzdem nett
-
@DirkB sagte in Structures:
Das hängt mehr von der Busbreite des Datenbuses zum Speicher ab.
ja also ich ging jetzt davon aus, dass bei gängigen 64-bit rechnern daten- und adressbus 64-bit breit sind (auch wenn letzterer wahrscheinlich erst einmal nicht ausgeschöpft werden kann).
@Joshuah sagte in Structures:
davon habe ich keine Ahnung und ich werde zumindest im nächsten halben Jahr auch nicht umsteigen.
sehr löblich!
-
@Joshuah Upps, übersehen dass die Frage ja im C Unterforum ist. Ob es in C auch so üble Stolpersteine gibt wie in C++ weiss ich nicht. Also so Stolpersteine wie eben dass es auch wichtig ist wie man zu einem Zeiger gekommen ist, und nicht nur ob man die richtige Adresse hat.
-
@DirkB sagte in Structures:
Wenn jetzt aber ein Word (16-Bit) auf einer ungeraden Adresse liegt, dann muss die CPU zweimal lesen.
Und shiften und odern, um sich das Word zusammenzubasteln. Daher sollte man bei zeitkritischen Programen auf sowas wie #pragma pack(1) besser verzichten.