При чем тут С ??! ??!оператор делает? - PullRequest
1848 голосов
/ 19 октября 2011

Я видел строку C, которая выглядела так:

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

Она скомпилирована правильно и, кажется, работает нормально.Кажется, что он проверяет, произошла ли ошибка, и если она есть, то обрабатывает ее.Но я не совсем уверен, что он на самом деле делает или как он это делает.Похоже, что программист пытается выразить свои чувства по поводу ошибок.

Я никогда раньше не видел ??!??! ни на одном языке программирования, и нигде не могу найти документацию по нему.(Google не помогает с поисковыми терминами, такими как ??!??!).Что это делает и как работает пример кода?

Ответы [ 4 ]

1499 голосов
/ 19 октября 2011

??! - это триграф , который переводится как |.Таким образом, он говорит:

!ErrorHasOccured() || HandleError();

, что из-за короткого замыкания эквивалентно:

if (ErrorHasOccured())
    HandleError();

Гуру недели (имеет дело с C ++, но актуально здесь), где я поднял это.

Возможное происхождение триграфов или, как @DwB указывает в комментариях, это более вероятно из-за сложности EBCDIC (снова). Это обсуждение на платформе IBM developerWorks, похоже, подтверждает эту теорию.

Из ISO / IEC 9899: 1999 §5.2.1.1, сноска 12 (h / t @ Random832):

Последовательности триграфа позволяют вводить символы, которые не определены в наборе инвариантных кодов, как описано в ИСО / МЭК 646, который является подмножеством семибитного набора кодов США ASCII.

421 голосов
/ 20 октября 2011

Ну, почему это вообще существует, вероятно, отличается от того, почему оно существует в вашем примере.

Все началось полвека назад с перепрофилирования терминалов связи в качестве компьютерных пользовательских интерфейсов. В начальную эпоху Unix и C это был телетайп ASR-33.

Это устройство было медленным (10 cps), шумным и уродливым, и его представление набора символов ASCII закончилось на 0x5f, поэтому у него не было (посмотрите на рис.) Ни одной из клавиш:

{ | } ~ 

Триграфы были определены для решения конкретной проблемы. Идея заключалась в том, чтобы программы на C могли использовать подмножество ASCII, найденное на ASR-33 и в других средах, в которых отсутствуют высокие значения ASCII.

Ваш пример - это два из ??!, каждое из которых означает |, поэтому результат равен ||.

Однако люди, пишущие код на C почти по определению, имели современное оборудование, 1 , поэтому я думаю: кто-то хвастается или развлекается, оставляя в коде своеобразное пасхальное яйцо для вас найти.

Конечно, это сработало, это привело к невероятно популярному вопросу SO.

ASR-33 Teletype

Телетайп ASR-33


1. В связи с этим триграфы были изобретены комитетом ANSI, который впервые встретил после того, как C стал безудержным успехом, поэтому ни один из исходного кода C или кодировщиков не использовал бы их.
155 голосов
/ 19 октября 2011

Это триграф C .??! равно |, поэтому ??!??! это оператор ||

130 голосов
/ 25 марта 2016

Как уже говорилось, ??!??! - это, по сути, два триграфа (* ??! и ??! снова), собранные вместе и замененные на ||, то есть логическое ИЛИ , препроцессором.

Следующая таблица, содержащая каждый триграф, должна помочь устранить неоднозначность альтернативных комбинаций триграфов:

Trigraph   Replaces

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

Источник: C: Справочное руководство, 5-е издание

Таким образом, триграф, который выглядит как ??(??), в конечном итоге отобразится на [], ??(??)??(??) будет заменен на [][] и так далее, вы получите идею.

Поскольку триграфы заменяются во время предварительной обработки, вы можете использовать cpp, чтобы самостоятельно просмотреть вывод, используя глупую программу trigr.c:

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

и обрабатывая еес:

cpp -trigraphs trigr.c 

Вы получите консольный вывод

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

Как вы можете заметить, должна быть указана опция -trigraphs, иначе cpp выдаст предупреждение;это указывает на то, что триграфы остались в прошлом и не представляют никакой современной ценности, кроме как сбивать с толку людей, которые могут натолкнуться на них .


Что касается обоснования введения триграфов,это лучше понять при рассмотрении исторического раздела ИСО / МЭК 646 :

ИСО / МЭК 646 и его предшественника ASCII (ANSI X3.4) в значительной степени одобрили существующую практику в отношениикодировки символов в телекоммуникационной отрасли.

Поскольку в ASCII не предусмотрено количество символов, необходимых для языков, отличных от английского, был создан ряд национальных вариантов, в которых некоторые менее используемые символы заменены нанеобходимые .

(выделено мной)

Итак, по сути, некоторые необходимые символы (те, для которых существует триграф)были заменены в определенных национальных вариантах.Это приводит к альтернативному представлению с использованием триграфов, состоящих из символов, которые все еще были в других вариантах.

...