А 10. Внешние объявления
То, что подготовлено в качестве ввода для Си-компилятора, называет- ся единицей трансляции. Она состоит из последовательности внешних объявлений, каждое из которых представляет собой либо объявление, либо определение функции.
единица -трансляции:
внешнее-объявление
единица-трансляции внешнее-объявление
внешнее -объявление:
определение-функции
объявление
Область видимости внешних объявлений простирается до конца еди- ницы трансляции, в которой они объявлены, точно так же, как область видимости объявлений в блоке распространяется до конца этого блока.
Синтаксис внешнего объявления не отличается от синтаксиса любого другого объявления за одним исключением: код функции можно опреде- лять только с помощью внешнего объявления.
А
Определение функции
Определение функции имеет следующий вид:
определение-функции:
Из спецификаторов класса памяти в спецификаторах-объявлениях воз- можны только extern и static; различия между последними рассматри- ваются в
Типом возвращаемого функцией значения может быть арифмети- ческий тип,
объединение, указатель и void, но не "функция" и не "массив". Объявитель в объявлении функции должен явно указывать на что описываемый им идентификатор имеет тип "функция", т. е. он должен иметь одну из следующих двух форм
( список-типов-параметров )
(
)
1116
290 Приложение А. Справочное руководство где есть идентификатор или идентификатор, за- ключенный в скобки. Заметим, что тип "функция" посредством typedef получить нельзя.
Первая форма соответствует определению функции новым способом, для которого характерно объявление параметров в списке-типов-параметров вместе с их типами; за объявителем не должно быть списка-объявлений.
Если список-типов-параметров не состоит из слова void, показывающего, что параметров у функции нет, то в каждом объяви- теле в списке-типов-параметров обязан присутствовать идентификатор.
Если список-типов-параметров заканчивается то вызов функции может иметь аргументов больше, чем параметров; в таком слу- чтобы обращаться к дополнительным аргументам, следует пользо- ваться механизмом макроса va_arg из заголовочного файла описанного в приложении В. Функции с переменным числом аргументов должны иметь по крайней мере один именованный параметр.
Вторая форма - определение функции старым способом. Список-иден- тификаторов содержит имена параметров, а список-объявлений припи- сывает им типы. В списке-объявлении разрешено объявлять только име- нованные параметры, инициализация запрещается, и из спецификаторов класса памяти возможен только register.
И в том и другом способе определения функции мыслится, что все па- раметры как бы объявлены в самом начале составной инструкции, обра- зующей тело функции, и совпадающие с ними имена здесь объявляться не должны (хотя, как и
любые идентификаторы, их можно переобъявить в более внутренних блоках). Объявление параметра "массив из
типа"можно трактовать как "указатель на
тип"; аналогично объявлению пара- метра объявление "функция, возвращающая
тип" можно трактовать как "указатель на функцию, возвращающую
В момент вызова функции ее аргументы соответствующим образом преобразуются и присваивают- ся параметрам (см.
Новый способ определения функций введен ANSI-стандартом. Есть так- же небольшие изменения в операции повышения типа; в первой версии языка параметры типа следовало читать как double. Различие между и double становилось заметным, лишь когда внутри функции генери- ровался указатель на параметр.
Ниже приведен пример определения функции новым способом:
int max(int a, int b, int с)
{
int
= (a > b) ? a : b;
А
Внешние объявления 291
return
> с) ? : с;
}
Здесь int — спецификаторы-объявления;
a, int b, int явитель функции, а {
} - блок, задающий ее код. Определение старым способом той же функции выглядит следующим образом:
int b, с)
int a, b, с;
{
}
b,
int a, b, с - список-объявлений для пара- метров.
А 10.2. Внешние объявления
Внешние объявления специфицируют характеристики объектов, функ- ций и других идентификаторов. Термин "внешний" здесь используется,
чтобы подчеркнуть тот факт, что объявления расположены вне функций;
впрямую с ключевым словом
("внешний") он не связан. Класс па- мяти для объекта с внешним объявлением либо вообще не указывается,
либо специфицируется как или static.
В одной единице трансляции для одного идентификатора может со- держаться несколько внешних объявлений, если они согласуются друг с другом по типу и способу связи и если для этого идентификатора суще- ствует не более одного определения.
Два объявления объекта или функции считаются согласованными по типу в соответствии с правилами, рассмотренными в
Кроме того,
если объявления отличаются лишь тем, что в одном из них тип структу- ры, объединения или перечисления незавершен (А8.3), а в другом соот- ветствующий ему тип с тем же тегом завершен, то такие типы считаются согласованными. Если два типа массива (8.6.2) отличаются лишь тем,
что один завершенный, а другой незавершенный, то такие типы также считаются согласованными. Наконец, если один тип специфицирует функцию старым способом, а другой - ту же функцию новым способом
(с объявлениями параметров), то такие типы также считаются согласо- ванными.
Если первое внешнее объявление функции или объекта помечено спе- цификатором static, то объявленный идентификатор имеет внутреннюю
в противном случае - внешнюю
Способы связей обсуждают- ся в
Внешнее объявление объекта считается определением, если оно имеет инициализатор. Внешнее объявление, в котором нет инициализатора и нет
292 Приложение А. Справочное руководствоспецификатора считается
пробным определением. Если в единице трансляции появится определение то все его пробные определения просто станут избыточными объявлениями. Если никакого определения для этого
объекта в единице трансляции не обнаружится, то все его проб- ные определения будут трактоваться как одно определение с инициа- лизатором 0.
Каждый объект должен иметь ровно одно определение. Для объекта с внутренней связью это правило относится к каждой отдельной единице трансляции, поскольку объекты с внутренними связями в каждой едини- це уникальны. В случае объектов с внешними связями указанное прави- ло действует в отношении всей программы в целом.
Хотя правило одного определения формулируется несколько иначе, чем в первой версии языка, по существу оно совпадает с прежним. Некоторые реализации ослабляют, более широко трактуя понятие пробного опре- деления. В другом варианте указанного правила, который распространен в системах UNIX и признан как общепринятое расширение стандарта, все пробные определения объектов с внешними связями из всех транслиру- емых единиц программы рассматриваются вместе, а не отдельно в каждой единице. Если где-то в программе обнаруживается определение, то проб- ные определения становятся просто объявлениями, но, если никакого опре- деления не встретилось, то все пробные определения становятся одним- единственным определением с инициализатором 0.
А Область видимости и связиКаждый раз компилировать всю программу целиком нет необходимо- сти. Исходный текст можно хранить в нескольких файлах, представля- ющих собой единицы трансляции. Ранее скомпилированные программы могут загружаться из библиотек. Связи между функциями программы могут осуществляться через вызовы и внешние данные.
Следовательно, существуют два вида областей видимости: первая - это
лексическая идентификатора: т. е. область в тексте программы,
где имеют смысл все его характеристики; вторая область - это область,
ассоциируемая с объектами и функциями, имеющими внешние связи,
устанавливаемые между идентификаторами из раздельно компилируе- мых единиц трансляции.
А Лексическая область видимостиКаждый идентификатор попадает в одно из нескольких пространств имен. Эти пространства никак не связаны друг с другом. Один и тот же идентификатор может использоваться в разных смыслах даже в одной об-
А
Область видимости и связи 293
ласти видимости, если он принадлежит разным пространствам имен. Ниже через точку с запятой перечислены классы объектов, имена которых пред- ставляют собой отдельные независимые пространства: объекты,
и
метки инструкций; теги структур, объ- единений и перечислений; элементы каждой отдельной структуры или объединения.
Сформулированные правила несколько отличаются от прежних, описанных в первом издании. Метки инструкций не имели раньше собственного про- странства; теги структур и теги объединений (а в некоторых реализациях и теги перечислений) имели отдельные пространства. Размещение тегов структур, объединений и перечислений в одном общем пространстве - это дополнительное ограничение, которого раньше не было. Наиболее суще- ственное отклонение от первой редакции что каждая отдельная струк- тура (или объединение) создает свое собственное пространство имен для своих элементов. Таким образом, одно и то же имя может использоваться в нескольких различных структурах. Это правило широко применяется уже несколько лет.
Лексическая область видимости идентификатора объекта (или функ- ции), объявленного во внешнем объявлении, начинается с места, где за- канчивается его объявитель, и простирается до конца единицы трансля- ции, в которой он объявлен. Область видимости параметра в определе- нии функции начинается с начала блока, представляющего собой тело функции, и распространяется на всю функцию; область видимости пара- метра в описании функции заканчивается в конце этого описания. Об- ласть видимости идентификатора, объявленного в начале блока, начина- ется от места, где заканчивается его объявитель, и продолжается до конца этого блока. Областью видимости метки является вся функция, где эта метка встречается. Область видимости структуры, объединения или перечисления начинается от его появления в спецификаторе типа и про- должается до конца единицы трансляции для объявления внешнего уров- ня и до конца блока для объявления внутри функции.
Если идентификатор явно объявлен в начале блока (в том числе тела функции), то любое объявление того же идентификатора, находящееся снаружи этого блока, временно перестает действовать вплоть до конца блока.
А 11.2. СвязиЕсли встречается несколько объявлений, имеющих одинаковый иден- тификатор и описывающих объект (или функцию), то все эти объявле- ния в случае внешней связи относятся к одному объекту (функции) -
уникальному для всей программы; если же связь внутренняя, то свойство уникальности распространяется только на единицу трансляции.
294 Приложение А. Справочное руководствоКак говорилось в если первое внешнее объявление имеет спе- цификатор static, то оно описывает идентификатор с внутренней свя- зью, если такого спецификатора нет, то - с внешней связью. Если объяв- ление находится внутри блока и не содержит extern, то соответствую- щий идентификатор ни с чем не связан и уникален для данной функции.
Если объявление содержит и блок находится в области видимос- ти внешнего объявления этого идентификатора, то последний имеет ту же связь и относится к тому же объекту (функции). Однако если ни од- ного внешнего объявления для этого идентификатора нет, то он имеет внешнюю связь.
12. ПрепроцессированиеПрепроцессор выполняет макроподстановку, условную компиляцию,
включение именованных файлов. Строки, начинающиеся со знака (пе- ред которым возможны символы-разделители), устанавливают связь с
Их синтаксис не зависит от остальной части языка; они могут появляться где угодно и оказывать влияние (независимо от облас- ти видимости) вплоть до конца транслируемой единицы. Границы строк принимаются во внимание; каждая строка анализируется отдельно (од- нако есть возможность "склеивать" строки, см.
Лексемами для препроцессора являются все лексемы языка и последовательности сим- волов, задающие имена файлов, как, например, в директиве
(А12.4). Кроме того, любой символ, не определенный каким-либо другим способом, воспринимается как лексема. Влияние символов-разделителей,
отличающихся от пробелов и горизонтальных табуляций, внутри строк препроцессора не определено.
Само препроцессирование проистекает в нескольких логически после- довательных фазах. В отдельных реализациях некоторые фазы объеди- нены.
1.
последовательности, описанные в заменяются их эквивалентами. Между строками вставляются символы новой строки,
если того требует операционная система.
2. Выбрасываются пары символов, состоящие из обратной наклонной чер- ты с последующим символом новой строки; тем самым осуществляется "склеивание" строк
3. Программа разбивается на лексемы, разделенные символами-раздели- телями. Комментарии заменяются единичными пробелами. Затем вы-
полняются директивы препроцессора и макроподстановкиА12.10).
А 12. Препроцессирование 295 4.
в символьных константах и строковых ли- тералах
А2.6) заменяются на символы, которые они обознача- ют. Соседние строковые литералы конкатенируются.
5. Результат транслируется. Затем устанавливаются связи с другими про- граммами и библиотеками посредством сбора необходимых программ и данных и соединения ссылок на внешние функции и объекты с их определениями.
А
последовательности
Множество символов, из которых набираются исходные Си-програм- мы, основано на семибитовом ASCII-коде. Однако он чем инвари- антный код символов ISO
(ISO
Invariant Code Set).
Чтобы дать возможность пользоваться сокращенным набором символов,
все указанные ниже последовательности заменяются на соответствующие им единичные символы. Замена осуществляется до любой иной обработки.
??=
??( [ ??< {
??/ \ ??) [ ??> }
??'
??! ,' ??-
Никакие другие замены, кроме указанных, не делаются.
Трехзнаковые последовательности введены ANSI-стандартом.
А
Склеивание строк
Строка, заканчивающаяся обратной наклонной чертой, соединяется со следующей, поскольку символ \ и следующий за ним символ новой стро- ки выбрасываются. Это делается перед "разбиением" текста на лексемы.
А
Макроопределение и макрорасширение
Управляющая строка вида define идентификатор последовательность-лексем
заставляет препроцессор заменять идентификатор на последовательность лексем; символы-разделители в начале и в конце последовательности лек- сем выбрасываются. Повторная строка tfdefine с тем же идентификато- ром считается ошибкой, если последовательности лексем неидентичны
(несовпадения в символах-разделителях при сравнении во внимание не при- нимаются). Строка вида
)
последовательность-лексем
где между первым идентификатором и знаком ( не должно быть ни одного
296 Приложение А. Справочное руководствосимвола-разделителя, представляет собой макроопределение с параметра- ми, задаваемыми списком идентификаторов.
и в первом варианте, сим- волы-разделители в начале и в конце последовательности лексем выбра- сываются, и макрос может быть повторно определен только с тем же спис- ком параметров и с той же последовательностью лексем. Управляющая строка вида
идентификаторпредписывает препроцессору "забыть" определение, данное идентифи- катору. Применение к неизвестному идентификатору ошибкой не считается.
Если макроопределение было задано вторым способом, то текстовая последовательность, состоящая из идентификатора, возможно, со сле- дующими за ним символами-разделителями, знака списка лексем, раз- деленных запятыми, и знака представляет собой вызов макроса. Аргу-
ментами вызова макроса являются лексемы, разделенные запятыми (за- пятые, "закрытые" кавычками или вложенными скобками, в разделении аргументов не участвуют). Аргументы при их выделении макрорасшире- ниям не подвергаются. Количество аргументов в вызове макроса должно соответствовать количеству параметров макроопределения. После выде- ления аргументов окружающие их символы-разделители выбрасыва- ются. Затем в замещающей последовательности лексем макроса иден- тификаторы-параметры (если они не закавычены) заменяются на соот- ветствующие им аргументы. Если в замещающей последовательности перед параметром не стоит знак и ни перед ним, ни после него нет знака то лексемы аргумента не содержат ли они в себе макро- вызова, и если содержат, то прежде чем аргумент будет подставлен, про- изводится соответствующее ему макрорасширение.
На процесс подстановки влияют два специальных оператора. Первый - это оператор который ставится перед параметром. Он требует, чтобы подставляемый вместо параметра и знака (перед ним) текст был заклю- чен в двойные кавычки. При этом в строковых литералах и символьных константах аргумента перед каждой двойной кавычкой (включая и об- рамляющие строки), а также перед каждой обратной наклонной чертой \
вставляется \.
Второй оператор записывается как
Если последовательность лек- сем в любого вида макроопределении содержит оператор
tttt, то сразу пос- ле подстановки параметров он вместе с окружающими его символами- разделителями выбрасывается, благодаря чему "склеиваются" соседние образуя тем самым новую лексему. Результат не определен при получении неправильных лексем или когда генерируемый текст зависит
А 12. Препроцессирование 297от порядка применения операторов
Кроме того,
не может стоять ни в начале, ни в конце замещающей последовательности лексем.
В макросах обоих видов замещающая последовательность лексем по- вторно просматривается на предмет обнаружения там новых ine-имен.
Однако, если некоторый идентификатор уже был заменен в данном ширении, повторное появление такого идентификатора не вызовет его замены.
Если полученное расширение начинается со знака оно не будет вос- принято как директива препроцессора.
•
В ANSI-стандарте процесс макрорасширения описан более точно, чем в пер- вом издании книги. Наиболее важные изменения касаются введения опе- раторов tt и tt#, которые предоставляют возможность осуществлять расши- рения внутри строк и конкатенацию лексем. Некоторые из новых правил,
особенно касающиеся конкатенации, могут показаться несколько странны- ми. (См. приведенные ниже примеры.)
Описанные возможности можно использовать для показа смысловой сущности констант,
например, в
TABSIZE 100
int
Определение
«define ABSDIFF(a, b)
? (a)-(b) :
задает макрос, возвращающий абсолютное значение разности его аргу- ментов. В отличие от функции, делающей то же самое, аргументы и воз- вращаемое значение здесь могут иметь любой арифметический тип и даже быть указателями. Кроме того, аргументы, каждый из которых может иметь побочный эффект, вычисляются дважды: один раз - при проверке,
другой раз - при вычислении результата.
Если имеется определение то макровызов i l e ( / u s r / t m p )
даст в результатеДалее эти две строки превратятся в одну строку. По макросу x «« у вызов c a t ( v a r , 123) сгенерирует var123. Однако cat (cat
3)
даст желаемого, так как оператор воспрепятствует получению правильных
298 Приложение А. Справочное руководство аргументов для внешнего вызова cat. В результате будет выдана следу- ющая цепочка лексем:
cat ( 1 , 2 )3
где )3 (результат "склеивания" последней лексемы первого аргумента с первой лексемой второго аргумента) не является правильной лексемой.
Если второй уровень макроопределения задан в виде то никаких коллизий здесь не возникает;
2), 3)витогедаст поскольку сам xcat не использует оператора
tttt.Аналогично сработает и ABSDIFF(ABSDIFF(a,
с), и мы получим пра- вильный результат.