Given by a colleague as a puzzle, I cannot figure out how this C program actually compiles and runs. What is this >>>=
operator and the strange 1P1
literal? I have tested in Clang and GCC. There are no warnings and the output is "???"
#include <stdio.h>
int main()
{
int a[2]={ 10, 1 };
while( a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ] )
printf("?");
return 0;
}
The line:
while( a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ] )
contains the digraphs :>
and <:
, which translate to ]
and [
respectively, so it's equivalent to:
while( a[ 0xFULL?'\0':-1 ] >>= a[ !!0X.1P1 ] )
The literal 0xFULL
is the same as 0xF
(which is hex for 15
); the ULL
just specifies that it's an unsigned long long
literal. In any case, as a boolean it's true, so 0xFULL ? '\0' : -1
evaluates to '\0'
, which is a character literal whose numerical value is simply 0
.
Meanwhile, 0X.1P1
is a hexadecimal floating point literal equal to 2/16 = 0.125. In any case, being non-zero, it's also true as a boolean, so negating it twice with !!
again produces 1
. Thus, the whole thing simplifies down to:
while( a[0] >>= a[1] )
The operator >>=
is a compound assignment that bit-shifts its left operand right by the number of bits given by the right operand, and returns the result. In this case, the right operand a[1]
always has the value 1
, so it's equivalent to:
while( a[0] >>= 1 )
or, equivalently:
while( a[0] /= 2 )
The initial value of a[0]
is 10. After shifting right once, it become 5, then (rounding down) 2, then 1 and finally 0, at which point the loop ends. Thus, the loop body gets executed three times.
It is some rather obscure code involving digraphs, namely <:
and :>
which are alternative tokens for [
and ]
respectively. There is also some use of the conditional operator. There is also a bit shifting operator, the right shift assignment >>=
.
This is a more readable version:
while( a[ 0xFULL ? '\0' : -1 ] >>= a[ !!0X.1P1 ] )
and an even more readable version, replacing the expressions in the []
for the values they resolve to:
while( a[0] >>= a[1] )
Replacing a[0]
and a[1]
for their values should make it easy to figure out what the loop is doing, i.e. the equivalent of:
int i = 10;
while( i >>= 1)
which is simply performing (integer) division by 2 in each iteration, producing the sequence 5, 2, 1
.
Let's go through the expression left-to-right:
a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ]
The first thing I notice is that we are using the ternary operator from the use of ?
. So the subexpression:
0xFULL ? '\0' : -1
is saying "if 0xFULL
is non-zero, return '\0'
, otherwise -1
. 0xFULL
is a hexadecimal literal with the unsigned long-long suffix - meaning it's a hexadecimal literal of type unsigned long long
. That doesn't really matter though, because 0xF
can fit inside a regular integer.
Also, the ternary operator converts the types of the second and third terms to their common type. '\0'
is then converted to int
, which is just 0
.
The value of 0xF
is way bigger than zero, so it passes. The expression now becomes:
a[ 0 :>>>=a<:!!0X.1P1 ]
Next, :>
is a digraph. It is a construct that expands to ]
:
a[0 ]>>=a<:!!0X.1P1 ]
>>=
is the signed right shift operator, we can space that out from a
to make it clearer.
Moreover, <:
is a digraph that expands to [
:
a[0] >>= a[!!0X.1P1 ]
0X.1P1
is a hexadecimal literal with an exponent. But no matter the value, the !!
of anything that's non-zero is true. 0X.1P1
is 0.125
which is non-zero, so it becomes:
a[0] >>= a[true]
-> a[0] >>= a[1]
The >>=
is the signed right shift operator. It changes the value of its left operand by shifting its bits forward by the value on the operator's right side. 10
in binary is 1010
. So here are the steps:
01010 >> 1 == 00101
00101 >> 1 == 00010
00010 >> 1 == 00001
00001 >> 1 == 00000
>>=
returns the result of its operation, so as long as shifting a[0]
remains non-zero for every time its bits are shifted right by one, the loop will continue. The fourth attempt is where a[0]
becomes 0
, so the loop is never entered.
As a result, ?
is printed three times.
来源:https://stackoverflow.com/questions/25495042/what-is-the-operator-in-c