Hallo, um Bitgruppen zu lesen habe ich mir eine kleine Funktion geschrieben, die eine Bitgruppe liest und den Wert zurück gibt. Eigentlich dachte ich das angehängte Beispiel sollte zu einem Überlauf im folgenden Kommando führen: (BitGroupMask << BitGroupPosition) Laut Standard werden Zwischenergebnisse als Integer interpretiert. Jedoch stimmt das Ergebnis für sämtliche ausprobierten Werte. Anscheinend überprüft der Compiler den größten Datentyp im gesamten Ausdruck. Zumindest meine Vermutung. Meine Frage: Liegt das am Compiler, dass es immer funktioniert (GCC), könnte also mit anderen Compiler schief gehen oder ist das im Standard so festgelegt? Gruß Andreas
Andreas schrieb: > (BitGroupMask << BitGroupPosition) > > Laut Standard werden Zwischenergebnisse als Integer interpretiert. Der Typ des Ergebnisses beim Shift ist der gleiche wie der des linken Operators. Falls dieser kleiner als int ist, wird er erst auf int erweitert. Somit wird dein Shift mit Typ int durchgeführt, also bei dir vermutlich in 32 Bit. Shifts um mehr Bits als die Größe des Ergebnis-Typs produzieren undefiniertes Verhalten. Da du hier versuchst, eine 32-Bit-Variable um 60 Bit zu schieben, ist das gegeben. Dass am Ende trotzdem zufällig das richtige rauskommt, liegt an einer Reihe von verschiedenen Umständen, aber das ist alles andere als garantiert. Ich habe auch etwas gebraucht, um die Erklärung für das richtige Endergebnis zu finden: Bei mir ist das Ergebnis des Shifts 0xf0000000, was schon mal nicht dem für die Berechnung eigentlich nötigen Wert entspricht - undefiniertes Verhalten eben. Da es als (signed) int aber negativ ist, wird später bei der Erweiterung auf 64 Bit eine Sign-Extension durchgeführt, und es kommt 0xfffffffff0000000 heraus. Das wird dann mit Var verODERt, so dass 0xbffffffff0000000 herauskommt. Dann shiftest du diesen Wert wieder um 60 Bit nach rechts, so dass in diesem Fall zufällig das 0xb herauskommt, das du dir wünschst. Probier es mal mit:
1 | uint64_t Variable{0b1111111110111111111111111111111111111111111111111111111111111111ULL}; |
2 | uint8_t GMask{0b1111}; |
3 | uint8_t GPos{52}; |
Damit müsste bei korrekter Umsetzung das gleiche Ergebnis rauskommen, tut es bei mir aber nur, wenn ich den obigen Shift ersetze mit: ((uint64_t)BitGroupMask << BitGroupPosition) > Jedoch stimmt das Ergebnis für sämtliche ausprobierten Werte. > Anscheinend überprüft der Compiler den größten Datentyp im gesamten > Ausdruck. Zumindest meine Vermutung. Das wäre nicht konform. > Meine Frage: Liegt das am Compiler, dass es immer funktioniert (GCC), > könnte also mit anderen Compiler schief gehen oder ist das im Standard > so festgelegt? Es ist mehr oder weniger Zufall, dass es funktioniert.
Hallo Rolf, vielen Dank für die ausführliche Antwort. Hab mir schon gedacht, dass ich genau die Werte probiert hab, bei denen es zufälligerweise funktioniert. Wie das doch meistens der Fall ist ;-) Wenn ich die Zeile so verändere:
1 | return (VarType)((Var & ((VarType)BitGroupMask << BitGroupPosition)) >> BitGroupPosition); |
sollte ich eigentlich immer auf der sicheren Seite sein. Falls das Zwischenergebnis größer als VarType ist, stimmen die Maske und Position einfach nicht.
Andreas B. schrieb: > Wenn ich die Zeile so verändere: >
1 | return (VarType)((Var & ((VarType)BitGroupMask << BitGroupPosition)) >> BitGroupPosition); |
den letzten Cast kann ich mir wsl schenken:
1 | return ((Var & ((VarType)BitGroupMask << BitGroupPosition)) >> BitGroupPosition); |
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.