Forum: Mikrocontroller und Digitale Elektronik C++ Überlauf bei Zwischenergebnis provozieren


von Andreas (Gast)


Lesenswert?

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

von Andreas B. (andreas_b395)


Angehängte Dateien:

Lesenswert?

Das Beispiel natürlich

von Rolf M. (rmagnus)


Lesenswert?

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.

von Andreas B. (andreas_b395)


Lesenswert?

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.

von Andreas B. (andreas_b395)


Lesenswert?

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
Noch kein Account? Hier anmelden.