Диграф и триграф не могут работать вместе?
Я изучаю орграф и триграф, и вот код, который я не могу понять. (Да, я признаю, что это очень некрасиво.)
Этот код может скомпилировать:
#define _(s) s%:%:s
main(_(_))
<%
__;
%>t
Этот код также может компилироваться:
#define _(s) s??=??=s
main(_(_))
<%
__;
%>
Однако ни один из следующих двух фрагментов кода не может быть скомпилирован:
#define _(s) s%:??=s
main(_(_))
<%
__;
%>
А также
#define _(s) s??=%:s
main(_(_))
<%
__;
%>
Это меня смущает: так как первые два фрагмента кода могут компилироваться, я предполагаю, что расширение digraph и trigraph оба имеют место перед расширением макроса. Так почему же он не может скомпилироваться, когда орграф и триграф используются вместе?
2 ответа
Диграфы и триграфы совершенно разные. Триграфы заменяются во время фазы 1 перевода, [см. Примечание 1], то есть до разделения исходного кода на токены. Органы - это токены, которые являются альтернативным написанием для других токенов, поэтому они не имеют смысла до тех пор, пока источник не будет разделен на токены. (Слово "орграф" не очень точное; оно используется, потому что оно напоминает "триграф", но набор орграфов включает в себя %:%:
который состоит из четырех символов.)
Так ??=
заменяется на #
перед анализом любого токена. Но %:
это просто знак, имеющий то же значение, что и #
,
Более того, %:%:
это токен с тем же значением, что и ##
, Но %:#
это два жетона (%:
а также #
), что недопустимо, так как оператор stringify (будь то пишется %:
или же #
) может сопровождаться только параметром макроса. [См. Примечание 2] И оно не станет менее незаконным, если #
были результатом замены триграфа.
Одно важное различие между орграфами и триграфами, о чем свидетельствует веселый фрагмент в ответе chqrlie, состоит в том, что триграфы также работают в строках. Диграфы позволяют вам писать код на C, даже если на клавиатуре отсутствуют скобки и октоторпи, но они не помогают печатать эти символы.
Примечания (Стандартные цитаты):
§5.1.1.2, Этапы перевода, пункт 1:
Приоритет среди синтаксических правил перевода определяется следующими этапами.
- Многобайтовые символы физического исходного файла отображаются, в зависимости от реализации, в исходный набор символов (ввод символов новой строки для индикаторов конца строки), если это необходимо. Триграфные последовательности заменяются соответствующими односимвольными внутренними представлениями.
§6.10.3.2, Оператор #, параграф 1:
За каждым # токеном предварительной обработки в списке замены для функционально-подобного макроса должен следовать параметр в качестве следующего токена предварительной обработки в списке замены.
Для академической стороны, посмотрите на хорошо документированный ответ Ричи.
Что касается здравого смысла, если вы уже недостаточно хорошо владеете C, орграфы и триграфы совершенно бесполезны, и вам даже не следует тратить время на эту тему. Они были изобретены как способ поддержки не-американских 7-битных наборов символов, которые все еще использовались в 1980-х годах на мэйнфреймах и некоторых миникомпьютерах. В этих наборах символов отсутствовали некоторые знаки препинания, необходимые для языка Си, такие как #
, {
, }
и т.п.
Даже в тех системах, которые я использовал некоторое время, триграфы никогда не использовались, потому что существовали безобразные прагматические альтернативы: во французских системах такие буквы с акцентом, как é
а также è
были напечатаны, но будет интерпретироваться компилятором C как {
а также }
, Это сделало программирование на C неясным и подтолкнуло многих программистов переключиться на американскую QWERTY-клавиатуру и Locale (или эквивалент).
Это в прошлом, только исторический интерес, и вы никогда не увидите их в действии, кроме опечаток, запутывания и неприятных вопросов для интервью.
Что касается последнего, я не могу удержаться от публикации этого:
Я не могу получить
fnmatch
чтобы проверить мой шаблон даты, даже если я ввожу правильную дату, что не так с этим кодом:#include <stdio.h> #include <fnmatch.h> int main() { char date[16] = "01/01/1988"; if (fnmatch("??/??/????", datebuf, 0)) printf("invalid date format\n"); return 0; }