Biorąc pod uwagę kolegę jako zagadkę, nie mogę zrozumieć, jak ten program C faktycznie się kompiluje i działa. Co to jest ten >>>=
operator i dziwny 1P1
literał? Testowałem w Clang i GCC. Nie ma ostrzeżeń, a wynikiem jest „???”
#include <stdio.h>
int main()
{
int a[2]={ 10, 1 };
while( a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ] )
printf("?");
return 0;
}
0x.1P1
to literał szesnastkowy z wykładnikiem wykładniczym. Jest0x.1
to część liczbowa, czyli 1/16 tutaj. Liczba po „P” jest potęgą dwóch, przez którą liczba jest mnożona. Tak0x.1p1
naprawdę jest 1/16 * 2 lub 1/8. A jeśli zastanawiasz się nad0xFULL
tym, to jest po prostu0xF
iULL
jest to przyrostek dlaunsigned long long
Odpowiedzi:
Linia:
zawiera digrafy
:>
i<:
, które tłumaczą odpowiednio na]
i[
, więc jest to równoważne z:Dosłowność
0xFULL
jest taka sama jak0xF
(dla której jest hex15
); poULL
prostu określa, że jest tounsigned long long
dosłowne . W każdym razie, jako wartość logiczna jest to prawda, więc0xFULL ? '\0' : -1
ocenia na'\0'
, czyli dosłowny znak, którego wartość liczbowa jest po prostu0
.Tymczasem
0X.1P1
jest szesnastkowym literałem zmiennoprzecinkowym równym 2/16 = 0,125. W każdym razie, ponieważ jest niezerowa, jest również prawdziwa jako wartość logiczna, więc podwójna negacja!!
powoduje ponownie1
. W ten sposób całość upraszcza się do:Operator
>>=
jest złożonym przypisaniem, które przesuwa bit w lewo swojego operandu w prawo o liczbę bitów podaną przez prawy operand i zwraca wynik. W takim przypadku właściwy operanda[1]
ma zawsze wartość1
, więc jest równoważny z:lub równoważnie:
Początkowa wartość
a[0]
wynosi 10. Po jednokrotnym przesunięciu w prawo staje się 5, następnie (zaokrąglając w dół) 2, następnie 1 i na końcu 0, w którym momencie pętla się kończy. W ten sposób ciało pętli zostanie wykonane trzy razy.źródło
P
IN0X.1P1
.e
w10e5
, z tym wyjątkiem, że musisz używaćp
literałów szesnastkowych, ponieważe
jest to cyfra szesnastkowa.p
oddziela mantysę i wykładnik, tak jake
w normalnym naukowym zapisie zmiennoprzecinkowym; jedna różnica polega na tym, że przy0x0.1p1
liczbach zmiennoprzecinkowych podstawa części wykładniczej wynosi 2 zamiast 10, więc równa się 0x0.1 = 1/16 razy 2¹ = 2. (W każdym razie nic z tego nie ma znaczenia; żadne niezerowe wartość działałaby tam równie dobrze.)char
dosłowny” i dodałem link do Wikipedii. Dzięki!Jest raczej pewne niejasne kod udziałem digrafy , mianowicie
<:
a:>
które są alternatywne dla tokeny[
i]
odpowiednio. Istnieje również pewne zastosowanie operatora warunkowego . Istnieje również nieco zmieniony operator , właściwe przypisanie zmiany>>=
.To jest bardziej czytelna wersja:
i jeszcze bardziej czytelną wersję, zastępując wyrażenia w
[]
wartościach, które postanawiają:Wymiana
a[0]
ia[1]
dla ich wartości powinny ułatwić dowiedzieć się, co robi pętlę, czyli równowartość:który po prostu wykonuje dzielenie (całkowite) przez 2 w każdej iteracji, tworząc sekwencję
5, 2, 1
.źródło
????
rezultatu,???
jak PO? (Huh.) Codepad.org/nDkxGUNi nie produkuje???
.Przejdźmy do wyrażenia od lewej do prawej:
Pierwszą rzeczą, którą zauważam, jest to, że korzystamy z operatora trójskładnikowego z użycia
?
. Podwyrażenie:mówi „jeśli
0xFULL
jest niezerowe, zwróć'\0'
, w przeciwnym razie-1
.0xFULL
jest literą szesnastkową z niepodpisanym długim sufiksem - co oznacza, że jest literą szesnastkową typuunsigned long long
. To nie ma znaczenia, ponieważ0xF
może mieścić się w regularnej liczbie całkowitej.Ponadto operator trójskładnikowy przekształca typy drugiego i trzeciego wyrażenia na ich wspólny typ.
'\0'
jest następnie konwertowany naint
, co jest sprawiedliwe0
.Wartość
0xF
jest znacznie większa od zera, więc mija. Wyrażenie staje się teraz:Dalej
:>
jest digraf . Jest to konstrukcja, która rozwija się do]
:>>=
jest podpisanym operatorem prawej zmiany, możemya
to zrobić, aby było wyraźniej.Co więcej,
<:
jest to digraf, który rozwija się do[
:0X.1P1
jest literałem szesnastkowym z wykładnikiem wykładniczym. Ale bez względu na wartość,!!
wszystko, co nie jest zerem, jest prawdą.0X.1P1
jest0.125
niezerowe, więc staje się:Jest
>>=
to podpisany operator prawej zmiany. Zmienia wartość lewego operandu, przesuwając bity do przodu o wartość po prawej stronie operatora.10
w formacie binarnym jest1010
. Oto kroki:>>=
zwraca wynik działania, więc dopóki przesunięciea[0]
pozostaje niezerowe za każdym razem, gdy jego bity są przesunięte o jeden w prawo, pętla będzie kontynuowana. Czwarta próba dotyczy miejsca, w któryma[0]
się znajduje0
, więc pętla nigdy nie jest wprowadzana.W rezultacie
?
jest drukowany trzy razy.źródło
:>
to digraf , a nie trigraf. Nie jest obsługiwany przez preprocesor, jest po prostu rozpoznawany jako token równoważny]
.?:
) ma typ, który jest typowym typem drugiego i trzeciego wyrażenia. Pierwszy termin jest zawsze warunkowy i ma swój typbool
. Ponieważ zarówno drugi i trzeci mają terminy wpiszint
wynik potrójnego działania będzieint
, nieunsigned long long
.#
i##
mieć digraf formy; nic nie powstrzymuje implementacji od tłumaczenia digraphów na non-digraphs we wczesnych fazach tłumaczenia