Co znaczy ??! ??! operator zrobić w C?


1989

Widziałem linię C, która wyglądała tak:

!ErrorHasOccured() ??!??! HandleError();

Kompiluje się poprawnie i wydaje się działać poprawnie. Wygląda na to, że sprawdza, czy wystąpił błąd, a jeśli tak, to go obsługuje. Ale nie jestem do końca pewien, co to właściwie robi i jak to robi. Wygląda na to, że programista próbuje wyrazić swoje uczucia na temat błędów.

Nigdy wcześniej nie widziałem ??!??!w żadnym języku programowania i nigdzie nie mogę znaleźć dokumentacji na ten temat. (Google nie pomaga w wyszukiwaniu takich haseł jak ??!??!). Co to robi i jak działa próbka kodu?


44
@PeterOlson, jak możesz !ErrorHasOccurred() ??!???! HandleError();się skompilować? To ??! ??? !. Udowadnia sens?
CVn

31
Sugeruję przeczytanie czystego kodu. ErrorHasOccured () należy przekształcić w ErrorHasNotOccured (), tym samym usuwając wykrzyknik ... kto ma czas na zrozumienie wszystkich tych operatorów ??!
KadekM

17
Raczej wolę ErrorHasOccured() && HandleError()siebie. Tak też robi Lua.
Hugo Zink,

76
@KadekM, przeniesienie negacji do nazwy funkcji nie tworzy czystego kodu, a wręcz przeciwnie.
marcelm

14
Uwaga dla każdego, kto znalazł się tutaj po walce na śmierć i życie dzięki swojej wyszukiwarce: SymbolHound może pomóc w wyszukiwaniu symbolicznym.
Jakob

Odpowiedzi:


1578

??!to trigraf, który tłumaczy |. Więc mówi:

!ErrorHasOccured() || HandleError();

co z powodu zwarcia odpowiada:

if (ErrorHasOccured())
    HandleError();

Guru tygodnia (zajmuje się C ++, ale dotyczy tutaj), gdzie to podniosłem.

Możliwe pochodzenie trigrafów lub, jak wskazuje @DwB w komentarzach, jest bardziej prawdopodobne, ponieważ EBCDIC jest trudny (ponownie). Ta dyskusja na forum programistów IBM wydaje się potwierdzać tę teorię.

Z ISO / IEC 9899: 1999 § 5.2.1.1, przypis 12 (h / t @ Random832):

Sekwencje trigraficzne umożliwiają wprowadzanie znaków, które nie są zdefiniowane w niezmiennym zestawie kodów, jak opisano w ISO / IEC 646, który jest podzbiorem siedmiobitowego zestawu kodów US ASCII.


376
Początkowo potrzebne były trygrysy na wypadek, gdyby na klawiaturze nie było np. „|” symbol. Tutaj albo programista celowo jest denerwujący, albo jakaś dziwna funkcja edytora
Martin Beckett,

35
Tak, to jest równoważne z if (ErrorHasOccured()) HandleError(). Na szczęście ten idiom zwykle występuje tylko w kodzie Perla.
user786653,

22
Niekoniecznie jest to EBCDIC - zestaw znaków wymagających kaligrafii prawie dokładnie odpowiada zestawowi znaków, które nie są niezmienne w ISO-646 (tj. Starych standardach „krajowych ascii”).
Random832,

52
Najlepiej czytelną alternatywą byłoby ErrorHasOccurred() && HandleError();, jeśli jesteś przyzwyczajony do wykonywania skryptów powłoki. :)
Yam Marcovic

18
Przeczytaj to jako „Albo ErrorHasOcurred, albo musisz HandleError”, @SparkyRobinson.
Omar Antolín-Camarena

453

Cóż, dlaczego to w ogóle istnieje, prawdopodobnie jest inne niż dlaczego w twoim przykładzie.

Wszystko zaczęło się pół wieku temu od zmiany przeznaczenia terminali komunikacyjnych w formie papierowych interfejsów użytkownika. W początkowej erze Uniksa i C był to Teletype ASR-33.

To urządzenie było powolne (10 cps), głośne i brzydkie, a jego widok zestawu znaków ASCII zakończył się na 0x5f, więc nie miał (spójrz na obrazek) żadnego z klawiszy:

{ | } ~ 

Trygrafy zostały zdefiniowane, aby rozwiązać konkretny problem. Pomysł polegał na tym, że programy C mogą korzystać z podzbioru ASCII znalezionego na ASR-33, aw innych środowiskach, w których brakuje wysokich wartości ASCII.

Twój przykład to właściwie dwa ??!, każde znaczenie |, więc wynik jest ||.

Jednak ludzie piszący kod C niemal z definicji miała nowoczesny sprzęt, 1 więc moje przypuszczenie jest: ktoś popisywać lub bawi samym sobą, pozostawiając rodzaju Pisanka w kodzie, aby znaleźć.

Z pewnością zadziałało, doprowadziło to do bardzo popularnego pytania SO.

ASR-33 Teletype

                                            ASR-33 Teletype


1. W tym przypadku trygrafy zostały wymyślone przez komitet ANSI, który spotkał się po tym, jak C stał się niekwestionowanym sukcesem, więc żaden z oryginalnych kodów C ani koderów nie użyłby ich.


18
To nie jedyny przypadek braku znaków w klawiaturze i zestawie znaków. Commodore 64 jest prawdopodobnie bardziej znany wielu ludziom po trzydziestce i wyżej - w wyświetlanych zestawach znaków brakowało nawiasów klamrowych (i prawdopodobnie również paska i tylda) - w tym przypadku, ponieważ „ASCII” nie był ASCII . W ECMA-6 (prawie zawsze nazywany ASCII, ale nie US-ASCII) istniało 18 kodów specyficznych dla regionu, ale nie wiem, które to kody. Jedno mogę powiedzieć na pewno - w brytyjskim „ASCII” #zostało zastąpione £. W innych regionach być może „ASCII” nie miał
aparatów

7
W podobnym zestawie znaków ATASCII dla komputerów 8-bitowych Atari również brakowało {} oraz ~ i `.
dan04,

42
Zobacz te dwa artykuły Wikipedii. Jestem prawie na tyle duży, że wciąż pamiętam epokę 7-bitowych zestawów znaków narodowych (chociaż jestem pewien, że wciąż trwają w ciemnych, nietkniętych zakątkach), a książka, z której po raz pierwszy nauczyłem się C, uznała za konieczne ostrzeżenie o możliwość if (x || y) { a[i] = '\0'; }wyglądania jak if (x öö y) ä aÄiÅ = 'Ö0'; åw złym zestawie znaków.
Ilmari Karonen,

9
Inną interesującą historyczną notatką jest to, że Unix (który był dużą platformą, na której jechał C) mógł być pierwszym systemem o dowolnym znaczeniu (i być może pierwszym ogólnym) do domyślnych wartości alfabetycznych pisanych małymi, a nie dużymi literami. Chociaż nie widziałem wielu współczesnych systemów, myślę, że był to prawdziwy znak wyrafinowania. Poza tym, że jest naprawdę jedynym przyzwoitym systemem operacyjnym, Unix przekształcił również wielkie litery na małe, a nie odwrotnie. Ci faceci byli naprawdę fajni.
DigitalRoss,

16
Zabawna historia. Muszę ci powiedzieć ... Kompilator XL Fortran stacji roboczej IBM RS / 6000 został opracowany z kompilatora XL C. W kilku pierwszych wydaniach przypadkowo pozostawili się w trakcie przetwarzania trigrafii, więc istniały pewne prawidłowe sekwencje znaków Fortrana (w dosłownym ciągu, IIRC), które zostały błędnie zinterpretowane jako litery C, prowadząc do kilku interesujących błędów!
Phil Perry

166

To jest C- kaligraf . ??!jest |, podobnie ??!??!jak operator||


5
trygrafy pochodzą z okresu, w którym niektóre klawisze nie miały wszystkich klawiszy, które mają teraz. Pomaga również, gdy jakiś edytor tekstu zarezerwował znaki specjalne na specjalne rzeczy. To głównie relikt przeszłości i quiz;)
Joel Falcou

5
Ponieważ niektóre klawiatury najwyraźniej nie mają „|” więc niektórzy ludzie nie mają innego wyjścia, jak kilkakrotne uderzanie w klawiaturę, aż pojawi się trójgranica, która daje im potrzebne symbole.
Sowa

A potem jest <iso646.h>plik nagłówka.
David R Tribble,

149

Jak już wspomniano, ??!??!są to zasadniczo dwa trygrafy ( ??!i ??!ponownie) wymieszane razem, które są zastępowane-tłumaczone ||, tj. Logiczne OR , przez preprocesora.

Poniższa tabela zawierająca wszystkie trygrafy powinna pomóc w jednoznacznym odróżnieniu alternatywnych kombinacji trygrafów:

Trigraph   Replaces

??(        [
??)        ]
??<        {
??>        }
??/        \
??'        ^
??=        #
??!        |
??-        ~

Źródło: C: A Reference Manual wydanie 5

Tak więc trójwymiarowy wygląd, który wygląda tak ??(??), ostatecznie zostanie zamapowany [], ??(??)??(??)zostanie zastąpiony przez [][]i tak dalej, masz pomysł.

Ponieważ podczas wstępnego przetwarzania trigry są zastępowane, możesz użyć tego, cppaby samemu zobaczyć wynik, używając głupiego trigr.cprogramu:

void main(){ const char *s = "??!??!"; } 

i przetwarzając go za pomocą:

cpp -trigraphs trigr.c 

Otrzymasz wyjście konsoli

void main(){ const char *s = "||"; }

Jak można zauważyć, -trigraphsnależy określić opcję, w przeciwnym razie cpppojawi się ostrzeżenie; wskazuje to na to, że trygrafy należą do przeszłości i nie mają żadnej nowoczesnej wartości poza mylącymi ludźmi, którzy mogą się na nie natknąć .


Jeśli chodzi o uzasadnienie wprowadzenia kaligrafii, lepiej to zrozumieć, patrząc na sekcję historii ISO / IEC 646 :

ISO / IEC 646 i jego poprzednik ASCII (ANSI X3.4) w dużej mierze poparły istniejącą praktykę dotyczącą kodowania znaków w branży telekomunikacyjnej.

Ponieważ ASCII nie podał liczby znaków potrzebnych w językach innych niż angielski, opracowano wiele wariantów krajowych, które zastąpiły niektóre rzadziej używane znaki potrzebnymi .

(moje podkreślenie)

Zasadniczo niektóre potrzebne postacie (te, dla których istnieje trigraf) zostały zastąpione w niektórych wariantach krajowych. Prowadzi to do alternatywnej reprezentacji za pomocą trójwymiarowych znaków składających się z postaci, które inne warianty nadal miały w pobliżu.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.