netlib.narod.ru | < Назад | Оглавление | Далее > |
То, что подготовлено в качестве ввода для Си-компилятора, называется единицей трансляции. Она состоит из последовательности внешних объявлений, каждое из которых представляет собой либо объявление, либо определение функции.
единица-трансляции: внешнее-объявление единица-трансляции внешнее-объявление внешнее-объявление: определение-функции объявление
Область видимости внешних объявлений простирается до конца единицы трансляции, в которой они объявлены, точно так же, как область видимости объявлений в блоке распространяется до конца этого блока. Синтаксис внешнего объявления не отличается от синтаксиса любого другого объявления за одним исключением: код функции можно определять только с помощью внешнего объявления.
Определение функции имеет следующий вид:
определение-функции: спецификаторы-объявлениянеоб объявитель список-объявленийнеоб составная-инструкция
Из спецификаторов класса памяти в спецификаторах-объявлениях возможны только extern и static; различия между последними рассматриваются в A11.2.
Типом возвращаемого функцией значения может быть арифметический тип, структура, объединение, указатель и void, но не «функция» и не «массив». Объявитель в объявлении функции должен явно указывать на то, что описываемый им идентификатор имеет тип «функция», т.е. он должен иметь одну из следующих двух форм (A8.6.3):
собственно-объявитель ( список-типов-параметров ) собственно-объявитель ( список-идентификаторовнеоб)
где собственно-объявитель есть идентификатор или идентификатор, заключенный в скобки. Заметим, что тип «функция» посредством typedef получить нельзя.
Первая форма соответствует определению функции новым способом, для которого характерно объявление параметров в списке-типов-параметров вместе с их типами; за объявителем не должно быть списка-объявлений. Если список-типов-параметров не состоит из единственного слова void, показывающего, что параметров у функции нет, то в каждом объявителе в списке-типов-параметров обязан присутствовать идентификатор. Если список-типов-параметров заканчивается знаками «, ...», то вызов функции может иметь аргументов больше, чем параметров; в таком случае, чтобы обращаться к дополнительным аргументам, следует пользоваться механизмом макроса va_arg из заголовочного файла <stdarg.h>, описанного в приложении B. Функции с переменным числом аргументов должны иметь по крайней мере один именованный параметр.
Вторая форма — определение функции старым способом. Список идентификаторов содержит имена параметров, а список объявлений приписывает им типы. В списке объявлений разрешено объявлять только именованные параметры, инициализация запрещается, и из спецификаторов класса памяти возможен только register.
И в том и другом способе определения функции подразумевается, что все параметры как бы объявлены в самом начале составной инструкции, образующей тело функции, и совпадающие с ними имена здесь объявляться не должны (хотя, как и любые идентификаторы, их можно переобъявить в более внутренних блоках). Объявление параметра «массив из типа» можно трактовать как «указатель на тип», аналогично объявлению параметра объявление «функция, возвращающая тип» можно трактовать как «указатель на функцию, возвращающую тип». В момент вызова функции ее аргументы соответствующим образом преобразуются и присваиваются параметрам (см. A7.3.2).
Новый способ определения функций введен стандартом ANSI. Есть также небольшие изменения в операции повышения типа; в первой версии языка параметры типа float следовало читать как double. Различие между float и double становилось заметным, лишь когда внутри функции генерировался указатель на параметр.
Ниже приведен пример определения функции новым способом:
int max(int a, int b, int c) { int m; m = (a > b) ? a : b; return (m > с) ? m : с; }
Здесь int — спецификаторы-объявления; max(int a, int b, int с) — объявитель функции, a { ... } — блок, задающий ее код. Определение той же функции старым способом выглядит следующим образом:
int max(a, b, с) int а, b, с; { /* ... */ }
где max(a, b, c) — объявитель, а int a, b, c — список объявлений для параметров.
Внешние объявления специфицируют характеристики объектов, функций и других идентификаторов. Термин «внешний» здесь используется, чтобы подчеркнуть тот факт, что объявления расположены вне функций; впрямую с ключевым словом extern (внешний) он не связан. Класс памяти для объекта с внешним объявлением либо вообще не указывается, либо специфицируется как extern или static.
В одной единице трансляции для одного идентификатора может содержаться несколько внешних объявлений, если они согласуются друг с другом по типу и способу связи и если для этого идентификатора существует не более одного определения.
Два объявления объекта или функции считаются согласованными по типу в соответствии с правилами, рассмотренными в A8.10. Кроме того, если объявления отличаются лишь тем, что в одном из них тип структуры, объединения или перечисления незавершен (A8.3), а в другом соответствующий ему тип с тем же тегом завершен, то такие типы считаются согласованными. Если два типа массива (A8.6.2) отличаются лишь тем, что один завершенный, а другой незавершенный, то такие типы также считаются согласованными. Наконец, если один тип специфицирует функцию старым способом, а другой — ту же функцию новым способом (с объявлениями параметров), то такие типы также считаются согласованными.
Если первое внешнее объявление функции или объекта помечено спецификатором static, то объявленный идентификатор имеет внутреннюю связь; в противном случае — внешнюю связь. Способы связей обсуждаются в A11.2.
Внешнее объявление объекта считается определением, если оно имеет инициализатор. Внешнее объявление, в котором нет инициализатора и нет спецификатора extern, считается пробным определением. Если в единице трансляции появится определение объекта, то все его пробные определения просто станут избыточными объявлениями. Если никакого определения для этого объекта в единице трансляции не обнаружится, то все его пробные определения будут трактоваться как одно определение с инициализатором 0.
Каждый объект должен иметь ровно одно определение. Для объекта с внутренней связью это правило относится к каждой отдельной единице трансляции, поскольку объекты с внутренними связями в каждой единице уникальны. В случае объектов с внешними связями указанное правило действует в отношении всей программы в целом.
Хотя правило одного определения формулируется несколько иначе, чем в первой версии языка, по существу оно совпадает с прежним. Некоторые реализации его ослабляют, более широко трактуя понятие пробного определения. В другом варианте указанного правила, который распространен в системах UNIX и признан как общепринятое расширение стандарта, все пробные определения объектов с внешними связями из всех транслируемых единиц программы рассматриваются вместе, а не отдельно в каждой единице. Если где-то в программе обнаруживается определение, то пробные определения становятся просто объявлениями, но, если никакого определения не встретилось, то все пробные определения становятся одним-единственным определением с инициализатором 0.
netlib.narod.ru | < Назад | Оглавление | Далее > |