Von einem Kollegen als Puzzle gegeben, kann ich nicht herausfinden, wie dieses C-Programm tatsächlich kompiliert und ausgeführt wird. Was ist dieser >>>=
Operator und das seltsame 1P1
Literal? 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;
}
0x.1P1
ist ein hexadezimales Literal mit einem Exponenten. Das0x.1
ist der Zahlenteil oder 1/16 hier. Die Zahl nach dem 'P' ist die Zweierpotenz, mit der die Zahl multipliziert wird. Also0x.1p1
ist wirklich 1/16 * 2 oder 1/8. Und wenn Sie sich fragen , über0xFULL
das ist nur0xF
, undULL
ist das Suffix für eineunsigned long long
Antworten:
Die Linie:
enthält die Digraphen
:>
und<:
, die übersetzen ,]
und die[
jeweils so ist es äquivalent zu:Das Literal
0xFULL
ist dasselbe wie0xF
(was hexadezimal ist15
); Das gibtULL
nur an, dass es einunsigned long long
Literal ist . In jedem Fall ist es als Boolescher Wert wahr, also wird0xFULL ? '\0' : -1
ausgewertet'\0'
, was ein Zeichenliteral ist, dessen numerischer Wert einfach ist0
.Inzwischen
0X.1P1
ist ein hexadezimales Gleitkomma-Literal gleich 2/16 = 0,125. Da es nicht Null ist, gilt es in jedem Fall auch als Boolescher Wert. Wenn Sie es also zweimal negieren, wird es!!
erneut erzeugt1
. Somit vereinfacht sich das Ganze bis auf:Der Operator
>>=
ist eine zusammengesetzte Zuweisung , die ihren linken Operanden um die vom rechten Operanden angegebene Anzahl von Bits nach rechts verschiebt und das Ergebnis zurückgibt. In diesem Fall hat der richtige Operanda[1]
immer den Wert1
, also entspricht er:oder gleichwertig:
Der Anfangswert von
a[0]
ist 10. Nach einmaligem Verschieben nach rechts wird er zu 5, dann (abgerundet) zu 2, dann zu 1 und schließlich zu 0, an welchem Punkt die Schleife endet. Somit wird der Schleifenkörper dreimal ausgeführt.quelle
P
in0X.1P1
.e
in10e5
, außer dass Siep
für hexadezimale Literale verwenden müssen, dae
es sich um eine hexadezimale Ziffer handelt.p
trennt das die Mantisse und den Exponenten, genau wiee
in der normalen wissenschaftlichen Float-Notation; Ein Unterschied besteht darin, dass bei Hex-Floats die Basis des Exponentialteils 2 statt 10 ist, also0x0.1p1
0x0,1 = 1/16 mal 2¹ = 2. (In jedem Fall ist hier nichts davon von Bedeutung; keine Nicht-Null Wert würde dort gleich gut funktionieren.)char
Literal" sagt , und einen Wikipedia-Link hinzugefügt. Vielen Dank!Es ist ein ziemlich dunkler Code, der Digraphen enthält , nämlich
<:
und:>
die alternative Token für[
bzw.]
sind. Es gibt auch eine Verwendung des bedingten Operators . Es gibt auch einen Bitverschiebungsoperator , die richtige Verschiebungszuordnung>>=
.Dies ist eine besser lesbare Version:
und eine noch besser lesbare Version, die die Ausdrücke in
[]
den Werten ersetzt, in die sie aufgelöst werden:Das Ersetzen
a[0]
unda[1]
für ihre Werte sollte es einfach machen, herauszufinden, was die Schleife tut, dh das Äquivalent von:Dies führt einfach eine (ganzzahlige) Division durch 2 in jeder Iteration durch, wodurch die Sequenz erzeugt wird
5, 2, 1
.quelle
????
, anstatt???
wie es das OP bekam? (Huh.) Codepad.org/nDkxGUNi tut produzieren???
.Lassen Sie uns den Ausdruck von links nach rechts durchgehen:
Das erste, was mir auffällt, ist, dass wir den ternären Operator aus der Verwendung von verwenden
?
. Also der Unterausdruck:sagt: "Wenn
0xFULL
nicht Null ist, geben Sie'\0'
andernfalls zurück-1
.0xFULL
ist ein hexadezimales Literal mit dem vorzeichenlosen Long-Long-Suffix - was bedeutet, dass es ein hexadezimales Literal vom Typ istunsigned long long
. Das ist jedoch nicht wirklich wichtig, da0xF
es in eine reguläre Ganzzahl passen kann.Außerdem konvertiert der ternäre Operator die Typen des zweiten und dritten Terms in ihren gemeinsamen Typ.
'\0'
wird dann konvertiertint
, was gerecht ist0
.Der Wert von
0xF
ist viel größer als Null, also geht er vorbei. Der Ausdruck wird jetzt:Als nächstes
:>
ist ein Digraph . Es ist ein Konstrukt, das sich erweitert zu]
:>>=
ist der signierte Rechtsschichtbetreiber, wir können das ausräumena
, um es klarer zu machen.Darüber hinaus
<:
ist ein Digraph, der sich erweitert zu[
:0X.1P1
ist ein hexadezimales Literal mit einem Exponenten. Aber egal welcher Wert,!!
alles, was nicht Null ist, ist wahr.0X.1P1
ist0.125
was nicht Null ist, also wird es:Das
>>=
ist der signierte Rechtsschichtbetreiber. Es ändert den Wert seines linken Operanden, indem es seine Bits um den Wert auf der rechten Seite des Operators nach vorne verschiebt.10
in binär ist1010
. Also hier sind die Schritte:>>=
Gibt das Ergebnis seiner Operation zurück. Solange die Verschiebunga[0]
für jedes Mal, wenn ihre Bits um eins nach rechts verschoben werden, ungleich Null bleibt, wird die Schleife fortgesetzt. Der vierte Versuch ist, woa[0]
wird0
, so dass die Schleife nie betreten wird.Infolgedessen
?
wird dreimal gedruckt.quelle
:>
ist ein Digraph , kein Trigraph. Es wird nicht vom Präprozessor verarbeitet, sondern lediglich als Token-Äquivalent zu erkannt]
.?:
) hat einen Typ, der dem allgemeinen Typ des zweiten und dritten Terms entspricht. Der erste Term ist immer eine Bedingung und hat einen Typbool
. Da sowohl der zweite als auch der dritte Term vom Typ sind, wirdint
das Ergebnis der ternären Operationint
nicht seinunsigned long long
.#
und##
haben digraph Formen; Nichts hindert eine Implementierung daran, Digraphen in den frühen Übersetzungsphasen in Nicht-Digraphen zu übersetzen