Керниган и Ритчи - Язык программирования Си (793773), страница 56
Текст из файла (страница 56)
Трехзнаковые последовательности, описанные в А12.1, заменяются их эквивалентами. Междустроками вставляются символы новой строки, если того требует операционная система.2. Выбрасываются пары символов, состоящие из обратной наклонной черты с последующим символомновой строки; тем самым осуществляется "склеивание" строк (А12.2).3. Программа разбивается на лексемы, разделенные символами-разделителями. Комментариизаменяются единичными пробелами. Затем выполняются директивы препроцессора имакроподстановки (А12.3-А12.10).4.
Эскейп-последовательности в символьных константах и строковых литералах (А2.5.2, А2.6) заменяютсяна символы, которые они обозначают. Соседние строковые литералы конкатенируются.5. Результат транслируется. Затем устанавливаются связи с другими программами и библиотекамипосредством сбора необходимых программ и данных и соединения ссылок на внешние функции иобъекты с их определениями.А 12.1.
Трехзнаковые последовательностиМножество символов, из которых набираются исходные Си-программы, основано на семибитовом ASCII-коде.Однако он шире, чем инвариантный код символов ISO 646-1983 (ISO 646-1983 Invariant Code Set). Чтобы датьвозможность пользоваться сокращенным набором символов, все указанные ниже трехзнаковыепоследовательности заменяются на соответствующие им единичные символы. Замена осуществляется долюбой иной обработки.??=??/??'#\^??(??)??![];??<??>??-{}~Никакие другие замены, кроме указанных, не делаются.Трехзнаковые последовательности введены ANSI-стандартом.А 12.2. Склеивание строкСтрока, заканчивающаяся обратной наклонной чертой, соединяется со следующей, поскольку символ \ иследующий за ним символ новой строки выбрасываются.
Это делается перед "разбиением" текста налексемы.А 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(xcat(1, 2), 3) выдаст 123, поскольку сам xcat неиспользует оператора #.Аналогично сработает и ABSDIFF(ABSDIFF(a, b), с), и мы получим правильный результат.А 12.4.
Включение файлаУправляющая строка# include <имя-файла>заменяется на содержимое файла с именем имя-файла. Среди символов, составляющих имя-файла, недолжно быть знака > и символа новой строки. Результат не определен, если имя-файла содержит любой изсимволов ", ', \ или пару символов /*.
Порядок поиска указанного файла зависит от реализации.Подобным же образом выполняется управляющая строка# include "имя-файла"Сначала поиск осуществляется по тем же правилам, по каким компилятор ищет первоначальный исходныйфайл (механизм этого поиска зависит от реализации), а в случае неудачи осуществляется методом поиска,принятым в #include первого типа. Результат остается неопределенным, если имя файла содержит ", \ или/*; использование знака > разрешается.Наконец,директива# include последовательность-лексемне совпадающая ни с одной из предыдущих форм, рассматривает последовательность лексем как текст,который в результате всех макроподстановок должен дать #include <...> или #include "...".Сгенерированная таким образом директива далее будет интерпретироваться в соответствии с полученнойформой.Файлы, вставляемые с помощью #include, сами могут содержать в себе директивы #include.А 12.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 в зависимости от того, был или не былопределен препроцессором указанный в них идентификатор.















