The addition of trigraphs has made it easier to make programs hard to read. It was a dark and stormy night for C when the ANSI C committee added trigraphs to the standard.
Это фраза из старых правил конкурса Obfuscated C, конкурса на самый запутанный код на языке C.
Вообще триграфы были придуманы для терминалов, в которых некоторых символов не хватает. В итоге вместо
#define
можно написать ??=define
. Триграфы подменяются на нужные символы в самом начале, поэтому эти записи эквивалентны. Вместо {
можно написать ??<
, вместо }
использовать ??>
. Триграф | Что заменяет |
??= | # |
??( | [ |
??/ | \ |
??) | ] |
??’ | ^ |
??< | { |
??! | | |
??> | } |
??- | ~ |
Кроме триграфов есть еще альтернативные символы. Например вместо тильды
~
можно использовать compl
, решетку #
можно заменить на %:
.Альтернативные символы, которые не являются словами, известны как диграфы. Даже несмотря на то, что есть диграф, состоящий из четырех символов.
Диграф | Что заменяет |
<% | { |
%> | } |
<: | [ |
:> | ] |
%: | # |
%:%: | ## |
С триграфами связан один неприятный момент: можно опечататься и получить весьма странное поведение программы. Классический пример опечатки, которая превращается в тригаф, приведен у Герба Саттера. GotW #86: Slight Typos? Graphic Language and Other Curiosities
// What will the next line do? Increment???????????/
++x;
Символы '?' и '/' находятся на одной кнопке и опечататься так несложно. Но последовательность ??/ - это триграф, который заменяется на '\', что означает конкатенацию строк. Код превращается в
// What will the next line do? Increment?????????++x;
В gcc неприятностей от триграфов меньше, потому что там надо указать опцию -trigraphs для обработки триграфов. Кстати, в мануале к одной из старых версий gcc об опции -trigraphs сказано "You don't want to know about this brain-damage". :-)
А вот в Visual С++ никаких опций для триграфов указывать не надо, хотя для альтернативных символов - надо...
Триграфы будут заменены везде в коде, например внутри текстовых строк. И вот такой код
printf( "??-I wonder!\n" );
Выдаст на печать
~I wonder!
Так как триграф
??-
будет заменен на тильду. Для получения желаемого вывода придется использовать обратные слэши.
printf( "\?\?-I wonder!\n" );
Для подавляющего большинства программистов, пользы от триграфов - никакой, одни неприятности... Хотя, можно изобрести какой-нибудь хоррор-код, а потом приставать ко всем с глупым вопросом: "Почему данный С++ код корректен и что он делает?".
??<z=compl(compl x??!y);%>
На конкурсе Obfuscated C в 90-м году победил как раз код, активно использующий триграфы. Но сейчас на этом конкурсе тригафы использовать не рекомендуется.
Изуродовать код до неузнаваемости можно не только с помощью триграфов и диграфов. Если лгать в комментариях, придумывать дурацкие названия переменным и в усмерть все задефайнить, то код будет очень сложно читать и поддерживать. О чем очень убедительно рассказывается на сайте Unmaintainable code, причем не только про C/С++, там есть правила, применимые к любым языкам программирования, есть правила по отдельным языкам. За примерами совершенно невообразимого кода лучше сходить на сайт упомянутого уже конкурса Obfuscated C или на страничку The Daily WTF, где ежедневно публикуются реальные примеры кода, вызывающие ужас и отвращение.
Ссылки по теме:
comp.lang.c++.moderated printf() question
Obfuscated C
The Daily WTF
8 коммент.:
С g++ ошибиться стало немного сложнее, сейчас при обнаружении триграфов он выводит (в примере триграф "??=" ):
warning: trigraph ??= ignored, use -trigraphs to enable
Небольшое исследование
Все ключи по умолчанию
1. MinGW gcc (версия 3.4.2) выдает warning при обнаружении триграфов и игнорирует их.
2. MinGW gcc-2 (версия 2.95.3-8) warning не выводит и также игнорирует их.
3. Microsoft С++ Toolkit 2003 (версия 13.10.3052) компилит и выполняет триграфы по стандарту.
Выводит warning предупреждая что однострочный коментарий переходит на следущую строку.
4. OpenWatcom 1.3 работает также как и микрософтский.
5. Borland C++ (версия 5.5.1) ничего не знает о триграфах.
6. Digital Mars Compiler версии 8.42n ничего не знает о триграфах.
7. Lcc версии 3.8 работает
аналогично микрософтскому.
8. Ch интерпретатор версии 5.0.0 игнорирует триграф в коментарии (коммент остается однострочным) и выполняет в символьной строке.
Не знал об этом. Спасибо.
Небольшая, но неприятная для не очень внимательных новичков, ачипятка:
...
??/
...
Очевидно, имелось в виду:
...
??/ \
...
Небольшая, но неприятная для не очень внимательных новичков, ачипятка:
Спасибо, поправила.
про *графы впервые узнал здесь :) опыт работы с С++, правда, совсем небольшой, но удивляюсь, как я на эти грабли не натыкался? Такое количество разнообразных три/диграфов...
Предлагаю не очень изящный, но всё же пример запутывания - надеюсь, предостережёт кого-нибудь от попыток коряво "сделать красивое форматирование" :)
Именно у Саттера об этом узнал впервые. C++ - язык с юмором :)
Отправить комментарий