Стадии препроцессорной обработки
Препроцессорная обработка текста включает несколько стадий, выполняемых последовательно в следующем порядке: • все системно зависимые обозначения (например системно зависимый индикатор конца строки) перекодируются в стандартные коды; • каждая пара из символов ‘\’ и “конец строки” вместе с пробелами между ними убираются, и тем самым следующая строка исходного текста присоединяется к строке, в которой находилась эта пара символов; • в тексте каждой отдельной строки распознаются директивы и лексемы препроцессора, а каждый комментарий заменяется одним символом пустого промежутка; • выполняются директивы препроцессора и производятся макроподстановки; • эскейп-последовательности в символьных константах и символьных строках, например ‘\n’ или ‘\xF2’, заменяются на их эквиваленты (на соответствующие числовые коды); • смежные символьные строки (строковые константы) конкатенируются, т.е. соединяются в одну строку; • каждая препроцессорная лексема преобразуется в текст на языке Си. К препроцессорным лексемам или лексемам препроцессора (preprocessing token) относятся: символьные константы, имена включаемых файлов, идентификаторы, знаки операций, препроцессорные числа, знаки препинания, строковые константы (строки) и любые символы, отличные от пробела. Рассмотрим подробно стадию обработки директив препроцессора. При ее выполнении возможны следующие действия: • замена идентификаторов (обозначений) заранее подготовленными последовательностями символов; • включение в программу текстов из указанных файлов; • исключение из программы отдельных частей ее текста (условная компиляция); • макроподстановка, т.е. замена обозначения параметризованным текстом, формируемым препроцессором с учетом конкретных параметров. Перед символом ‘#’ и после него в директиве разрешены пробелы. Пробелы также разрешены перед лексемами препроцессора, между ними и после них. Окончанием препроцессорной директивы служит конец текстовой строки (при наличии символа ‘\’, обозначающего перенос строки, окончанием препроцессорной директивы будет признак конца следующей строки текста). Определены следующие препроцессорные директивы: #define – определение макроса или препроцессорного идентификатора; #include – включение текста из файла; #undef – отмена определения макроса или идентификатора (препроцессорного); #if – проверка условия-выражения; #ifdef – проверка определенности идентификатора; #ifndef – проверка неопределенности идентификатора; #else – начало альтернативной ветви для #if; #endif – окончание условной директивы #if; #elif – составная директива #else/#if; #line – смена номера следующей ниже строки; #еrrоr – формирование текста сообщения об ошибке трансляции; #pragma – действия, предусмотренные реализацией; # – пустая директива. Кроме препроцессорных директив имеются три препроцессорные операции, которые будут подробно рассмотрены вместе с командой #define: defined – проверка истинности операнда; ## – конкатенация препроцессорных лексем; # – преобразование операнда в строку символов. Директива #define имеет несколько модификаций. Они предусматривают определение макросов и препроцессорных идентификаторов, каждому из которых ставится в соответствие некоторая символьная последовательность. В последующем тексте программы препроцессорные идентификаторы заменяются заранее запланированными последовательностями символов. Директива #include позволяет включать в текст программы текст из указанного файла. Директива #undef отменяет действие директивы #define, которая определила до этого имя препроцессорного идентификатора. Директива #if и ее модификации #ifdef, #ifndef совместно с директивами #else, #endif, #elif позволяют организовать условную обработку текста программы. При использовании этих средств компилируется не весь текст, а только те его части, которые выделены с помощью перечисленных директив. Директива #line позволяет управлять нумерацией строк в файле с программой. Имя файла и желаемый начальный номер строки указываются непосредственно в директиве #line. Директива #error позволяет задать текст диагностического сообщения, которое выводится при возникновении ошибок. Директива #pragma вызывает действия, зависящие от реализации, т.е. запланированные авторами компилятора. Директива # ничего не вызывает, так как является пустой директивой, т.е. не дает никакого эффекта и всегда игнорируется. Рассмотрим возможности перечисленных директив и препроцессорных операций.
|