1.2 Первичная обработка ¶
Препроцессор выполняет серию текстовых преобразований входного файла. Эти преобразования выполняются до любой другой обработки. Понятийно, они выполняются в жёсткой последовательности, и весь файл проходит через каждое преобразование перед началом следующего. CPP фактически выполняет все преобразования одновременно по причинам производительности. Эти преобразования примерно соответствуют трём первым «фазам трансляции», описанным в стандарте C.
- Входной файл считывается в память и разбивается на строки.
Разные системы используют разные соглашения для обозначения конца строки. GCC принимает ASCII-управляющие последовательности LF, CR LF и CR в качестве маркеров конца строки. Это канонические последовательности, используемые Unix, DOS и VMS, и классической Mac OS (до OSX), соответственно. Поэтому вы можете без проблем скопировать исходный код, написанный в любой из этих систем, в другую и использовать его без преобразования. (GCC может потерять отслеживание текущей строки, если файл не использует последовательно одно соглашение, как иногда бывает, когда он редактируется на компьютерах с разными соглашениями, которые используют общую файловую систему.)
Если последняя строка любого входного файла не содержит маркера конца строки, конец файла неявно считается таковым. Стандарт C говорит, что это условие вызывает неопределённое поведение, поэтому GCC выведет сообщение об ошибке.
- Если триграфы включены, они заменяются соответствующими одиночными символами. По умолчанию GCC игнорирует триграфы, но если вы запросите строгое соответствие с параметром -std или укажете опцию -trigraphs, то он их преобразует.
Это девять трёхсимвольных последовательностей, все начинающиеся с ‘??’, которые определены в ISO C как представляющие одиночные символы. Они позволяют использовать C на устаревших системах, которым не хватает некоторых знаков препинания C. Например, ‘??/’ обозначает ‘\’, поэтому
'??/n'— это символ-константа для новой строки.Триграфы не популярны, и многие компиляторы реализуют их неправильно. Переносимый код не должен полагаться на преобразование или игнорирование триграфов. С параметром -Wtrigraphs GCC предупредит вас, когда триграф может изменить смысл вашей программы, если он будет преобразован. Смотрите Wtrigraphs.
В строковой константе вы можете предотвратить конфликт последовательности вопросительных знаков с триграфом, вставив обратный слэш между вопросительными знаками или разделив строковый литерал на триграф и используя конкатенацию строковых литералов.
"(??\?)"— это строка ‘(???)’, а не ‘(?]’. Традиционные компиляторы C не распознают эти конструкции.Девять триграфов и их замены:
Trigraph: ??( ??) ??< ??> ??= ??/ ??' ??! ??- Replacement: [ ] { } # \ ^ | ~ - Объединённые строки объединяются в одну длинную строку.
Продолжаемая строка — это строка, которая заканчивается обратным слэшем, ‘\’. Обратный слэш удаляется, и следующая строка присоединяется к текущей. Пробелы не вставляются, поэтому вы можете разбить строку в любом месте, даже посередине слова. (Обычно более читаемо разбивать строки только на пробелы.)
Обратный слэш в конце строки продолжительной строки обычно называется обратным слэшем-новой строки.
Если между обратным слэшем и концом строки есть пробел, это всё равно продолжение строки. Однако, поскольку это обычно результат ошибки редактирования, и многие компиляторы не примут это как продолжение строки, GCC выведет предупреждение об этом.
- Все комментарии заменяются на один пробел.
Существует два вида комментариев. Блочные комментарии начинаются с ‘/*’ и продолжаются до следующего ‘*/’. Блочные комментарии не вложены:
/* this is /* one comment */ text outside comment
Строчные комментарии начинаются с ‘//’ и продолжаются до конца текущей строки. Строчные комментарии тоже не вложены, но это не имеет значения, потому что они заканчиваются в одном и том же месте.
// this is // one comment text outside comment
Можно безопасно размещать строчные комментарии внутри блочных комментариев или наоборот.
/* block comment // contains line comment yet more comment */ outside comment // line comment /* contains block comment */
Но будьте осторожны, комментируя один конец блочного комментария строчным комментарием.
// l.c. /* block comment begins oops! this isn’t a comment anymore */
Комментарии не распознаются внутри строковых литералов. "/* blah */" — это строковая константа ‘/* blah */’, а не пустая строка.
Строчные комментарии отсутствуют в издании стандарта C 1989, но GCC их распознаёт как расширение. В C++ и в издании стандарта C 1999 они являются официальной частью языка.
Поскольку эти преобразования выполняются до любой другой обработки, вы можете механически разбить строку с обратным слэшем-новой строки в любом месте. Вы можете закомментировать конец строки. Вы можете продолжить строчный комментарий на следующую строку с обратным слэшем-новой строки. Вы даже можете разбить ‘/*’, ‘*/’ и ‘//’ на несколько строк с обратным слэшем-новой строки. Например:
/\ * */ # /* */ defi\ ne FO\ O 10\ 20
эквивалентно #define FOO 1020. Все эти приёмы чрезвычайно запутанные и не должны использоваться в коде, предназначенном для чтения.
Нет способа предотвратить интерпретацию обратного слэша в конце строки как обратного слэша-новой строки. Однако это не может повлиять ни на одну корректную программу.
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-10.5.0/cpp/Initial-processing.html