Что такое оператор >> >= в C?
Учитывая коллегу как загадку, я не могу понять, как эта программа на самом деле компилируется и запускается. Что это за оператор >>>=
и странный 1P1
литерал? Я тестировал в Clang и GCC. Предупреждений нет, а выход "???"
#include <stdio.h>
int main()
{
int a[2]={ 10, 1 };
while( a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ] )
printf("?");
return 0;
}
Ответы
Ответ 1
Строка:
while( a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ] )
содержит digraphs :>
и <:
, которые переводятся на ]
и [
соответственно, поэтому эквивалентно:
while( a[ 0xFULL?'\0':-1 ] >>= a[ !!0X.1P1 ] )
Литерал 0xFULL
совпадает с 0xF
(который является шестнадцатеричным для 15
); ULL
просто указывает, что это unsigned long long
литерал. В любом случае, как логическое значение true, поэтому 0xFULL ? '\0' : -1
оценивается как '\0'
, который является символом , численное значение которого просто 0
.
Между тем 0X.1P1
представляет собой шестнадцатеричный литерал с плавающей запятой, равный 2/16 = 0.125. В любом случае, будучи ненулевым, оно также истинно как логическое, поэтому его двойное отключение с !!
вызывает 1
. Таким образом, все это упрощается до:
while( a[0] >>= a[1] )
Оператор >>=
представляет собой составное назначение, которое бит сдвигает свой левый операнд на правое число бит, заданное правым операндом, и возвращает результат. В этом случае правый операнд a[1]
всегда имеет значение 1
, поэтому он эквивалентен:
while( a[0] >>= 1 )
или, что эквивалентно:
while( a[0] /= 2 )
Начальное значение a[0]
равно 10. После смещения вправо один раз он становится 5, затем (округляется) 2, затем 1 и, наконец, 0, после чего цикл заканчивается. Таким образом, тело цикла запускается три раза.
Ответ 2
Это довольно неясный код, включающий digraphs, а именно <:
и :>
, которые являются альтернативными токенами для [
и ]
соответственно. Существует также некоторое использование условного оператора . Существует также оператор смещения битов, назначение правого сдвига >>=
.
Это более читаемая версия:
while( a[ 0xFULL ? '\0' : -1 ] >>= a[ !!0X.1P1 ] )
и еще более читаемую версию, заменив выражения в []
на значения, которые они разрешают:
while( a[0] >>= a[1] )
Замена a[0]
и a[1]
для их значений должна упростить вычисление того, что делает цикл, т.е. эквивалент:
int i = 10;
while( i >>= 1)
который просто выполняет (целое) деление на 2 на каждой итерации, производя последовательность 5, 2, 1
.
Ответ 3
Перейдем к выражению слева направо:
a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ]
Первое, что я замечаю, это то, что мы используем тернарный оператор из использования ?
. Итак, подвыражение:
0xFULL ? '\0' : -1
говорит: "Если 0xFULL
отличен от нуля, верните '\0'
, в противном случае -1
. 0xFULL
- это шестнадцатеричный литерал с беззнаковым длинно-длинным суффиксом - это означает шестнадцатеричный литерал типа unsigned long long
Это не имеет большого значения, потому что 0xF
может вписываться в регулярное целое число.
Кроме того, тернарный оператор преобразует типы второго и третьего членов в их общий тип. '\0'
затем преобразуется в int
, который просто 0
.
Значение 0xF
больше, чем ноль, поэтому оно проходит. Теперь выражение будет выглядеть следующим образом:
a[ 0 :>>>=a<:!!0X.1P1 ]
Далее, :>
является digraph. Это конструкция, которая расширяется до ]
:
a[0 ]>>=a<:!!0X.1P1 ]
>>=
является подписанным оператором сдвига справа, мы можем выделить это из a
, чтобы сделать его более ясным.
Кроме того, <:
является орграфом, который расширяется до [
:
a[0] >>= a[!!0X.1P1 ]
0X.1P1
- это шестнадцатеричный литерал с показателем. Но независимо от значения, !!
всего, что отличное от нуля, истинно. 0X.1P1
0.125
, который отличен от нуля, поэтому он становится:
a[0] >>= a[true]
-> a[0] >>= a[1]
>>=
- это подписанный оператор сдвига справа. Он изменяет значение его левого операнда, сдвигая его биты вперед на значение на правой стороне оператора. 10
в двоичном формате 1010
. Итак, вот шаги:
01010 >> 1 == 00101
00101 >> 1 == 00010
00010 >> 1 == 00001
00001 >> 1 == 00000
>>=
возвращает результат своей работы, поэтому, если сдвиг a[0]
остается ненулевым для каждого момента, когда его биты сдвигаются вправо на один, цикл будет продолжаться. Четвертая попытка заключается в том, что a[0]
становится 0
, поэтому цикл никогда не вводится.
В результате ?
печатается три раза.