netlib.narod.ru< Назад | Оглавление | Далее >

А10. Внешние объявления

То, что подготовлено в качестве ввода для Си-компилятора, называется единицей трансляции. Она состоит из последовательности внешних объявлений, каждое из которых представляет собой либо объявление, либо определение функции.

  единица-трансляции:
      внешнее-объявление
      единица-трансляции внешнее-объявление

  внешнее-объявление:
      определение-функции
      объявление

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

A10.1. Определение функции

Определение функции имеет следующий вид:

  определение-функции:
      спецификаторы-объявлениянеоб объявитель список-объявленийнеоб
      составная-инструкция

Из спецификаторов класса памяти в спецификаторах-объявлениях возможны только 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 — список объявлений для параметров.

A10.2. Внешние объявления

Внешние объявления специфицируют характеристики объектов, функций и других идентификаторов. Термин «внешний» здесь используется, чтобы подчеркнуть тот факт, что объявления расположены вне функций; впрямую с ключевым словом extern (внешний) он не связан. Класс памяти для объекта с внешним объявлением либо вообще не указывается, либо специфицируется как extern или static.

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

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

Если первое внешнее объявление функции или объекта помечено спецификатором static, то объявленный идентификатор имеет внутреннюю связь; в противном случае — внешнюю связь. Способы связей обсуждаются в A11.2.

Внешнее объявление объекта считается определением, если оно имеет инициализатор. Внешнее объявление, в котором нет инициализатора и нет спецификатора extern, считается пробным определением. Если в единице трансляции появится определение объекта, то все его пробные определения просто станут избыточными объявлениями. Если никакого определения для этого объекта в единице трансляции не обнаружится, то все его пробные определения будут трактоваться как одно определение с инициализатором 0.

Каждый объект должен иметь ровно одно определение. Для объекта с внутренней связью это правило относится к каждой отдельной единице трансляции, поскольку объекты с внутренними связями в каждой единице уникальны. В случае объектов с внешними связями указанное правило действует в отношении всей программы в целом.

Хотя правило одного определения формулируется несколько иначе, чем в первой версии языка, по существу оно совпадает с прежним. Некоторые реализации его ослабляют, более широко трактуя понятие пробного определения. В другом варианте указанного правила, который распространен в системах UNIX и признан как общепринятое расширение стандарта, все пробные определения объектов с внешними связями из всех транслируемых единиц программы рассматриваются вместе, а не отдельно в каждой единице. Если где-то в программе обнаруживается определение, то пробные определения становятся просто объявлениями, но, если никакого определения не встретилось, то все пробные определения становятся одним-единственным определением с инициализатором 0.


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz