Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
А12.3. Макроопределение и макрорасширение
Управляющая строка вида #define идентификатор последовательность-лексемзаставляет препроцессор заменять идентификатор на последовательность-лексем; символы-разделители в начале и в конце последовательности лексем выбрасываются. Повторная строка #define с тем же идентификатором считается ошибкой, если последовательности лексем неидентичны (несовпадения в символах- разделителях при сравнении во внимание не принимаются). Строка вида #define идентификатор(список-идентификаторов) последовательность-лексемгде между первым идентификатором и знаком ( не должно быть ни одного символа- разделителя, представляет собой макроопределение с параметрами, задаваемыми списком идентификаторов. Как и в первом варианте, символы-разделители в начале и в конце последовательности лексем выбрасываются, и макрос может быть повторно определен только с тем же списком параметров и с той же последовательностью лексем. Управляющая строка вида #undef идентификаторпредписывает препроцессору " забыть" определение, данное идентификатору. Применение #undef к неизвестному идентификатору ошибкой не считается. Если макроопределение было задано вторым способом, то текстовая последовательность, состоящая из его идентификатора, возможно, со следующими за ним символами-разделителями, знака (, списка лексем, разделенных запятыми, и знака ), представляет собой вызов макроса. Аргументами вызова макроса являются лексемы, разделенные запятыми (запятые, " закрытые" кавычками или вложенными скобками, в разделении аргументов не участвуют). Аргументы при их выделении макрорасширениям не подвергаются. Количество аргументов в вызове макроса должно соответствовать количеству параметров макроопределения. После выделения аргументов окружающие их символы-разделители выбрасываются. Затем в замещающей последовательности лексем макроса идентификаторы-параметры (если они не закавычены) заменяются на соответствующие им аргументы. Если в замещающей последовательности перед параметром не стоит знак # и ни перед ним, ни после него нет знака ##, то лексемы аргумента проверяются: не содержат ли они в себе макровызова, и если содержат, то прежде чем аргумент будет подставлен, производится соответствующее ему макрорасширение. На процесс подстановки влияют два специальных оператора. Первый -это оператор #, который ставится перед параметром. Он требует, чтобы подставляемый вместо параметра и знака # (перед ним) текст был заключен в двойные кавычки. При этом в строковых литералах и символьных константах аргумента перед каждой двойной кавычкой " (включая и обрамляющие строки), а также перед каждой обратной наклонной чертой \ вставляется \. Второй оператор записывается как ##. Если последовательность лексем в любого вида макроопределении содержит оператор ##, то сразу после подстановки параметров он вместе с окружающими его символами-разделителями выбрасывается, благодаря чему " склеиваются" соседние лексемы, образуя тем самым новую лексему. Результат не определен при получении неправильных лексем или когда генерируемый текст зависит от порядка применения операторов ##. Кроме того, ## не может стоять ни в начале, ни в конце замещающей последовательности лексем. В макросах обоих видов замещающая последовательность лексем повторно просматривается на предмет обнаружения там новых define-имен. Однако, если некоторый идентификатор уже был заменен в данном расширении, повторное появление такого идентификатора не вызовет его замены. Если полученное расширение начинается со знака #, оно не будет воспринято как директива препроцессора. В ANSI-стандарте процесс макрорасширения описан более точно, чем в первом издании книги. Наиболее важные изменения касаются введения операторов # и ##, которые предоставляют возможность осуществлять расширения внутри строк и конкатенацию лексем. Некоторые из новых правил, особенно касающиеся конкатенации, могут показаться несколько странными. (См. приведенные ниже примеры.) Описанные возможности можно использовать для показа смысловой сущности констант, как, например, в #define TABSIZE 100int table[TABSIZE];Определение #define ABSDIFF(a, b) ((a)> (b)? (a)-(b): (b)-(a))задает макрос, возвращающий абсолютное значение разности его аргументов. В отличие от функции, делающей то же самое, аргументы и возвращаемое значение здесь могут иметь любой арифметический тип и даже быть указателями. Кроме того, аргументы, каждый из которых может иметь побочный эффект, вычисляются дважды: один раз - при проверке, другой раз - при вычислении результата. Если имеется определение #define tempfile(dir) #dir " /%s"то макровызов tempfile(/usr/tmp) даст в результате " /usr/tmp" " /%s"Далее эти две строки превратятся в одну строку. По макросу #define cat(x, y) x ## yвызов cat(var, 123) сгенерирует var123. Однако cat (cat (1, 2), 3) не даст желаемого, так как оператор ## воспрепятствует получению правильных аргументов для внешнего вызова cat. В результате будет выдана следующая цепочка лексем: cat ( 1, 2 )3где )3 (результат " склеивания" последней лексемы первого аргумента с первой лексемой второго аргумента) не является правильной лексемой. Если второй уровень макроопределения задан в виде #define xcat(x, y) cat(x, y)то никаких коллизий здесь не возникает; xcat(хсat(1, 2), 3)в итоге даст 123, поскольку сам xcat не использует оператора ##. Аналогично сработает и ABSDIFF(ABSDIFF(a, b), c), и мы получим правильный результат. A12.4. Включение файла Управляющая строка #include < имя-файла>заменяется на содержимое файла с именем имя-файла. Среди символов, составляющих имя-файла, не должно быть знака > и символа новой строки. Результат не определен, если имя-файла содержит любой из символов ", ', \ или пару символов /*. Порядок поиска указанного файла зависит от реализации. Подобным же образом выполняется управляющая строка #include " имя-файла"Сначала поиск осуществляется по тем же правилам, по каким компилятор ищет первоначальный исходный файл (механизм этого поиска зависит от реализации), а в случае неудачи осуществляется методом поиска, принятым в #include первого типа. Результат остается неопределенным, если имя файла содержит ", \ или /*; использование знака > разрешается. Наконец, директива #include последовательность-лексемне совпадающая ни с одной из предыдущих форм, рассматривает последовательность лексем как текст, который в результате всех макроподстановок должен дать #include <...> или #include "..." . Сгенерированная таким образом директива далее будет интерпретироваться в соответствии с полученной формой. Файлы, вставляемые с помощью #include, сами могут содержать в себе директивы #include. A12.5. Условная компиляция Части программы могут компилироваться условно, если они оформлены в соответствии со следующим схематично изображенным синтаксисом: условная - конструкция -препроцессора: if-строка текст elif-части else-частьнеоб #endifif-строка: #if константное-выражение #ifdef идентификатор #ifndef идентификаторelif-части: elif-строка текст elif-частинеобelif-строка: #elif константное-выражениеelse-часть: else-строка текстelse-строка: #elseКаждая из директив (if-строка, elif-строка, else-строка и #endif) записывается на отдельной строке. Константные выражения в #if и последующих строках #elif вычисляются по порядку, пока не обнаружится выражение с ненулевым (истинным) значением; текст, следующий за строкой с нулевым значением, выбрасывается. Текст, расположенный за директивой с ненулевым значением, обрабатывается обычным образом. Под словом " текст" здесь имеется в виду любая последовательность строк, включая строки препроцессора, которые не являются частью условной структуры; текст может быть и пустым. Если строка #if или #elif с ненулевым значением выражения найдена и ее текст обработан, то последующие строки #elif и #else вместе со своими текстами выбрасываются. Если все выражения имеют нулевые значения и присутствует строка #else, то следующий за ней текст обрабатывается обычным образом. Тексты " неактивных" ветвей условных конструкций, за исключением тех, которые заведуют вложенностью условных конструкций, игнорируются. Константные выражения в #if и #elif являются объектами для обычной макроподстановки. Более того, прежде чем просматривать выражения вида defined идентификатори defined ( идентификатор )на предмет наличия в них макровызова, они заменяются на 1L или 0L в зависимости от того, был или не был определен препроцессором указанный в них идентификатор. Все идентификаторы, оставшиеся после макрорасширения, заменяются на 0L. Наконец, предполагается, что любая целая константа всегда имеет суффикс L, т. е. вся арифметика имеет дело с операндами только типа long или unsigned long. Константное выражение (A7.19) здесь используется с ограничениями: оно должно быть целочисленным, не может содержать в себе перечислимых констант, преобразований типа и операторов sizeof. Управляющие строки #ifdef идентификатор#ifndef идентификаторэквивалентны соответственно строкам #if defined идентификатор#if! defined идентификаторСтроки #elif не было в первой версии языка, хотя она и использовалась в некоторых препроцессорах. Оператор препроцессора defined - также новый. A12.6. Нумерация строк Для удобства работы с другими препроцессорами, генерирующими Си-программы, можно использовать одну из следующих директив: #line константа " имя-файла" #line константаЭти директивы предписывают компилятору считать, что указанные десятичное целое и идентификатор являются номером следующей строки и именем текущего файла соответственно. Если имя файла отсутствует, то ранее запомненное имя не изменяется. Расширения макровызовов в директиве #line выполняются до интерпретации последней. |
Последнее изменение этой страницы: 2017-03-14; Просмотров: 385; Нарушение авторского права страницы