Verschiebeoperatoren in C ++

8
  

Wenn der Wert nach dem Schichtoperator ist   ist größer als die Anzahl der Bits in   der linke Operand ist das Ergebnis   nicht definiert. Wenn der linke Operand ist   unsigned, die richtige Verschiebung ist logisch   so verschieben, dass die oberen Bits gefüllt werden   mit Nullen. Wenn der linke Operand   ist unterschrieben, die rechte Schicht darf oder darf   nicht eine logische Verschiebung (das heißt, die   Verhalten ist nicht definiert).

Kann mir jemand erklären, was die obigen Zeilen bedeuten?

    
Jony 30.03.2010, 18:50
quelle

6 Antworten

5

Ich gehe davon aus, dass Sie wissen, was es bedeutet, zu wechseln. Nehmen wir an, Sie haben mit einem 8-Bit char s

zu tun %Vor%

In der ersten Schicht kann der Compiler tun, was er will, denn 9 & gt; 8 [die Anzahl der Bits in char ]. Undefiniertes Verhalten bedeutet, dass alle Wetten aus sind, es gibt keine Möglichkeit zu wissen, was passieren wird. Die zweite Schicht ist klar definiert. Du bekommst 0s auf der linken Seite: 11111111 wird 00001111 . Die dritte Schicht ist wie die erste unbestimmt.

Beachten Sie, dass es in diesem dritten Fall nicht wichtig ist, was der Wert von c ist. Wenn es sich auf signed bezieht, bedeutet dies den Typ der Variablen, nicht, ob der tatsächliche Wert größer als Null ist oder nicht. signed char c = 5 und signed char c = -5 sind beide signiert und Verschiebung nach rechts ist undefiniertes Verhalten.

    
Dennis Zickefoose 30.03.2010, 19:00
quelle
21

Es ist nicht so wichtig, was diese Zeilen bedeuten, sie sind im Wesentlichen falsch.

"Wenn der Wert nach dem Schichtoperator ist größer als die Anzahl der Bits in der linke Operand ist das Ergebnis undefiniert. "

Stimmt, sollte aber "größer als oder gleich" sagen. 5.8 / 1:

  

... das Verhalten ist nicht definiert, wenn der   Der Operand der rechten Hand ist negativ oder   größer als oder gleich der Länge in   Bits des aktualisierten linken Operanden.

Undefiniertes Verhalten bedeutet "tue es nicht" (siehe später). Das heißt, wenn int 32 Bits auf Ihrem System hat, können Sie keine der folgenden Aktionen ausführen:

%Vor%

"Wenn der linke Operand unsigniert ist, ist die rechte Verschiebung eine logische Verschiebung, so dass die oberen Bits mit Nullen gefüllt werden."

Das ist wahr. 5.8 / 3 sagt:

  

Wenn E1 vorzeichenlosen Typ hat oder wenn E1 a hat   signierter Typ und ein nicht negativer Wert,   Das Ergebnis ist der integrale Teil der   Quotient von E1 dividiert durch die Menge   2 erhöht auf die Leistung E2

wenn das für Sie mehr Sinn macht. >>1 entspricht der Division durch 2, >>2 dividiert durch 4, >>3 ist 8 und so weiter. In einer binären Darstellung eines positiven Wertes ist das Teilen durch 2 das gleiche wie das Bewegen aller Bits eins nach rechts, das Verwerfen des kleinsten Bits und das Auffüllen des größten Bits mit 0.

"Wenn der linke Operand signiert ist, kann die Verschiebung nach rechts eine logische Verschiebung sein (dh das Verhalten ist nicht definiert)."

Erster Teil ist wahr (es kann oder kann nicht eine logische Verschiebung sein - es ist auf einigen Compilern / Plattformen, aber nicht anderen. Ich denke, bei weitem ist das häufigste Verhalten, dass es nicht ist). Zweiter Teil ist falsch, das Verhalten ist nicht undefiniert. Undefiniertes Verhalten bedeutet, dass alles passieren darf - ein Absturz, Dämonen, die aus der Nase fliegen, ein zufälliger Wert, was auch immer. Dem Standard ist das egal. Es gibt viele Fälle, in denen der C ++ - Standard besagt, dass das Verhalten nicht definiert ist, aber das ist nicht einer davon.

Wenn der linke Operand signiert ist und der Wert positiv ist, verhält er sich genauso wie eine vorzeichenlose Verschiebung.

Wenn der linke Operand signiert ist und der Wert negativ ist, dann ist der resultierende Wert implementierungsdefiniert. Es ist nicht erlaubt zu stürzen oder Feuer zu fangen. Die Implementierung muss ein Ergebnis liefern, und die Dokumentation für die Implementierung muss genügend Informationen enthalten, um das Ergebnis zu definieren. In der Praxis beginnt die "Dokumentation für die Implementierung" mit der Compiler-Dokumentation, die Sie jedoch implizit oder explizit auf andere Dokumente für das Betriebssystem und / oder die CPU verweisen kann.

Wieder aus dem Standard, 5.8 / 3:

  

Wenn E1 Typ und Negativ signiert hat   Wert, der resultierende Wert ist   Implementierung definiert.

    
Steve Jessop 30.03.2010 19:18
quelle
5
  

Wenn der Wert nach dem Verschiebeoperator größer als die Anzahl der Bits im linken Operanden ist, ist das Ergebnis nicht definiert.

Es bedeutet (unsigned int)x >> 33 kann alles tun [1] .

  

Wenn der linke Operand nicht vorzeichenbehaftet ist, ist die rechte Verschiebung eine logische Verschiebung, so dass die oberen Bits mit Nullen gefüllt werden.

Das bedeutet 0xFFFFFFFFu >> 4 muss 0x0FFFFFFFu

sein
  

Wenn der linke Operand signiert ist, kann die rechte Verschiebung eine logische Verschiebung sein oder auch nicht (dh das Verhalten ist nicht definiert).

Dies bedeutet 0xFFFFFFFF >> 4 kann 0xFFFFFFFF (arithmetische Verschiebung) oder 0x0FFFFFFF (logische Verschiebung) oder alles, was durch physikalisches Gesetz erlaubt ist, d.h. das Ergebnis ist undefiniert.

[1]: auf 32-Bit-Maschine mit einem 32-Bit int .

    
kennytm 30.03.2010 19:00
quelle
2
  

Wenn der Wert nach dem Schichtoperator ist   ist größer als die Anzahl der Bits in   der linke Operand ist das Ergebnis   undefiniert.

Wenn Sie versuchen, eine 32-Bit-Ganzzahl um 33 zu verschieben, ist das Ergebnis nicht definiert. h., es kann oder darf nicht nur aus Nullen bestehen.

  

Wenn der linke Operand unsigniert ist,   Die richtige Verschiebung ist also eine logische Verschiebung   die oberen Bits werden mit gefüllt   Nullen.

Unsignierter Datentyp wird beim Rechtsschieben mit Nullen aufgefüllt.

so 1100 >> 1 == 0110

  

Wenn der linke Operand signiert ist,   die Rechtsverschiebung kann oder kann nicht a sein   logische Verschiebung (das heißt, das Verhalten   ist undefiniert).

Wenn der Datentyp signiert ist, ist das Verhalten nicht definiert. Signierte Datentypen werden in einem speziellen Format gespeichert, wobei das linke Bit positiv oder negativ anzeigt. Wenn Sie also auf eine vorzeichenbehaftete ganze Zahl umschalten, tun Sie das nicht, was Sie erwarten. Sehen Sie den Wikipedia-Artikel für Details.

Ссылка

    
Matthew 30.03.2010 18:59
quelle
2

Um etwas Kontext zu geben, hier ist der Anfang dieses Absatzes:

  

Die Shift-Operatoren manipulieren auch Bits. Der Linksverschiebungsoperator (& lt; & lt;) erzeugt den Operanden links vom Operator, der um die nach dem Operator spezifizierte Anzahl von Bits nach links verschoben ist. Der Rechtsverschiebungsoperator (& gt; & gt;) erzeugt den Operanden links vom Operator, der um die nach dem Operator spezifizierte Anzahl von Bits nach rechts verschoben ist.

Jetzt der Rest, mit Erklärungen:

  

Wenn der Wert nach dem Verschiebeoperator größer als die Anzahl der Bits im linken Operanden ist, ist das Ergebnis nicht definiert.

Wenn Sie eine 32-Bit-Ganzzahl haben und versuchen, Bit-Bit zu verschieben, ist das nicht erlaubt und das Ergebnis ist nicht definiert. Mit anderen Worten, das Ergebnis könnte alles sein, oder Ihr Programm könnte abstürzen.

  

Wenn der linke Operand unsigniert ist, ist die rechte Verschiebung eine logische Verschiebung, so dass die oberen Bits mit Nullen gefüllt werden.

Dies sagt, dass es definiert ist, a >> b zu schreiben, wenn a ein vorzeichenloser int ist. Wenn Sie sich nach rechts bewegen, werden die niedrigstwertigen Bits entfernt, andere Bits werden nach unten verschoben, und die höchstwertigen Bits werden zu Null.

Mit anderen Worten:

%Vor%
  

Wenn der linke Operand signiert ist, kann die rechte Verschiebung eine logische Verschiebung sein oder auch nicht (dh das Verhalten ist nicht definiert).

Eigentlich glaube ich, dass das Verhalten hier definiert ist, wenn a negativ und definiert ist, wenn a positiv ist und nicht wie im Zitat vorgeschlagen. Das bedeutet, wenn Sie a >> b machen, wenn a eine negative ganze Zahl ist, können viele verschiedene Dinge passieren. Um zu sehen, was Sie erhalten, sollten Sie die Dokumentation für Ihren Compiler lesen. Eine gängige Implementierung ist das Verschieben von Nullen, wenn die Zahl positiv ist, und von Einsen, wenn die Zahl negativ ist. Sie sollten sich jedoch nicht auf dieses Verhalten verlassen, wenn Sie tragbaren Code schreiben möchten.

    
Mark Byers 30.03.2010 19:13
quelle
1

Ich nehme an, das Schlüsselwort ist "undefiniert", was bedeutet, dass die Spezifikation nicht sagt, was passieren soll. Die meisten Compiler werden in solchen Fällen etwas Sinnvolles tun, aber Sie können sich im Allgemeinen nicht auf irgendein Verhalten verlassen. Es ist normalerweise am besten, das Aufrufen von undefiniertem Verhalten zu vermeiden, es sei denn, die Dokumentation für den von Ihnen verwendeten Compiler gibt an, was es im konkreten Fall tut.

Der erste Satz besagt, dass es undefiniert ist, wenn Sie beispielsweise versuchen, einen 32-Bit-Wert um mehr als 32 Bit zu verschieben.

Die zweite besagt, dass wenn Sie einen unsigned int nach rechts verschieben, die linken Bits mit Nullen gefüllt werden.

Die dritte besagt, dass wenn Sie ein int-Zeichen nach rechts verschieben, nicht definiert ist, was in die linken Bits eingefügt wird.

    
Nick Moore 30.03.2010 18:57
quelle

Tags und Links