Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Brian_Kernighan_Dennis_Ritchie-The_C_Programming_Language-RU.pdf
Скачиваний:
39
Добавлен:
17.03.2018
Размер:
2.45 Mб
Скачать

классы объектов, имена которых представляют собой отдельные независимые пространства: объекты, функции, typedef-имена и enum-константы; метки инструкций; теги структур, объединений и перечислений; элементы каждой отдельной структуры или объединения.

Сформулированные правила несколько отличаются от прежних, описанных в первом издании. Метки инструкций не имели раньше собственного пространства; теги структур и теги объединений (а в некоторых реализациях и теги перечислений) имели отдельные пространства. Размещение тегов структур, объединений и перечислений в одном общем пространстве — это дополнительное ограничение, которого раньше не было. Наиболее существенное отклонение от первой редакции в том, что каждая отдельная структура (или объединение) создает свое собственное пространство имен для своих элементов. Таким образом, одно и то же имя может использоваться в нескольких различных структурах. Это правило широко применяется уже несколько лет.

Лексическая область видимости идентификатора объекта (или функции), объявленного во внешнем объявлении, начинается с места, где заканчивается его объявитель, и простирается до конца единицы трансляции, в которой он объявлен. Область видимости параметра в определении функции начинается с начала блока, представляющего собой тело функции, и распространяется на всю функцию; область видимости параметра в описании функции заканчивается в конце этого описания. Область видимости идентификатора, объявленного в начале блока, начинается от места, где заканчивается его объявитель, и продолжается до конца этого блока. Областью видимости метки является вся функция, где эта метка встречается. Область видимости тега структуры, объединения или перечисления начинается от его появления в спецификаторе типа и продолжается до конца единицы трансляции для объявления внешнего уровня и до конца блока для объявления внутри функции.

Если идентификатор явно объявлен в начале блока (в том числе тела функции), то любое объявление того же идентификатора, находящееся снаружи этого блока, временно перестает действовать вплоть до конца блока.

А 11.2. Связи

Если встречается несколько объявлений, имеющих одинаковый идентификатор и описывающих объект (или функцию), то все эти объявления в случае внешней связи относятся к одному объекту (функции) — уникальному для всей программы; если же связь внутренняя, то свойство уникальности распространяется только на единицу трансляции.

Как говорилось в А10.2, если первое внешнее объявление имеет спецификатор static, то оно описывает идентификатор с внутренней связью, если такого спецификатора нет, то — с внешней связью. Если объявление находится внутри блока и не содержит extern, то соответствующий идентификатор ни с чем не связан и уникален для данной функции. Если объявление содержит extern и блок находится в области видимости внешнего объявления этого идентификатора, то последний имеет ту же связь и относится к тому же объекту (функции). Однако если ни одного внешнего объявления для этого идентификатора нет, то он имеет внешнюю связь.

А 12. Препроцессирование

Препроцессор выполняет макроподстановку, условную компиляцию, включение именованных файлов. Строки, начинающиеся со знака # (перед которым возможны символы-разделители), устанавливают связь с препроцессором. Их синтаксис не зависит от остальной части языка; они могут появляться где угодно и оказывать влияние (независимо от области видимости) вплоть до конца транслируемой единицы. Границы строкпринимаются во внимание; каждая строка анализируется отдельно (однако есть возможность "склеивать" строки, см. А12.2). Лексемами для препроцессора являются все лексемы языка и последовательности символов, задающие имена файлов, как, например, в директиве #include (А12.4). Кроме того, любой символ, не определенный каким-либо другим способом, воспринимается как лексема.

Влияние символов-разделителей, отличающихся от пробелов и горизонтальных табуляций, внутри строк препроцессора не определено.

Само препроцессирование проистекает в нескольких логически последовательных фазах. В отдельных реализациях некоторые фазы объединены.

1.Трехзнаковые последовательности, описанные в А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 100 int 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 последовательность-лексем

Соседние файлы в предмете Программирование