Was ist der >>>= Operator in C?

Lesezeit: 7 Minuten

Benutzeravatar von CustomCalc
CustomCalc

Von einem Kollegen als Puzzle gegeben, kann ich nicht herausfinden, wie dieses C-Programm tatsächlich kompiliert und ausgeführt wird. Was ist das >>>= Betreiber und das Seltsame 1P1 wörtlich? Ich habe in Clang und GCC getestet. Es gibt keine Warnungen und die Ausgabe ist “???”

#include <stdio.h>

int main()
{
    int a[2]={ 10, 1 };

    while( a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ] )
        printf("?");

    return 0;
}

  • Einige davon sind Digraphen.

    – Juanchopanza

    25. August 2014 um 22:04 Uhr

  • @Kay, nein in diesem Fall: :> = ]dann a[…] >> = ein[…]

    – Adriano Repetti

    25. August 2014 um 22:08 Uhr


  • @Marc Ich glaube nicht, dass es “>> >=” sein kann, weil das nicht kompilieren würde, aber der obige Code wird tatsächlich kompiliert.

    – CustomCalc

    25. August 2014 um 22:08 Uhr

  • Das 0x.1P1 ist ein hexadezimales Literal mit einem Exponenten. Das 0x.1 ist der Zahlenteil, hier 1/16. Die Zahl nach dem ‘P’ ist die Zweierpotenz, mit der die Zahl multipliziert wird. So 0x.1p1 ist wirklich 1/16 * 2 oder 1/8. Und falls Sie sich fragen 0xFULL das ist einfach 0xFund ULL ist das Suffix für ein unsigned long long

    – Jackarms

    25. August 2014 um 22:10 Uhr


  • C-Syntax – endloses Material für Experten und Trivia-Liebhaber, aber letztendlich nicht so wichtig.

    – Kerrek SB

    25. August 2014 um 22:41 Uhr

Benutzeravatar von Ilmari Karonen
Ilmari Karonen

Die Linie:

while( a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ] )

enthält die Digraphen :> und <:was übersetzt ] und [ respectively, so it’s equivalent to:

while( a[ 0xFULL?'\0':-1 ]  >>= a[ !!0X.1P1 ] )

Das wörtliche 0xFULL ist das gleiche wie 0xF (was hex für ist 15); das ULL gibt nur an, dass es sich um eine handelt unsigned long long wörtlich. Als boolescher Wert ist es jedenfalls wahr, also 0xFULL ? '\0' : -1 wertet zu '\0'die ein Zeichenliteral dessen Zahlenwert einfach ist 0.

In der Zwischenzeit, 0X.1P1 ist ein hexadezimales Fließkommaliteral gleich 2/16 = 0,125. In jedem Fall ist es, da es nicht Null ist, auch als boolescher Wert wahr, also muss es zweimal mit negiert werden !! wieder produziert 1. Somit vereinfacht sich das Ganze zu:

while( a[0] >>= a[1] )

Der Betreiber >>= ist ein zusammengesetzte Zuordnung das seinen linken Operanden um die Anzahl der Bits, die durch den rechten Operanden angegeben sind, bitweise nach rechts verschiebt und das Ergebnis zurückgibt. In diesem Fall der rechte Operand a[1] hat immer den Wert 1also ist es äquivalent zu:

while( a[0] >>= 1 )

oder gleichwertig:

while( a[0] /= 2 )

Der Anfangswert von a[0] ist 10. Nach einmaligem Verschieben nach rechts wird es 5, dann (abgerundet) 2, dann 1 und schließlich 0, an welchem ​​Punkt die Schleife endet. Somit wird der Schleifenkörper dreimal ausgeführt.

  • Könnten Sie bitte näher darauf eingehen P in 0X.1P1.

    – kay

    25. August 2014 um 22:20 Uhr

  • @Kay: Es ist dasselbe wie e in 10e5außer Sie müssen verwenden p für hexadezimale Literale weil e ist eine Hexadezimalzahl.

    – Dietrich Ep

    25. August 2014 um 22:21 Uhr

  • @Kay: Die Hex-Float-Literale sind Teil von C99, aber GCC akzeptiert sie auch in C++-Code. Wie Dietrich feststellt, die p trennt die Mantisse und den Exponenten, genau wie die e in normaler wissenschaftlicher Float-Notation; Ein Unterschied besteht darin, dass bei Hex-Floats die Basis des Exponentialteils 2 statt 10 ist, also 0x0.1p1 gleich 0x0,1 = 1/16 mal 2¹ = 2. (Auf jeden Fall spielt das hier keine Rolle; jeder Wert ungleich Null würde dort genauso gut funktionieren.)

    – Ilmari Karonen

    25. August 2014 um 22:27 Uhr


  • Minor: Bestimmt '\0' ist ein int wörtlich. Versuchen printf("%zu %zu\n", sizeof ('\0'), sizeof (char));

    – chux – Wiedereinsetzung von Monica

    25. August 2014 um 22:46 Uhr


  • @chux: Anscheinend hängt das davon ab, ob der Code als C oder (wie ursprünglich gekennzeichnet) C++ kompiliert wurde. Aber ich habe den Text so korrigiert, dass er “Zeichenliteral” anstelle von “char wörtlich” und fügte einen Wikipedia-Link hinzu. Danke!

    – Ilmari Karonen

    25. August 2014 um 23:24 Uhr


Benutzeravatar von juanchopanza
Juanchopanza

Es handelt sich um einen ziemlich obskuren Code Digraphennämlich <: und :> die alternative Token für sind [ and ] beziehungsweise. Es gibt auch einige Verwendung von Bedingter Operator. Da ist auch ein Bitverschiebungsoperatordie richtige Schichtzuordnung >>=.

Dies ist eine besser lesbare Version:

while( a[ 0xFULL ? '\0' : -1 ] >>= a[ !!0X.1P1 ] )

und eine noch besser lesbare Version, die die Ausdrücke in ersetzt [] für die Werte, zu denen sie sich auflösen:

while( a[0] >>= a[1] )

Ersetzen a[0] und a[1] denn ihre Werte sollten es leicht machen, herauszufinden, was die Schleife tut, dh das Äquivalent zu:

int i = 10;
while( i >>= 1)

was einfach eine (ganzzahlige) Division durch 2 in jeder Iteration durchführt und die Sequenz erzeugt 5, 2, 1.

  • Ich habe es nicht ausgeführt – würde dies nicht produzieren ????aber eher als ??? wie das OP bekam? (Hm.) codepad.org/nDkxGUNi tut produzieren ???.

    – Jongware

    25. August 2014 um 22:34 Uhr


  • @Jongware die 10 wurden bei der ersten Iteration geteilt. Die von der Schleife ausgewerteten Werte sind also 5, 2, 1 und 0. Sie druckt also nur dreimal.

    – MysticXG

    25. August 2014 um 22:49 Uhr

Benutzeravatar von David G
David G

Gehen wir den Ausdruck von links nach rechts durch:

a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ]

Das erste, was mir auffällt, ist, dass wir den ternären Operator von use of verwenden ?. Also der Teilausdruck:

0xFULL ? '\0' : -1

sagt „wenn 0xFULL nicht null ist, zurück '\0'Andernfalls -1. 0xFULL ist ein hexadezimales Literal mit der unsigned long-long Suffix – was bedeutet, dass es sich um ein hexadezimales Literal des Typs handelt unsigned long long. Das ist aber eigentlich egal, denn 0xF kann in eine reguläre Ganzzahl passen.

Außerdem wandelt der ternäre Operator die Typen des zweiten und dritten Terms in ihren gemeinsamen Typ um. '\0' wird dann umgewandelt in intwas gerecht ist 0.

Der Wert von 0xF ist viel größer als Null, also geht es. Der Ausdruck wird nun:

a[ 0 :>>>=a<:!!0X.1P1 ]

Nächste, :> ist ein Digraph. Es ist ein Konstrukt, das erweitert wird ]:

a[0 ]>>=a<:!!0X.1P1 ]

>>= der vorzeichenbehaftete rechte Verschiebungsoperator ist, können wir davon Abstand nehmen a um es klarer zu machen.

Darüber hinaus, <: ist ein Digraph, der erweitert wird zu [:

a[0] >>= a[!!0X.1P1 ]

0X.1P1 ist ein hexadezimales Literal mit einem Exponenten. Aber egal der Wert, die !! von allem, was nicht Null ist, ist wahr. 0X.1P1 ist 0.125 was nicht Null ist, also wird es:

a[0] >>= a[true]
-> a[0] >>= a[1]

Das >>= ist der vorzeichenbehaftete Rechtsverschiebungsoperator. Er ändert den Wert seines linken Operanden, indem er seine Bits um den Wert auf der rechten Seite des Operators nach vorne verschiebt. 10 in binär ist 1010. Hier also die Schritte:

01010 >> 1 == 00101
00101 >> 1 == 00010
00010 >> 1 == 00001
00001 >> 1 == 00000

>>= gibt das Ergebnis seiner Operation zurück, solange es verschoben wird a[0] jedes Mal ungleich Null bleibt, wenn seine Bits um eins nach rechts verschoben werden, wird die Schleife fortgesetzt. Der vierte Versuch ist wo a[0] wird 0sodass die Schleife nie betreten wird.

Als Ergebnis, ? wird dreimal gedruckt.

  • :> ist ein Digraph, kein Trigraph. Es wird nicht vom Präprozessor verarbeitet, sondern einfach als Token-Äquivalent zu erkannt ].

    – Keith Thompson

    25. August 2014 um 22:33 Uhr

  • @KeithThompson Danke

    – David G

    25. August 2014 um 22:36 Uhr

  • Der ternäre Operator (?:) hat einen Typ, der der gemeinsame Typ des zweiten und dritten Terms ist. Der erste Term ist immer eine Bedingung und hat einen Typ bool. Da sowohl der zweite als auch der dritte Term einen Typ haben int das Ergebnis der ternären Operation sein wird intnicht unsigned long long.

    – Corei

    26. August 2014 um 6:25 Uhr

  • @KeithThompson könnte vom Präprozessor gehandhabt werden. Der Präprozessor muss über Digraphen Bescheid wissen, weil # und ## Digraphformen haben; Nichts hindert eine Implementierung daran, während der frühen Übersetzungsphasen Digraphen in Nicht-Digraphen zu übersetzen

    – MM

    26. August 2014 um 13:15 Uhr

  • @MattMcNabb Es ist lange her, dass ich das wissen musste, aber IIRC als Folge anderer Anforderungen müssen Digraphen in ihrer Digraphform bleiben, bis PP-Token in Token umgewandelt werden (gleich zu Beginn der Übersetzungsphase 7).

    – zol

    26. August 2014 um 13:43 Uhr

1426940cookie-checkWas ist der >>>= Operator in C?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy