Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #103, ИЮЛЬ 2007 г.

Трюки от Крыса

Крис Касперски

Хакер, номер #103, стр. 103-120-1

Сегодняшний выпуск трюков посвящен двум любопытным, но малоизвестным возможностям языка Си — «триграфам» (trigraph) и «диграфам» (digraph), в основном встречающимся в соревнованиях по непонятному программированию, однако в некоторых (достаточно редких) случаях разваливающим программу, написанную без учета их существования.

Совет 1: триграфы: жизнь без скобок

Ди- и триграфы широко используются в натуральных языках для обозначения «чужеродных» символов, отсутствующих в «своем» алфавите. Последовательность из двух (реже трех) «своих» символов кодирует один «чужой». Просто, как и все гениальное! Например, хангыль (корейское фонематическое письмо) состоит из блоков типа чамо, кодирующих отдельные слоги или даже целые слова. Всего существует 51 чамо, 24 из которых эквивалентны буквам обычного алфавита, а оставшиеся 27 представляют собой комбинации из двух или трех букв (то есть диграфы и триграфы соответственно).

Язык Си использует 9 символов, не входящих в наборы ISO 646 и EBCDIC, до сих пор используемые в некоторых терминалах. В результате квадратные и фигурные скобки невозможно ни набрать с клавиатуры, ни отобразить на экране такого терминала, а потому, начиная с самых первых редакций Стандарта, в язык ввели поддержку триграфов, скрестив символы базового набора ISO 646 с двумя знаками вопроса (смотри таблицу 1).

Программа, написанная с использованием триграфов, может выглядеть, например, так:

Исходный текст программы, написанной с использованием триграфов

??=include <stdio.h> /* # */

int main(void)

??< /* { */

char n??(5??); /* [ и ] */

n??(4??) = '0' - (??-0 ??' 1 ??! 2); /* ~, ^ и | */

printf("%c??/n", n??(4??)); /* ??/ = \ */

printf("??=??=??="); /* ### */

??>

Попробуй подсунуть эту абракадабру коллегам и спроси, что она делает. Кстати, обрати внимание на предпоследнюю строчку, здесь тригафы используются внутри строковых констант и, вместо ожидаемых «??=??=??=», функция printf напечатает три символа решетки! А ведь, не зная о существовании триграфов, о такую комбинацию можно спотыкнуться чисто случайно, долго ломая голову, почему программа работает не так, как это задумывалось.

Триграфы поддерживают практически все современные компиляторы, однако если Microsoft Visual C++ задействует триграфы по умолчанию, то Borland C++ для увеличения скорости трансляции использует внешний препроцессор, реализованный в файле trigraph.exe, входящий в штатный комплект поставки компилятора и вызываемый программистом самостоятельно. GCC поддерживает триграфы, но по умолчанию не обрабатывает их внутри строковых констант и делает это только при явном указании ключа ‘-trigraphs’.

Подробнее о триграфах можно почитать в Стандарте на Си, в любом хорошем учебнике или Википедии — http://en.wikipedia.org/wiki/C_trigraph.

Совет 2: диграфы: элегантный мир

Недостаточная выразительность и отвратительная читабельность триграфов привели к тому, что в последней редакции Стандарта ANSI C99 появилась достойная альтернатива в виде диграфов. Они используют всего два символа вместо трех, комбинируя угловые скобки со знаком процента или двоеточия, интерпретируемых как квадратные и фигурные скобки соответственно. Согласитесь, что так нагляднее (причем намного) и гораздо естественнее!

Содержание  Вперед на стр. 103-120-2