Препроцессор языка Си (А.А. Вылиток - Лекции)
Описание файла
Файл "Препроцессор языка Си" внутри архива находится в папке "А.А. Вылиток - Лекции". PDF-файл из архива "А.А. Вылиток - Лекции", который расположен в категории "". Всё это находится в предмете "операционные системы" из 3 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст из PDF
А.А. ВылитокПрепроцессор языка СиПрепроцессор языка Си – это обработчик макросов, аналог макрогенератора вмакроассемблере. Он просматривает текст программы, написанной на Си, до того, как онабудет обработана компилятором. Текст хранится в символьном файле, разбитом на строкисимволами-маркерами «конец строки».В некоторых системах препроцессор может быть совмещен с процессомкомпиляции, в других может быть отдельной программой, результатом которой являетсяфайл без макросов на «чистом» Си.Управляется препроцессор директивами. Директивой является строка,начинающаяся символом #.
До и после # могут быть пробелы. За символом # должноследовать имя директивы.Строки препроцессора распознаются до генерации макрорасширений. Поэтому,если макрос сгенерирует новую директиву препроцессора, она не будет выполнена.Если в строке исходного файла содержится символ \ непосредственно передмаркером конца строки, то этот символ выбрасывается вместе с маркером и две соседниестроки склеиваются в одну строку.
Это происходит до обработки директив препроцессора.Лексемами для препроцессора являются все лексемы языка Си и, кроме того,последовательности символов, задающие имена файлов, например, в директиве #include.Макроопределения без параметровСтрока вида# define имя последовательность_лексемопределяет макрос имя с телом последовательность_лексем (последовательность можетбыть пустой). Тело макроса начинается сразу за его именем (никаких знаков равенства ит.п.
перед телом нет). Когда в тексте программы встречается имя макроса, оно заменяетсяего телом (макроподстановка).Примеры#define#define#define#define#defineEOF -1EOT ’\004’BUF_SIZE 1024ER_MESSAGE ”>>error %d:%s\n”forever for(;;) /* бесконечный цикл */#define NUMBER_OF_FILES = 7 /* опасно! но лексически корректно,телом будет =7*/При таком определении, фрагмент while(n!=NUMBER_OF_FILES)преобразован к ошибочному фрагменту while (n!==7).будетМакроопределения с параметрамиСтрока вида# define имя( список_идентификаторов ) последовательность_лексем ,где между именем и открывающей скобкой нет ни одного пробельного символа, являетсямакроопределением с параметрами, задаваемыми списком идентификаторов (списокможет быть пустым). Параметры в списке перечисляются через запятую. Числофактических параметров при вызове макроса должно совпадать с числом формальныхпараметров.
При макрорасширении каждое вхождение формального параметра в теломакроса заменяется на соответствующий фактический параметр.Примеры[1]#define max(X,Y)((X) > (Y) ? (X) : (Y))Фрагмент a=max(b+c, f(d,e)) будет заменен наa=((b+c) > (f(d,e)) ? (b+c) : (f(d,e)))Вызов макроса похож на вызов функции, причем он подходит для аргументовлюбого типа, так что не нужно писать разные функции max для данных разных типов.Однако в отличие от вызова функции, одно из выражений, задающих фактическиепараметры в данном примере, вычисляется дважды, что может привести к неожиданнымпоследствиям в случае использования побочных эффектов в этих выражениях:max( x++, y++);a=(( x++) > (y++) ? (x++) : (y++)); — одиниз аргументов будет увеличен на 1, другой на 2.[2]Формальные параметры, вместо которых будут подставляться выражения, в телемакроса следует заключать в скобки, чтобы обеспечить нужный порядок вычислений.Вот пример потенциально ошибочного определения квадрата числа:#define square(x) x*x/* может быть ошибка при использовании */Длявызова square(y+1) получим расширение y+1*y+1, не соответствующееквадрату аргумента.[3]Макрос getchar имеет пустой список параметров:#define getchar() getc(stdin)При вызове этого макроса используется пустой список аргументов:while (( c=getchar() != EOF) …[4]Определение функциональных макросов для использования их в качествеоператоров-выражений, может приводить к ошибкам.
Например:#define swap(a,b) {unsigned long _temp=a; a=b; b=_temp;}Попробуем использовать данный макрос в следующем контексте:if (x>y) swap(x,y); /*ошибка*/else x=y;Макрорасширение будет содержать лишнюю точку с запятой перед else:if (x>y) {unsigned long _temp=x; x=y; y=_temp;}; /*ошибка*/else x=y;В данной ситуации можно использовать цикл do-while c единственной итерацией.#define swap(a,b) do { unsigned long _temp=a; a=b; b=_temp;} \while(0)Так как синтаксис цикла do-while требует точку с запятой после выражения, в результатемакроподстановки получим безошибочный фрагмент:if (x>y) do {unsigned long _temp=x; x=y; y=_temp;} while(0);else x=y;[5]В результате макроподстановки может появиться новый вызов макроса, который всвою очередь заменяется макрорасширением.
Рассмотрим пример:#define sum(x,y) add(y,x)#define add(x,y) ((x)+(y))Вызов sum(sum(a,b),c) приведет к следующей последовательностимакрорасширений:sum(sum(a,b),c) add(c, sum(a,b)) ((c)+(sum(a,b)) ((c)+(add(b,a)) ((c)+(((b)+(a))))[6]С помощью макросов можно переопределять функции. Следующий макроспереопределяет извлечение квадратного корня с целью особой обработки отрицательныхаргументов:#define sqrt(x) ((x)<0 ? sqrt(-(x)) : sqrt(x))Отмена определения макросаДиректива вида#undef имязаставляет препроцессор «забыть» определение макроса имя. Использование этойдирективы для неопределенного имени не считается ошибкой.
После отмены определенияимя может быть заново определено с помощью директивы #define. Чтобы избежатьповторного переопределения, можно использовать директиву условной компиляции#ifndef и парную ей конструкцию #endif:#ifndef SIZE#define SIZE 1000#endifВключение файловДиректива препроцессора вида#include < имя файла >#includeили”имя файла”подставляет весь текст, который находится в указанном файле, на место директивы. Имяфайла записывается в формате, зависящем от реализации. Порядок поиска указанногофайла также определяется реализацией. Основная цель использования формы вида ”…”состоит в доступе к заголовочным файлам, написанным самим программистом, в то времякак форма < … > используется для ссылки на стандартные файлы реализации.Условная компиляцияС помощью директив условной компиляции препроцессор включает в текстпрограммы или исключает из него строки исходного текста в зависимости от истинностиусловия.
В качестве условия используется константное выражение. Нулевое значениеозначает «ложь», ненулевое – «истину». Константное выражение должно бытьцелочисленным и не может содержать в себе перечислимых констант, преобразованийтипа и операторов sizeof.БНФ для условной конструкции препроцессора выглядит так: условная конструкция ::= if-строка elif-части [#else текст] #endif if-строка::=#if константное выражение |#ifdef идентификатор | #ifndef идентификатор elif-части::= { #elif константное выражение текст }Описанная выше конструкция обрабатывается так, что константные выражения вif- и elif-строках вычисляются по порядку, пока не будет обнаружено истинноевыражение. Текст, следующий за директивой с истинным значением, обрабатываетсяобычным образом, остальные части игнорируются.
Если все выражения ложны иприсутствует строка #else, то следующий за ней текст обрабатывается обычнымобразом. Директивы #ifdef и #ifndef позволяют включать текст в случае, еслиследующий за директивой идентификатор определен или не определен ( для #ifndef).Вместо #ifdef и #ifndef можно использовать #if с выражениями defined идентификатор и !defined идентификатор соответственно. Например, чтобызастраховаться от повторного включения заголовочного файла hdr.h, его можно оформитьследующим образом:#if !defined(HDR)#define HDR/* содержимое hdr.h */…#endifПреобразование лексем в строкиЛексема #, которая появляется внутри макроопределения перед параметром,означает что параметр (вместе со знаком # перед ним) заменяется на подставляемыйвместо параметра текст, заключенный в двойные кавычки.Склеивание лексем в макрорасширенияхСклеивание лексем с целью формирования новых лексем осуществляется спомощью операции ##.
Две лексемы, разделенные операцией ##, склеиваются в однулексему.#define TEMP(i) temp ## iTEMP(1)= TEMP(2+ m)+ y;После обработки препроцессором это выражение будет иметь следующий вид:temp1 = temp2 + m + y;.