Заданный коллегой как загадка, я не могу понять, как на самом деле эта 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;
}
0x.1P1
шестнадцатеричный литерал с показателем степени. Это0x.1
номер части, или 1/16 здесь. Число после 'P' является степенью двойки, на которую умножается число. Так0x.1p1
действительно 1/16 * 2 или 1/8. И если вам интересно,0xFULL
что это просто0xF
, иULL
является суффиксом дляunsigned long long
Ответы:
Линия:
содержит орграфы
:>
и<:
, которые переводят в]
и[
соответственно, так что это эквивалентно:Литерал
0xFULL
такой же как0xF
(который является шестнадцатеричным для15
); тоULL
просто указывает , что Этоunsigned long long
буквальное . В любом случае, как логическое значение, оно истинно, поэтому0xFULL ? '\0' : -1
оценивается'\0'
как символьный литерал , числовое значение которого просто0
.Между тем,
0X.1P1
это шестнадцатеричный литерал с плавающей точкой, равный 2/16 = 0,125. В любом случае, будучи ненулевым, это также верно как логическое значение, поэтому отрицание его дважды с помощью!!
снова производит1
. Таким образом, все это упрощается до:Оператор
>>=
является составным присваиванием, которое сдвигает бит левого операнда вправо на количество битов, заданных правым операндом, и возвращает результат. В этом случае правый операндa[1]
всегда имеет значение1
, поэтому оно эквивалентно:или, что эквивалентно:
Начальное значение
a[0]
равно 10. После однократного смещения вправо становится 5, затем (округление вниз) 2, затем 1 и, наконец, 0, после чего цикл заканчивается. Таким образом, тело цикла выполняется три раза.источник
P
ин0X.1P1
.e
в10e5
, за исключением того, что вы должны использоватьp
для шестнадцатеричных литералов, потому чтоe
это шестнадцатеричная цифра.p
мантисса и показатель степени разделяются, какe
в обычной научной нотации с плавающей точкой; Одно из отличий состоит в том, что при шестнадцатеричных числах основание экспоненциальной части равно 2 вместо 10, поэтому оно0x0.1p1
равно 0x0,1 = 1/16 умножить на 2¹ = 2. значение будет работать одинаково хорошо там.)char
буквальный», и добавил ссылку в Википедии. Спасибо!Это некоторый довольно туманен код с участием диграфов , а именно ,
<:
и:>
которые являются альтернативными маркерами для[
и]
соответственно. Существует также некоторое использование условного оператора . Также есть оператор сдвига битов , назначение сдвига вправо>>=
.Это более читаемая версия:
и еще более читаемая версия, заменяющая выражения в
[]
значениях, для которых они разрешают:Замена
a[0]
иa[1]
их значения должны облегчить понимание того, что делает цикл, то есть эквивалент:который просто выполняет (целочисленное) деление на 2 в каждой итерации, создавая последовательность
5, 2, 1
.источник
????
бы, а не так,???
как получил ОП? (Ха.) Codepad.org/nDkxGUNi действительно производит???
.Давайте пройдемся по выражению слева направо:
Первое, что я заметил, это то, что мы используем троичный оператор от использования
?
. Итак, подвыражение:говорит "если
0xFULL
не ноль, возвращайте'\0'
, в противном случае-1
.0xFULL
это шестнадцатеричный литерал с беззнаковым длинным-длинным суффиксом - это означает, что это шестнадцатеричный литерал типаunsigned long long
. Это на самом деле не имеет значения, потому что0xF
может помещаться внутри обычного целого числа.Кроме того, троичный оператор преобразует типы второго и третьего членов в их общий тип.
'\0'
затем преобразуется вint
, что просто0
.Значение
0xF
намного больше нуля, поэтому оно проходит. Выражение теперь становится:Далее
:>
идет орграф . Это конструкция, которая расширяется до]
:>>=
является подписанным оператором сдвига вправо, мы можем выделить его,a
чтобы сделать его более понятным.Кроме того,
<:
это орграф, который расширяется до[
:0X.1P1
является шестнадцатеричным литералом с показателем степени. Но независимо от значения,!!
все, что не равно нулю, верно.0X.1P1
это0.125
ненулевое значение, поэтому оно становится:Это
>>=
подписанный оператор правого сдвига. Он изменяет значение своего левого операнда, сдвигая свои биты вперед на значение с правой стороны оператора.10
в двоичном есть1010
. Итак, вот шаги:>>=
возвращает результат своей операции, поэтому, пока смещениеa[0]
остается ненулевым, каждый раз, когда его биты сдвигаются вправо на единицу, цикл будет продолжаться. Четвертая попытка заключается в том, гдеa[0]
делается0
, поэтому цикл никогда не вводится.В результате
?
печатается три раза.источник
:>
это орграф , а не триграф. Он не обрабатывается препроцессором, он просто распознается как эквивалент токена]
.?:
) имеет тип, который является общим типом второго и третьего членов. Первый член всегда является условным и имеет типbool
. Поскольку и второе, и третье слагаемые имеют тип,int
результатом троичной операции будетint
, а неunsigned long long
.#
и##
у орграфа формы; ничто не останавливает реализацию от перевода орграфиков к недиграфам на ранних этапах перевода