sizeof() bei struct



  • Moin. Ich kriege bei der Ausgabe eines sizeof ein Ergebnis, das ich mir noch nciht so ganz erklaeren kann.

    union uval {  
      void* ptr;
      SizeDepend<sizeof(void*)>::IntT i;
      SizeDepend<sizeof(void*)>::FloatT flt;
      bool bol;
    };
    
    struct Value
    {
      char tid;
      uval val;
    };
    
    int main()
    {
      std::cout<<sizeof(Value)<<std::endl;
      std::cout<<sizeof(uval)<<std::endl;
      std::cout<<sizeof(void*)<<std::endl;
      std::cout<<sizeof(bool)<<std::endl;
      std::cout<<sizeof(SizeDepend<sizeof(void*)>::IntT)<<std::endl;
      std::cout<<sizeof(SizeDepend<sizeof(void*)>::FloatT)<<std::endl;
    }
    

    gibt als ausgabe:
    8
    4
    4
    1
    4
    4

    Jetzt ist die Frage: warum 8 bei sizeof(Value)? 5 wuerde doch reichen, oder hat der COmpiler keinen Bock auf Krumme Zahlen?

    (SizeDepend ist eine kleine Templatespielerei, die mir fuer eine gegebene Zahl N den groessten(genauesten) Integer- und Float Typ ermittelt, der <= N bytes belegt)



  • pumuckl schrieb:

    5 wuerde doch reichen, oder hat der COmpiler keinen Bock auf Krumme Zahlen?

    exakt. krumme zahlen sind doof, weil sie nicht schön sind. es geht dabei um effektive speicher operationen. 5 bytes schaufeln ist doof. weil man kann 8 bytes auf einmal problemlos schaufeln. wenn man aber zwingend 5 haben will, dann muss man sie einzeln schaufeln, oder anderweitig sichern dass die 3 "nutzlosen" bytes die man schaufelt nichts kaputt machen.

    ergo ist es einfacher 3 bytes zu verschwenden (siehe "Padding") als bei jeder operation auf die fallstricke zu achten.

    das passende alignment wird von der unterliegenden plattform definiert. unter 32 bit systemen ist 8 eben eine passende zahl. auf anderen systemen mag 12 besser sein oder vielleicht auch 6.



  • Der Grund ist: damit "uval" immer auf einer durch 4 teilbaren Adresse liegt.
    Dann kann die CPU nämlich schneller darauf zugreifen.

    Auf manchen Systemen kann die CPU sogar NUR dann auf ein "long" zugreifen wenn dieses "long" auf einer durch 4 teilbaren Adresse liegt. Auf solchen Systemen MUSS der Compiler also die Struktur 8 Byte gross machen, da sonst bei Arrays für die einzelnen Elemente nichtmehr garantiert wäre dass "uval" auf einer durch 4 teilbaren Adresse liegen würde.

    D.h. in deinem Beispiel wird jeder mir bekannte Compiler die 3 "Füllbytes" nicht hinten dran packen, sondern als "Loch" zwischen dem "char" und dem "uval" einfügen.



  • Hallo pumuckl,

    was du entdeckt hast wird Speicherausrichtung, im Englischen "packing alignment" genannt. Ein 8 Bit Prozessor braucht keine Speicherausrichtung um optimal arbeiten zu können. Ein 16 Bit Prozessor benötigt eine 2 Byte Ausrichtung und ein 32 Bit Prozessor eine 4 Byte Ausrichtung. Warum das so ist, ist ja leicht einzusehen. Manche Compiler haben bereits eine 8 Byte Speicherausrichtung für 64Bit Prozessoren als Standard voreingestellt.

    Bei den M$ Compilern wird mit der Option /Zp die Speicherausrichtung eingestellt ( /Zp[1|2|4|8|16] z.B. /Zp1 für „packing alignment off“).

    Mit den Preprozessor Befehl pack kann man das Verhalten pro Deklaration beeinflussen. Mit

    #pragma pack(1)
    struct Value
    {
      char tid;
      uval val;
    };
    

    könntest du die erwartete Speicherausrichtung erreichen (sizeof(Value)==5).
    Wichtig ist das z.B. wenn Daten in ein bestimmten Format abgespeichert werden sollen oder bei Datenaustausch zwischen Systemen mit unterschiedlicher Speicherausrichtung.



  • Naja, da SizeDepend<sizeof(void*)>::IntT wohl ein long sein wird (4 byte) und da das ganze eh nur erste spielereien sind, werd ich da nicht manuell in der Speicherbelegung rumfuhrwerken. Aber danke für die Erklärungen 🙂



  • BerndD schrieb:

    ...Ein 8 Bit Prozessor braucht keine Speicherausrichtung um optimal arbeiten zu können....

    Dann warte mal auf die Diskussion mit Bitfeldern... 😉

    Aber ansonsten hast Du natürlich Recht.

    Gruß,

    Simon2.



  • @BerndD: die 8 Byte Alignment sind nicht für kommende 64 Bit CPUs voreingestellt, sondern weil es bei fast jeder CPU (ob nun 16, 32 oder 64 Bit) besser ist.

    Das liegt unter anderem daran dass es meistens "schlecht" (langsamer) ist wenn eine Struktur auf 2 verschiedene Cache-Lines aufgeteilt wird, oder gar auf 2 verschiedene MMU Pages.
    Mit einem Alignment von 8 Byte verhindert man das effektiv für alle Strukturen die <= 8 Byte sind.

    Zusätzlich bringt es Vorteile wenn ein N-Byte breiter Zugriff auf eine durch N teilbare Adresse erfolgt. Und ein 486er oder Pentium macht durchaus 8-Byte breite Zugriffe (obwohl es eine 32 Bit CPU ist), nämlich z.B. für doubles.


Anmelden zum Antworten