ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы.



 

Часть 3

                                      - 40 -
                                          
               
         
         жет быть задана более чем одна переменная путем задания  множест-
         венного  об"явления,  в  котором  деклараторы разделены запятыми.
          задает класс памяти переменной. В некоторых  слу-
         чаях  переменные  могут быть инициализированы при их определении.
         Классы памяти и инициализация описаны в разделах 4.6 и 4.7  соот-
         ветственно.
                                       
               
               4.4.1. Об"явление простой переменной
                
               Синтаксис:
                
               [,...];
                
               Об"явление  простой  переменной определяет имя переменной и
         ее тип; оно может также определять класс памяти  переменной,  как
         это описано в разделе 4.6. Имя переменной- это идентификатор, за-
         данный  в  об"явлении.  Спецификатор типа  задает
         имя определяемого типа данных.
               Можно определить имена различных переменных в том же  самом
         об"явлении,  задавая список идентификаторов, разделенных запятой.
         Каждый идентификатор списка именует переменную.  Все  переменные,
         заданные в об"явлении, имеют один и тот же тип.
                
               Примеры
                
               int x;                            /* Example 1 */
                
               unsigned long reply, flag         /* Example 2 */
                
               double order;                     /* Example 3 */
                
               В  первом примере об"является простая переменная x. Эта пе-
         ременная может принимать любое значение  из  множества  значений,
         определяемых для типа int.
               Во  втором  примере об"явлены две переменные: reply и flag.
         Обе переменные имеют тип unsigned long.
               В третьем примере об"явлена переменная order, которая имеет
         тип double. Этой переменной могут быть присвоены величины с  пла-
         вающей запятой.
         
               
               4.4.2. Об"явление перечисления
                
               Синтаксис:
                
           enum[]{}[,...];
                
           enum[,...];
                
               Об"явление  перечисления задает имя переменной перечисления
         и определяет список именованных констант, называемый списком  пе-
         речисления.  Значением каждого имени списка является целое число.
         Переменная перечисления принимает значение одной  из  именованных
         констант  списка. Именованные константы списка имеют тип int. Та-

                                      - 41 -
         
         
         ким образом, память соответствующая переменной перечисления-  это
         память, необходимая для размещения отдельной целой величины.
               Об"явление перечисления начинается с ключевого слова enum и
         имеет две формы представления. В первой форме представления имена
         перечисления задаются в списке перечисления .
               Опция  -  это идентификатор, который именует тип пере-
         числения, определенного в .
               Переменную перечисления именует . В  об"явлении
         может быть описана более чем одна переменная перечисления.
               Во второй форме используется тег перечисления, который ссы-
         лается  на тип перечисления. В этой форме об"явления список пере-
         числения не представлен, поскольку тип перечисления  определен  в
         другом  месте. Если задаваемый тег не ссылается на уже определен-
         ный тип перечисления, или если именуемый тегом тип находится  вне
         текущей видимости, то выдается ошибка.
                
                имеет следующий синтаксис:
                
               [=][,
                                       [= переопределяет последователь-
         ность значений, заданных по умолчанию.  Идентификатор,  следующий
         перед записью = принимает значение, задавае-
         мое  этим константным выражением. Константное выражение имеет тип
         int и может быть отрицательным. Следующий идентификатор в  списке
         ассоциируется  с  величиной, равной +1, если
         он явно не задается другой величиной.
               Перечисление может содержать повторяющиеся значения иденти-
         фикаторов, но каждый идентификатор должен быть уникальным.  Кроме
         того,  он должен быть отличным от всех других идентификаторов пе-
         речислений с той же видимостью. Например, двум различным  иденти-
         фикаторам  null и zero может быть задано значение 0 в одном и том
         же перечислении. Идентификаторы должны  быть  отличны  от  других
         идентификаторов  с той же самой видимостью, включая имена обычных
         переменных и идентификаторы других перечислений. Теги  перечисле-
         ний  должны  быть отличны от тегов перечислений, тегов структур и
         совмещений с той же самой видимостью.
               
                
               Примеры:
                
               /**************** Example 1 ***************/
                
               enum day {
                        saturday,
                        sunday = 0,
                        monday,

                                      - 42 -
         
         
                        tuesday,
                        wednesday,
                        thursday,
                        friday
                        } workday;
                
                /***************** Example 2 ***************/
                
                enum day today = wednesday;
                
               В первом примере определяется тип перечисления, поименован-
         ный day и об"является переменная workday этого типа перечисления.
         С saturday по умолчанию ассоциируется значение  0.  Идентификатор
         sunday  явно  устанавливается  в  0. Оставшиеся идентификаторы по
         умолчанию принимают значение от 1 до 5.
               Во втором примере переменной today типа enum day присваива-
         ется значение из перечисления. Заметим, что для присваивания  ис-
         пользуется  имя константы из перечисления. Так как тип перечисле-
         ния day был  предварительно  об"явлен,  то  достаточно  сослаться
         только на тег перечисления.
         
               
               4.4.3. Об"явления структур
                
               Синтаксис:
                
     struct[]{}[,...];

     struct[,...];
                
               Об"явление структуры задает имя типа структуры и специфици-
         рует последовательность переменных величин, называемых элементами
         структуры, которые могут иметь различные типы.
               Об"явление  структуры начинается с ключевого слова struct и
         имеет две формы представления, как показано выше. В первой  форме
         представления  типы и имена элементов структуры специфицируются в
         списке об"явлений элементов . - это
         идентификатор, который  именует  тип  структуры,  определенный  в
         списке об"явлений элементов.
               Каждый    задает имя переменной типа структуры.
         Тип переменной в деклараторе может быть модифицирован  на  указа-
         тель к структуре, на массив структур или на функцию, возвращающую
         структуру.
               Вторая синтаксическая форма использует тег-  структуры
         для  ссылки на тип структуры. В этой форме об"явления отсутствует
         список об"явлений элементов, поскольку тип структуры определен  в
         другом  месте. Определение типа структуры должно быть видимым для
         тега, который используется  в  об"явлении  и  определение  должно
         предшествовать об"явлению через тег, если тег не используется для
         об"явления  указателя  или структурного типа typedef. В последних
         случаях об"явления могут использовать тег структуры без предвари-
         тельного определения типа структуры, но все же определение должно
         находиться в пределах видимости об"явления.
               Список об"явлений элементов -  это
         одно  или  более  об"явлений переменных или битовых полей. Каждая

                                      - 43 -
         
         
         переменная,  об"явленная  в  этом  списке,  называется  элементом
         структурного  типа. Об"явления переменных списка имеют тот же са-
         мый синтаксис, что и об"явления  переменных  обсуждаемых  в  этой
         главе,  за  исключением  того,  что об"явления не могут содержать
         спецификаторов класса памяти или инициализаторов. Элементы струк-
         туры могут быть любого  типа:  основного,  массивом,  указателем,
         совмещением или структурой.
               Элемент  не может иметь тип структуры, в которой он появля-
         ется. Однако, элемент может быть об"явлен, как указатель  на  тип
         структуры,  в  которую  он  входит,  позволяя создавать списочные
         структуры.
                
               Битовые поля
                
               Об"явления битовых полей имеют следующий синтаксис:
                
                []:;
                
               Битовое  поле состоит из некоторого числа бит, специфициро-
         ванных константным выражением- . Для  бито-
         вого поля спецификатор типа  должен специфициро-
         вать  беззнаковый  целый тип, а константное выражение должно быть
         неотрицательной целой величиной. Массивы битовых полей, указатели
         на битовые поля и функции, возвращающие битовые поля не  допуска-
         ются.  Идентификатор-  именует битовое поле. Неимено-
         ванное битовое поле, чей размер специфицируется как нулевой, име-
         ет специальное назначение: оно гарантирует, что память для следу-
         ющей переменной об"явления будет начинаться на границе int.
               
               Идентификаторы элементов внутри об"являемой структуры долж-
         ны  быть  уникальными.  Идентификаторы  элементов  внутри  разных
         структур  могут совпадать. В пределах той же самой видимости теги
         структур должны отличаться от других тегов (тегов  других  струк-
         тур, совмещений и перечислений).
               Переменные (элементы) структуры запоминаются последователь-
         но  в том же самом порядке, в котором они об"являются: первой пе-
         ременной соответствует самый младший адрес памяти,  а  последней-
         самый  старший.  Память  каждой  переменной начинается на границе
         свойственной ее  типу.  Поэтому  могут  появляться  неименованные
         участки между соседними элементами.
               Битовые  поля не располагаются на пересечении границ, обяв-
         ленных для них типов. Например, битовое поле, об"явленое с  типом
         unsigned  int,  упаковывается  или  в пространстве, оставшимся от
         предидущего unsigned int или начиная с нового unsigned int.
           
               Примеры
                
                /**************** Example 1 ****************/
                
                struct {
                           float x,y;
                    } complex;
                
                /**************** Example 2 *****************/
                

                                      - 44 -
         
         
                struct employee {
                            char name[20];
                            int id;
                            long class;
                    } temp;
                
                /**************** Example 3 ******************/
                
                struct employee student, faculty, staff;
                
                /**************** Example 4 ******************/
                
                struct sample {
                            char c;
                            float *pf;
                            struct sample *next;
                    } x;
                
                /***************** Example 5 ******************/
                
                struct {
                            unsigned icon : 8;
                            unsigned color : 4;
                            unsigned underline : 1;
                            unsigned blink : 1;
                    } screen[25][80];
                
               В первом примере об"является переменная  с  именем  complex
         типа структура. Эта структура состоит из двух элементов x и y ти-
         па float. Тип структуры не поименован.
               Во втором примере об"является переменная с именем temp типа
         структура. Структура состоит из трех элементов с именами name, id
         и  class.  Элемент  с именем name- это массив иэ 20- ти элементов
         типа char. элементы с именами id и class- это простые  переменные
         типа  int  и long соответственно. Идентификатор employee является
         тегом структуры.
               В третьем примере об"явлены три переменных типа структура с
         именами: student, faculty и staff. Каждая из структур состоит  из
         трех  элементов  одной  и той же конструкции. Элементы определены
         при об"явлении типа структуры с тегом employee в предыдущем  при-
         мере.
               В  четвертом примере об"является переменная с именем x типа
         структура. Первые два элемента структуры представлены  переменной
         c  типа  char и указателем pf на величину типа float. Третий эле-
         мент с именем  next  об"являются  как  указатель  на  описываемую
         структуру sample.
               В  пятом примере об"является двумерный массив поименованный
         screen, элементы которого имеют структурный тип.  Массив  состоит
         из 2000 элементов и каждый элементэто отдельная структура, состо-
         ящая  из  четырех  элементов типа bit-fild с именами icon, color,
         underline и blink.
         
               
                

                                      - 45 -
         
               4.4.4. Об"явление совмещений
         
               Синтаксис:
                
     union[]{}[,...];
                 
     union[,...];
     
               Об"явление совмещения определяет имя переменной  совмещения
         и  специфицирует множество переменных, называемых элементами сов-
         мещения, которые могут быть различных типов. Переменная  с  типом
         совмещения  запоминает любую отдельную величину, определяемую на-
         бором элементов совмещения.
               Об"явление совмещения имеет тот же самый синтаксис,  как  и
         об"явление  структуры,  за исключением того, что она начинается с
         ключевого слова union вместо ключевого слова struct. Для об"явле-
         ния совмещения и структуры действуют одни и те же правила, за ис-
         ключением того, что в совмещении не допускаются элементы типа би-
         товых полей.
               
               Память, которая соответствует переменной  типа  совмещение,
         определяется  величиной для размещения любого отдельного элемента
         совмещения.
               Когда используется наименьший элемент совмещения, то  пере-
         менная типа совмещения может содержать неиспользованное простран-
         ство. Все элементы совмещения запоминаются в одном и том же прос-
         транстве  памяти  переменной,  начиная с одного и того же адреса.
         Запомненные значения затираются каждый раз,  когда  присваивается
         значение очередного элемента совмещения.
                
               Примеры:
     
                /************** Example 1 ********************/
                
                union sign {
                        int svar;
                        unsigned uvar;
                   } number;
                
                /************** Example 2 ********************/
                
                union {
                        char *a, b;
                        float f[20];
                    } jack;
                
                /*************** Example 2 *******************/
                
                union {
                        struct {
                                 char icon;
                                 unsigned color : 4;
                        } window1, window2, window3, window4;
                   } screen[25][80];
                
               В  первом  примере  об"является переменная типа совмещения,
         поименованная number. Список элементов совмещения состоит из двух

                                      - 46 -
         
         
         об"явлений переменных: svar типа int и uvar  типа  unsigned.  Это
         об"явление  позволяет запоминать текущее значение number в знако-
         вом или беззнаковом виде. Тип совмещения поименован идентификато-
         ром sign.
               Во втором примере об"является переменная типа совмещения  с
         именем jack. Список элементов об"явления состоит из трех об"явле-
         ний:  указателя a на величину типа char, переменной b типа char и
         массива f из 20 элементов типа float. Тип совмещения не  поимено-
         ван.
               Память,  распределенная  для переменной jack, равна памяти,
         распределенной под массив f, поскольку f  самый  большой  элемент
         совмещения.
               В третьем примере об"является двумерный массив совмещений с
         именем  screen.  Массив  состоит из 2000 об"ектов. Каждый об"ект-
         это отдельное совмещение из четырех элементов: window1,  window2,
         window3,  window4, где каждый элемент- это структура. В любое за-
         данное время каждый об"ект совмещения поддерживается одним из че-
         тырех возможных элементов типа структура. Таким образом, перемен-
         ная screen- это композиция четырех возможных "windows".
         
               
               4.4.5. Об"явление массива
                
               Синтаксис:
                
                [];
                
                [];
                
               Здесь квадратные скобки- это терминальные символы. Об"явле-
         ние массива определяет тип массива и тип  каждого  элемента.  Оно
         может определять также число элементов в массиве. Переменная типа
         массив  рассматривается как указатель на элементы массива. Об"яв-
         ление массива может представляться в двух синтаксических  формах,
         указанных  выше.  Декларатор  задает  имя переменной.
         Квадратные скобки, следующие за декларатором, модифицируют декла-
         ратор     на     тип     массива.      Константное      выражение
         , заключенное в квадратные скобки, определя-
         ет  число элементов в массиве. Каждый элемент имеет тип, задавае-
         мый спецификатором типа , который может  специфи-
         цировать любой тип, исключая void и тип функции.
               Во  второй синтаксической форме опущено константное выраже-
         ние в квадратных скобках. Эта форма может быть использована толь-
         ко тогда, когда массив инициализируется или об"явлен как формаль-
         ный параметр или об"явлен как ссылка на массив, явно определенный
         где-то в программе.
               Массив массивов или многомерный массив  определяется  путем
         задания списка константных выражений в квадратных скобках, следу-
         щего за декларатором:
                
               []
                                           []...
                
               Каждое константное выражение-  в квад-
         ратных скобках определяет число  элементов  в  даннном  иэмерении

                                      - 47 -
         
         
         массива, так что об"явление двумерного массива содержит два конс-
         тантных  выражения, трехмерного- три и т.д. Если многомерный мас-
         сив об"является внутри функции или если он инициализируется  либо
         об"является как формальный параметр или об"является как ссылка на
         массив, явно определенный где- то в программе, то первое констан-
         тное выражение может быть опущено.
               Массив  указателей  на  величины,заданного типа, может быть
         определен посредством составного декларатора, как было описано  в
         разделе 4.3.2.
               Типу  массив  соответствует  память,  которая требуется для
         размещения всех его элементов. Элементы массива с первого до пос-
         леднего запоминаются в последовательных возрастающих адресах  па-
         мяти. Между элементами массива в памяти разрывы отсутствуют. Эле-
         менты  массива  запоминаются  друг за другом построчно. Например,
         массив, содержащий две строки с тремя столбцами каждая,
                
                                  char A[2][3]
                
               будет запомнен следующим образом. Сначала запоминаются  три
         столбца  первой строки, затем элементы трех столбцов второй стро-
         ки. Смысл этого в том, чтобы последний индекс был более  быстрым.
         Чтобы  сослаться на отдельный элемент массива, нужно использовать
         индексное выражение, которое описано в разделе 5.2.5.
           
               Примеры:
                
                /*************** Example 1 ******************/
                
                int scores[10], game;
                
                /*************** Example 2 ******************/
                
                float matrix[10][15];
                
                /*************** Example 3 ******************/
                
                struct {
                            float x,y;
                            } complex[100];
                
                /*************** Example 4 *******************/
                
                char *name[20];
                
               В первом примере об"является переменная типа массив с  име-
         нем  scores  из  10  элементов типа int. Переменная с именем game
         об"явлена как простая переменная целого типа.
               Во втором примере об"является  двумерный  массив  с  именем
         matrix. Массив состоит из 150-ти элементов типа float.
               В  третьем примере об"является массив структур. Массив сос-
         тоит из 100 об"ектов. Каждый об"ект  массива  представляет  собой
         структуру, состоящую из двух элементов.
               В четвертом примере об"явлен массив указателей. Массив сос-
         тоит из 20-ти элементов, каждый из которых является указателем на
         величину типа char.

                                      - 48 -
         
         
         
               
               4.4.6. Об"явление указателей
                
               Синтаксис:
                
                 *;
                
               Об"явление  указателя определяет имя переменной типа указа-
         тель и тип об"екта, на который указывает эта переменная. Деклара-
         тор-  определяет имя переменной с возможной модифика-
         цией ее типа. Спецификатор типа-    задает  тип
         об"екта,  который  может  быть  базового типа, типа структуры или
         совмещения.
               Переменная типа указатель может указывать также на функции,
         массивы и другие указатели. Более полная информация о типах  ука-
         зателей дана в разделе 4.3.2. "Составные деклараторы".
               Если указатель не используется до определения типа структу-
         ры  или совмещения, то он может быть об"явлен ранее этого опреде-
         ления. Такие об"явления  допускаются,  поскольку  компилятору  не
         требуется знать размера структуры или совмещения, чтобы распреде-
         лить  память  под переменную типа указатель. Указатель может быть
         об"явлен посредством использования тега структуры или  совмещения
         (смотри ниже пример 4).
               Переменная, об"явленная как указатель, хранит адрес памяти.
         Размер  памяти,  требуемый  для адреса, и смысл адреса зависит от
         данной конфигурации машины. Указатели на различные типы не обяза-
         тельно имеют одну и ту же длину.
               Для некоторых реализаций используются специальные  ключевые
         слова  near,  far  и huge, чтобы модифицировать размер указателя.
         Об"явления, использующие специальные ключевые слова, были описаны
         в разделе 4.3.3. Информация о смысле ключевых слов дана в систем-
         ной документации.
                
               Примеры:
                
               char *message;                      /* Example 1 */
                
               int *pointers[10];                  /* Example 2 */
                
               int (*pointer)[10];                 /* Example 3 */
                
               struct list *next, *previous;       /* Example 4 */
                
               struct list {                       /* Example 5 */
                            char *token;
                            int count;
                            struct list *next;
                       } line;
                
               struct id {                         /* Example 6 */
                        unsigned int id_no;
                        struct name *pname;
                    } record;
               

                                      - 49 -
         
         
               В первом примере об"является переменная- указатель поимено-
         ванная message. Она указывает на величину типа char.
               Во втором примере об"явлен массив указателей, поименованный
         pointers. Массив состоит из 10  элементов.  Каждый  элемент-  это
         указатель на переменную типа int.
               В третьем примере об"явлена переменная- указатель, поимено-
         ванная  pointer.  Она указывает на массив из 10 элементов. Каждый
         элемент в этом массиве имеет тип int.
               В четвертом примере об"явлены две переменныхуказателя,  ко-
         торые ссылаются на величины структурного типа list (смотри следу-
         ющий  пример). Определение типа с именем list должно находиться в
         пределах видимости об"явления.
               В пятом  примере  об"является  переменная  с  именем  line,
         структурного  типа,  поименованного  list. Тип структуры с именем
         list определяется тремя элементами. Первый  элементэто  указатель
         на  величину  типа  char, второй- на величину типа int, а третий-
         это указатель на следующую структуру типа list.
               В шестом примере об"является переменная  с  именем  record,
         имеющая  тип структуры с именем id. Заметим, что третий элемент с
         именем pname об"явлен как указатель на  другой  тип  структуры  с
         именем  name.  Это  об"явление  может  появиться перед об"явление
         структуры с именем name.
         
               
               4.5. Об"явление функций
                
               Синтаксис:
                
      []([])[,...];
                
               Об"явление функции определяет имя, тип возврата функции  и,
         возможно,  типы  и  число ее аргументов. Об"явление функции также
         называется forward- об"явлением. Декларатор функции об"являет имя
         функции, а спецификатор типа задает тип возврата. Если специфика-
         тор  типа  опущен  в  об"явлении  функции, то предполагается, что
         функция возвращает величину типа int.
               Об"явление функции может включать спецификаторы класса  па-
         мяти extern или static.
                
               Список типов аргументов.
                
               Список типов аргументов-  определяет число и
         типы аргументов функции. Синтаксис списка аргументов следующий:
                
               [,...]
                
               Список  имен типов- это список из одного или более имен ти-
         пов. Каждое имя типа отделяется от другого  запятой.  Первое  имя
         типа  задает  тип  первого  аргумента, второе имя типа задает тип
         второго аргумента и т. д. Если список  имен  типов  заканчивается
         запятой с многоточием (,...), то это означает, что число аргумен-
         тов  функции переменно. Однако, предполагается, что функция будет
         иметь не меньше аргументов, чем имен типов, предшествующих много-
         точию.
               Если  список  типов  аргументов-    содержит

                                      - 50 -
         
         
         только многоточие (...), то число аргументов функции является пе-
         ременным или равно нулю.
                
               Замечание:
                
               Чтобы  поддержать  совместимость  с  программами предидущих
         версий, компилятор допускает символ запятой без многоточия в кон-
         це списка типов аргументов для обозначения их переменного  числа.
         Запятая  может быть использована и вместо многоточия для об"явле-
         ния нуля или более аргументов функции. Использование запятой под-
         держивается только для  совместимости.  Использование  многоточия
         рекомендуется для нового представления.
                
               Имя  типа-  для типов структуры, совмещения или
         базового типа состоит из спецификатора этого типа (такого как int
         ). Имена типов для указателей, массивов и функций формируются пу-
         тем комбинации спецификатора типа с  "абстрактным  декларатором".
         Абстрактный декларатор- это декларатор без идентификатора. В раз-
         деле  4.9 "Имена типов" об"ясняется, каким об"разом формировать и
         интерпретировать абстрактные деклараторы.
               Для того чтобы об"явить функцию, не имеющую аргументов, мо-
         жет быть использовано специальное ключевое слово  void  на  месте
         списка  типов аргументов. Компилятор вырабатывает предупреждающее
         сообщение, если в вызове такой функции будут специфицированы  ар-
         гументы.
               Еще одна специальная конструкция допускается в списке типов
         аргументов. Это фраза void *, которая специфицирует аргумент типа
         указатель. Эта фраза может быть использована в списке типов аргу-
         ментов вместо имени типа.
               
               Список  типов  аргументов  может быть опущен. В зтом случае
         скобки после идентификатора функции все же требуются, хотя они  и
         пусты.  В этом случае в об"явлении функции не определяются ни ти-
         пы, ни число аргументов в функции. Когда эта информация опускает-
         ся, то компилятор не проверяет соответствия между  формальными  и
         фактическими  параметрами при вызове функции. Более подробная ин-
         формация дана в разделе 7.4 "Вызовы функций".
                
               Тип возврата
                
               Функции могут возвращать величины любого типа за исключени-
         ем массивов и функций. Для этого посредством спецификатора  типа-
         "type-specifier" в об"явлении функции можно специфицировать любой
         тип:  основной,  структуру  или совмещение. Идентификатор функции
         может быть модифицирован одной или несколькими  звездочками  (*),
         чтобы об"явить возвращаемую величину типа указателя.
               Хотя функции и не допускают возвратов массивов или функций,
         но  они  могут возвращать указатели на массивы или функции. Функ-
         ции, которые возвращают указатели на  величины  типа  массив  или
         функция, об"являются посредством модификации идентификатора функ-
         ции  квадратными  скобками, звездочкой и круглыми скобками, чтобы
         сформировать составной декларатор. Формирование  и  интерпретация
         составных деклараторов рассматривались в разделе 4.3.2.
                 

                                      - 51 -
         
         
               Примеры:
                       
               int add(int, int);                  /* Example 1 */
                
               double calc();                      /* Example 2 */
                
               char *strfind(char *,...);          /* Example 3 */
                
               void draf(void);                    /* Example 4 */
                
               double (*sum(double, double)) [3];  /* Example 5 */
                
               int (*select(void)) (int) ;         /* Example 6 */
                
               char *p;                            /* Example 7 */
               short *q;
               int prt(void *);
                
               В  первом  примере  об"является функция, поименованная add,
         которая требует два аргумента типа int и возвращает величину типа
         int.
                                     
               Во  втором примере об"является функция, поименованная calc,
         которая возвращает величину типа double. Список типов  аргументов
         не  задан.  В  третьем примере об"является функция, поименованная
         strfind, которая возвращает указатель на величину типа char. Фун-
         кция требует, по крайней мере один аргументуказатель на  величину
         типа char. Список типов аргументов заканчивается запятой с много-
         точием, обозначающим, что функция может потребовать большее число
         аргументов.
               В  четвертом  примере  об"является функция с типом возврата
         void (нет возвращаемой величины). Список типов  аргументов  также
         void, означающий отсутствие аргументов для этой функции.
               В  пятом  примере sum об"является как функция, возвращающая
         указатель на массив из трех величин типа double. Функция sum тре-
         бует два аргумента, каждый из  которых  является  величиной  типа
         double.
               В  шестом  примере функция, поименованная select, об"явлена
         без аргументов и возвращает указатель на функцию. Указатель возв-
         рата ссылается на функцию, требующую один  аргумент  типа  int  и
         возвращающую величину типа int.
               В  седьмом  примере  об"явлена функция prt, которая требует
         аргумент- указатель любого типа, и  которая  возвращает  величину
         типа  int. Любой указатель p или q могли бы быть использованы как
         аргументы функции без выдачи при этом предупреждающего сообщения.
         
               
               4.6. Классы памяти
                
               Класс памяти  переменной,  которая  определяет  какой  либо
         об"ект, имеет глобальное или локальное время жизни. Об"ект с гло-
         бальным  временем жизни существует и имеет значение на протяжении
         всей программы. Все функции имеют глобальное время жизни.
               Переменные с локальным временем жизни захватывают новую па-
         мять при каждом выполнении блока, в котором они определены. Когда
         управление на выполнение передается из блока, то переменная теря-

                                      - 52 -
         
         
         ет свое значение.
               Хотя Си определяет два типа классов памяти, но, тем не  ме-
         нее, имеется следующих четыре спецификатора классов памяти:
                
               auto
               register
               static
               extern
               
               Об"екты классов auto и register имеют локальное время  жиз-
         ни. Спецификаторы static и extern определяют об"екты с глобальным
         временем  жизни. Каждый из спецификаторов класса памяти имеет оп-
         ределенный смысл, который влияет на видимость функций и  перемен-
         ных  в  той же мере, как и сами классы памяти. Термин "видимость"
         относится к той части программы, в которой могут  ссылаться  друг
         на друга функции и переменные. Об"екты с глобальным временем жиз-
         ни существуют на протяжении выполнения исходной программы, но они
         могут  быть  видимы не во всех частях программы. Видимость и свя-
         занная с ней концепция времени жизни рассмотрена в разделе 3.5.
               Месторасположение об"явления переменной или функции  внутри
         исходных  файлов  также влияют на класс памяти и видимость. Гово-
         рят, что об"явления вне определения всех функций и переменных от-
         носятся к внешнему уровню, а об"явления внутри определений  функ-
         ций относятся к внутреннему уровню.
               Точный смысл каждого спецификатора класса памяти зависит от
         того,  находится ли об"явление на внешнем или внутреннем уровне и
         от того, об"явлен ли об"ект функцией или переменной. В  следующем
         разделе  описывается  смысл спецификаторов класса памяти в каждом
         случае об"явления, а также  об"ясняется  режим  умолчания,  когда
         спецификатор  класса  памяти опущен при об"явлении переменной или
         функции.
         
               
               4.6.1. Об"явления переменной на внешнем уровне
                
               Об"явления переменной на внешнем уровне используют специфи-
         каторы класса памяти static и extern или совсем опускают их. Спе-
         цификаторы класса памяти auto и register не допускаются на  внеш-
         нем уровне.
               Об"явления  переменных  на  внешнем уровне- это определения
         переменных или ссылки на определения, сделанные в другом месте.
               Об"явление внешней переменной, которое  инициализирует  эту
         переменную  (явно или неявно), называется определением этой пере-
         менной. Определение на внешнем уровне может задаваться в  следую-
         щих различных формах:
                
               -переменная  на  внешнем уровне может быть определена путем
         ее об"явления со спецификатором класса памяти static. Такая пере-
         менная может быть явно инициализирована  константным  выражением.
         Если  инициализатор отсутствует, то переменная автоматически ини-
         циализируется нулем во время компиляции. Таким образом,  об"явле-
         ния  static  int  k = 16; и static int k; оба рассматриваются как
         определения;
                
               -переменная определяется, когда она  явно  инициализируется

                                      - 53 -
         
         
         на  внешнем уровне. Например, int j = 3; это определение перемен-
         ной.
                
               Так как переменная определяется на внешнем уровне,  то  она
         видима  в пределах остатка исходного файла, от места, где она оп-
         ределена. Переменная не видима выше своего определения в  том  же
         самом  исходном файле ни в других исходных файлах программы, если
         не об"явлена ссылка, которая делает ее видимой.
               Переменная может быть определена на внешнем  уровне  внутри
         исходного файла только один раз. Если задается спецификатор клас-
         са  памяти static, то в других исходных файлах могут быть опреде-
         лены переменные с тем  же  именем.  Так  как  каждое  определение
         static  видимо  только  в  пределах своего собственного исходного
         файла, то конфликта не возникнет.
               Спецификатор класса памяти extern используется для об"явле-
         ния ссылки на переменную, определенную где-то в другом месте. Та-
         кие об"явления используются в случае, когда нужно сделать видимым
         определение переменной в других исходных файлах или  выше  места,
         где  она определена в том же самом исходном файле. Так как ссылка
         на переменную об"явлена на внешнем уровне, то переменная видима в
         пределах остатка исходного файла от места об"явления ссылки.
               В об"явлениях, которые используют спецификатор класса памя-
         ти extern, инициализация не допускается, так как они ссылаются на
         переменные, чьи величины уже определены.
               Переменная, на которую делается ссылка extern, должна  быть
         определена  на  внешнем уровне только один раз. Определение может
         быть сделано в любом из исходных файлов, составляющих программу.
               Есть одно исключение из правил, описанных выше. Можно опус-
         тить из об"явления  переменной  на  внешнем  уровне  спецификатор
         класса  памяти и инициализатор. Например, об"явление int n; будет
         правильным внешним об"явлением. Это об"явление имеет два  различ-
         ных смысла в зависимости от контекста.
                
               1.  Если где-нибудь в программе будет определена на внешнем
         уровне переменная с тем же именем, то об"явление является ссылкой
         на эту переменную, как если бы был использован спецификатор клас-
         са памяти extern в об"явлении.
                
               2.  Если  нет такого определения, то об"явленной переменной
         распределяется память во время линкования и переменная  инициали-
         зируется  нулем.  Если  в программе появится более чем одно такое
         об"явление, то память распределится для  наибольшего  размера  из
         об"явленных переменных. Например, если программа содержит два не-
         инициализированных  об"явления переменной i на внешнем уровне int
         i; и char i; то память во время линкования распределится под  пе-
         ременную i типа int.
               
                
               Неинициализированные об"явления переменной на внешнем уров-
         не  не  рекомендуются  для файлов, которые могут быть размещены в
         библиотеку.
                

                                      - 54 -
         
               Пример:
                
                /*****************************************************
         
                                    SOURCE FILE ONE
                *****************************************************/
           
                extern int i;                 /* reference to i
                                                 defined below */
                
                main()
                {
                        i++;
                        printf("%d\n", i);    /* i equals 4 */
                        next();
                }
                
                int i = 3;                    /* definition of i */
                
                next()
                {
                        i++;
                        printf("%d\n", i);    /* i equals 5 */
                        other();
                }
                
                /*****************************************************
                                SOURCE FILE TWO
                *****************************************************/
                
                extern int i;                 /* reference to i in
                                                 first source file */
                
                other()
                {
                        i++;
                        printf("%d\n", i);    /* i equals 6 */
                }
                
               Два исходных файла  в  совокупности  содержат  три  внешних
         об"явления  i. Одно об"явление содержит инициализацию- int i = 3;
         , где глобальная переменная i определена  с  начальным  значением
         равным 3.
               Самое  первое  об"явление extern в первом файле делает гло-
         бальную переменную видимой выше ее определения в файле.
               Без об"явления extern функция main не смогла  бы  сослаться
         на  глобальную  переменную  i.  Об"явление extern переменной i во
         втором исходном файле делает глобальную переменную видимой в этом
         исходном файле.
               Все три функции выполняют одну и ту же задачу: они увеличи-
         вают i на 1 и печатают  получившееся  значение.  (Предполагается,
         что функция printf определена где-то еще в программе.). Печатают-
         ся величины равные 4, 5 и 6.
               Если бы переменная i не была бы инициализирована,она бы бы-
         ла  автоматически  установлена  в 0 при линковании. В этом случае
         напечатанные значения были бы равны 1, 2 и 3.
               
               

                                      - 55 -
         
               4.6.2. Об"явление переменной на внутреннем уровне
         
                
               Любой из четырех спецификаторов класса  памяти  может  быть
         использован  для об"явления переменной на внутреннем уровне. Если
         спецификатор класса памяти опускается в об"явлении переменной  на
         внутреннем уровне, то подразумевается класс памяти auto.
               Спецификатор  класса памяти auto об"являет переменную с ло-
         кальным временем жизни. Переменная видима только в том блоке, где
         она об"явлена. Об"явления переменных auto могут включать  инициа-
         лизаторы.  Переменные класса памяти auto автоматически не инициа-
         лизируются, а инициализируются явно при об"явлении или присваива-
         нии начальных значений, посредством операторов внутри блока. Если
         нет инициализации, то величина переменной auto считается  неопре-
         деленной.
               Спецификатор  класса памяти register сообщает компилятору о
         том, чтобы он распределил память под переменную в регистре,  если
         это  возможно. Использование регистровой памяти обычно приводит к
         более быстрому времени доступа и к меньшему размеру  результирую-
         щего кода. Переменные, об"явленные с классом памяти register име-
         ют ту же самую видимость, что и переменные auto.
               Число регистров, которое может быть использовано под память
         переменных,  зависит от машины. Когда компилятор встречает специ-
         фикатор класса памяти register в об"явлении, а свободного регист-
         ра не имеется, то для  переменной  распределяется  память  класса
         auto.  Компилятор  назначает  переменным регистровую память в том
         порядке, в котором появляются об"явления в исходном файле. Регис-
         тровая память, если она имеется, гарантирована только для  целого
         и адресного типов.
               Переменная,  об"явленная на внутреннем уровне со специфика-
         тором класса памяти static,имеет глобальное время жизни  и  имеет
         видимость только внутри блока, в котором она об"явлена. В отличие
         от переменных auto, переменные, об"явленные как static, сохраняют
         свое значение при завершении блока.
               Переменные класса памяти static могут быть инициализированы
         константным выражением. Если явной инициализации нет, то перемен-
         ная  класса памяти static автоматически устанавливается в 0. Ини-
         циализация выполняется один раз во время компиляции.  Инициализа-
         ция переменной класса памяти static не повторяется при новом вхо-
         де в блок.
               Переменная,  об"явленная  со  спецификатором  класса памяти
         extern, является ссылкой на переменную с тем же самым именем, оп-
         ределенную на внешнем уровне в любом исходном файле программы.
               Цель внутреннего об"явления extern  состоит  в  том,  чтобы
         сделать  определение  переменной  внешнего  уровня видимой внутри
         блока. Внутреннее об'явление extern не  изменяет  видимость  гло-
         бальной переменной в любой другой части программы.
                
               Пример:
                
               int i = 1;
                
               main()
                {       /* reference to i, defined above */
                        extern int i;
                
                        /* initial value is zero; a is

                                      - 56 -
         
         
                           visible only within main */
                        static int a;
                
                        /* b is stored in a register, if possible */
                        register int b = 0;
                
                        /* default storage class is auto */
                        int c = 0;
                
                        /* values printed are 1, 0, 0, 0 */
                        printf("%d\n%d\n%d\n%d\n", i, a, b, c);
                        other();
                }
                other()
                {
                        /* i is redefined */
                        int i = 16;
                
                        /* this a is visible only within other */
                        static int a = 2;
                        a += 2;
                        /* values printed are 16, 4 */
                        printf("%d\n%d\n", i, a);
                }
                
               Переменная i определяется на внешнем уровне с инициализаци-
         ей  1.  В  функции  main  об"явлена ссылка extern на переменную i
         внешнего уровня. Переменная класса  памяти  static  автоматически
         устанавливается  в 0, так как инициализатор опущен. Вызов функции
         print (предполагается, что функция print  определена  в  каком-то
         месте исходной программы.) печатает величины 1, 0, 0, 0.
               В  функции other, переменная i переопределяется как локаль-
         ная переменная с начальным значением 16. Это не влияет на  значе-
         ние внешней переменной i. Переменная a об"является как переменная
         класса памяти static с начальным значением 2. Она не противоречит
         переменной a, об"явленной в функции main, так как видимость пере-
         менных  класса памяти static на внутреннем уровне ограничена бло-
         ком, в котором она об"явлена.
               Значение переменной увеличивается на 2 и становится  равным
         4.  Если бы функция other была вызвана снова в той же самой прог-
         рамме, то начальное значение a стало бы равным 4. Внутренние  пе-
         ременные  класса памяти static сохраняют свои значения, когда за-
         канчивается выполнение блока, в котором они об"явлены.
         
               
               4.6.3. Об"явление функции на внешнем и внутреннем уровнях
                
               Функции могут быть об"явлены со спецификаторами класса  па-
         мяти  static  или  extern.  Функции всегда имеют глобальное время
         жизни.
               Правила видимости для функций отличаются от правил видимос-
         ти для переменных. Об"явления функций на внутреннем уровне  имеют
         тот  же самый смысл, что и об"явления на внешнем уровне. Это зна-
         чит, что функции не могут иметь  блочной  видимости  и  видимость
         функций  не может быть вложенной. Функция об"явленная как static,

                                      - 57 -
         
         
         видима только в пределах исходного файла, в котором она определя-
         ется. Любая функция в том же самом исходном файле  может  вызвать
         функцию  static,  но функции static из других файлов нет. Функция
         static с тем же самым именем может быть об"явлена в другом исход-
         ном файле.
               Функции, об"явленные как extern видимы в пределах всех  ис-
         ходных  файлов, которые составляют программу. Любая функция может
         вызвать функцию extern.
               Об"явления функций, в которых  опущен  спецификатор  класса
         памяти, считаются по умолчанию extern.
         
               
               4.7. Инициализация
                
               В об"явлении переменной может быть присвоено начальное зна-
         чение посредством инициализатора. Величина или величины инициали-
         затора присваиваются переменной.
               
               Синтаксически, записи инициализатора предшествует знак рав-
         но (=)
                
                =
                
               Могут быть инициализированы переменные любого типа. Функции
         не  инициализируются. Об"явления, которые используют спецификатор
         класса памяти extern не могут содержать инициализатора.
               Переменные, об"явленные на внешнем уровне, могут быть  ини-
         циализированы. Если они явно не инициализированы, то они устанав-
         ливаются  в  нуль во время компиляции или линкования. Любая пере-
         менная, об"явленная со спецификатором класса памяти static, может
         быть инициализирована константным выражением. Инициализация пере-
         менных класса static выполняется один раз  во  время  компиляции.
         Если отсутствует явная инициализация, то переменные класса памяти
         static автоматически устанавливаются в нуль.
               Инициализация переменных auto и register выполняется каждый
         раз при входе в блок, в котором они об"явлены. Если инициализатор
         опущен  в  об"явлении переменной класса памяти auto или register,
         то начальное значение  переменной  не  определено.  Инициализация
         составных  типов  auto (массив, структура, совмещение) запрещена.
         Любое составное об"явление класса памяти static может быть иници-
         ализировано на внешнем уровне.
               Начальными значениями для внешних об"явлений  переменной  и
         для  всех  переменных  static как внешних так и внутренних должно
         быть константное выражение. Автоматические и регистровые перемен-
         ные могут быть инициализированы константными или переменными  ве-
         личинами.
               
                
               4.7.1. Базовые типы и типы указателей
                
               Синтаксис:
                 
               =
                
               Величина  выражения присваивается переменной. Для выражения

                                      - 58 -
         
         
         допустимы правила преобразования.
               Примеры:
                
                int x = 10;                    /* Example 1 */
                
                register int *px = 0;          /* Example 2 */
                
                int c = (3 * 1024);            /* Example 3 */
                
                int *b = &x;                   /* Example 4 */
                
               В первом примере x инициализируется константным  выражением
         10.  Во втором примере, указатель px инициализирован нулем, в ре-
         зультате чего получился "null" указатель. В третьем  примере  ис-
         пользуется константное выражение для инициализации c. В четвертом
         примере инициализируется указатель b адресом другой переменной x.
         
               
               4.7.2. Составные типы
                
               Синтаксис:
                
               ={}
                
               Список  инициализаторов  - это последова-
         тельность инициализаторов, разделенных запятыми. Каждый инициали-
         затор в последовательности- это либо константное выражение,  либо
         список  инициализаторов.  Поэтому,  заключенный в фигурные скобки
         список, может появиться внутри другого списка инициализации.  Эта
         конструкция  используется  для  инициализации элементов составных
         конструкций.
               Для каждого списка инициализации значения константных выра-
         жений присваиваются в порядке следования элементов составной  пе-
         ременной.  Когда инициализируется совмещение, то список инициали-
         заторов представляет собой  единственное  константное  выражение.
         Величина  константного  выражения  присваивается первому элементу
         совмещения.
               Если в списке инициализации меньше величин, чем их  имеется
         в  составном  типе,  то оставшиеся памяти инициализируются нулем.
         Если число инициализирующих величин больше чем требуется, то  вы-
         дается ошибка.
               Эти правила применяются к каждому вложенному списку инициа-
         лизаторов, точно так же как и ко всей конструкции в целом.
                
               Пример:
                
               int p[4] [3] = {
                         { 1, 1, 1 },
                         { 2, 2, 2 },
                         { 3, 3, 3,},
                         { 4, 4, 4,},
                };
                
               В  примере  об"является  массив p размерности 4 строки на 3
         столбца. Элементы первой строки инициализируются 1, второй строки

                                      - 59 -
         
         
         2 и т. д. Заметим, что списки инициализаторов третьей и четвертой
         строк заканчиваются запятой. Последний список  инициализаторов  {
         4, 4, 4,} также заканчивается запятой.
               Эти  дополнительные  запятые  допускаются, но не требуются.
         Требуются только те запятые, которые разделяют константные  выра-
         жения  и  списки  инициализации.  Если  список инициализаторов не
         структурирован под составной об"ект, то его величины присваивают-
         ся в том порядке, в котором подстыкованы элементы об"екта. Поэто-
         му вышеприведенная инициализация эквивалентна следующей:
                
               int p[4] [3] = {
                         1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4
                };
                 
               Фигурные скобки могут также появляться вокруг  индивидуаль-
         ных инициализаторов в списке.
               Когда инициализируются составные переменные, то нужно поза-
         ботиться  о  том,  чтобы правильно использовать фигурные скобки и
         списки инициализаторов. В следующем примере иллюстрируется  более
         детально интерпретация компилятором фигурных скобок.
                
               typedef struct {
                    int n1, n2, n3;
                } triplet;
                
                triplet nlist[2] [3] = {
                 { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } },  /* Line 1 */
                 { { 10,11,12}, { 13,14,15}, { 15,16,17} }   /* Line 2 */
                };
                
               В  примере nlist об"является как массив структур, состоящий
         из двух строк и трех столбцов. Каждая структура состоит  из  трех
         элементов.  Первая строка инициализации назначает величины первой
         строке массива nlist следующим образом:
                
               1. Первая левая фигурная скобка Line 1 информирует компиля-
         тор о том, что это начало  инициализации  первой  строки  массива
         nlist(nlist[0]).
               2. Вторая левая фигурная скобка означает то, что начинается
         инициализация  первого  элемента первой строки массива ( nlist[0]
         [0] ).
               3. Первая правая фигурная скобка сообщает об окончании ини-
         циализации первого элемента- структуры  nlist[0]  [0].  Следующая
         левая  фигурная  скобка  сообщает  о начале инициализации второго
         элемента первой строки nlist[0] [1].
               4. Процесс продолжается до конца Line 1 и заканчивается  по
         последней правой фигурной скобке.
                
               Аналогично, Line 2 назначает величины второй строке массива
         nlist.
               Заметим, что внешние фигурные скобки инициализаторов Line 1
         и  Line 2 требуются. Следующая конструкция, в которой внешние фи-
         гурные скобки опущены будет неверной.
                
                            /* THIS CAUSES AN ERROR */

                                      - 60 -
         
         
                triplet nlist[2] [3] = {
                 { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 },  /* Line 1 */
                 { 10,11,12}, { 13,14,15}, {16,17,18 }   /* Line 2 */
               };
               
               В этом примере первая левая фигурная скобка в Line 1  стар-
         тует  инициализацию  nlist[0],  которая является массивом из трех
         структур. Величины 1, 2,  3  назначаются  трем  элементам  первой
         структуры. Когда встретится правая фигурная скобка (после величи-
         ны  3), инициализация nlist[0] закончится и две оставшиеся струк-
         туры автоматически инициализируются нулем. Аналогично, { 4, 5,  6
         }  инициализирует  первую структуру во второй строке nlist, а ос-
         тавшиеся две структуры nlist[1] установятся в нуль. Когда  компи-
         лятор встретит следующий список инициализации { 7, 8, 9 }, то это
         приведет  к  попытке инициализировать nlist[2]. Так как nlist со-
         держит только две строки, то будет выдано сообщение об ошибке.
                
               Примеры:
                
                /******************* Example 1 *********************/
                
                struct list {
                        int i, j, k;
                        float n[2] [3];
                        } x = {
                               1,
                               2,
                               3,
                               {4.0, 4.0, 4.0}
                       };
                
                /******************* Example 2 *********************/
                
                union {
                       char x[2] [3];
                       int i, j, k;
                       } y = {
                               {'1'},
                               {'4'}
                       };
                
               В первом примере три элемента int структурной переменной  x
         инициализированы  1,  2,  и 3 соответственно. Три элемента первой
         строки массива m инициализированы как 4.0. Элементы второй строки
         инициализированы нулем по умолчанию.
               Во втором примере инициализируется переменная y типа совме-
         щения. Первым элементом совмещения является массив, для  которого
         требуется составной инициализатор. Список инициализации {'1'} за-
         дает величины для первой строки массива. Поскольку в списке всего
         одна  величина, то только первый элемент строки массива инициали-
         зируется символом 1 , а оставшиеся два элемента в строке  инициа-
         лизируются  нулем  (символом  \0)  по умолчанию. Аналогично, первый
         элемент второй строки массива x инициализируется  символом  4,  а
         оставшиеся два элемента в строке инициализируются нулем.
         

                                      - 61 -
         
         
               
               4.7.3. Строковые инициализаторы
                
               Массив может быть инициализирован строчным литералом.
               Например,
                
                char code[ ] = "abc";
                
               инициализирует  code как массив символов из четырех элемен-
         тов. Четвертым элементом является символ \0, который завершает  все
         строковые литералы.
               Если  специфицируется  размер  массива, а строка больше чем
         специфицированный размер, то лишние символы отбрасываются. Следу-
         ющее об"явление инициализирует переменную code, как  трехэлемент-
         ный массив символов:
                
               char code[3] = "abcd"
                
               В примере только три первые символа инициализатора назнача-
         ются для массива code. Символ d и сивол нуль отбрасываются.
               Если  строка  короче, чем специфицированный размер массива,
         то оставшиеся элементы массива инициализируются  нулем  (символом
         \0).
         
               
               4.8. Об"явления типов
                
               Об"явление  типа определяет имя и элементы структурного или
         совмещающего типов или имя и перечислимое множество перечислимого
         типа.
               Имя типа может быть использовано в об"явлениях переменных и
         функций в качестве ссылки на этот тип. Это полезно, когда  многие
         переменные или функции имеют один и тот же тип.
               Об"явление  typedef  определяет спецификатор типа для типа.
         Это об"явление используется для того, чтобы создавать  более  ко-
         роткие  или  более  осмысленные имена типов уже определенных в Си
         или об"явленных пользователем.
         
               
               4.8.1. Типы структур, совмещений и перечислений
                
               Об"явления типов структур, совмещений и перечислений  имеют
         ту же самую общую синтаксическую форму, как и об"явления перемен-
         ных  этих  типов. В об"явлении типа идентификатор переменной опу-
         щен, так как нет переменной которая об"является. Именем  структу-
         ры, совмещения или перечисления является тег.
               В об"явлении типа может появиться список об"явлений элемен-
         тов-        или   список   перечисления-
         , определяющие тип.
               Сокращенная форма об"явления переменной, в котором tag ссы-
         лается на тип, определенный где-то еще, при  об"явлении  типа  не
         используется.
               Примеры:
                
                /******************** Example 1 ********************/

                                      - 62 -
         
         
                
                enum status {
                        loss = -1,
                        bye,
                        tie = 0,
                        win,
                        };
                
                /********************* Example 2 *******************/
                
                struct student {
                        char name[20];
                        int id, claas;
                        };
                
               В  первом примере об"является тип перечисления, поименован-
         ный status. Имя типа может быть использовано в  об'явлениях  пер-
         менных типа перечисления. Идентификатор loss явно устанавливается
         в  -1.  Идентификаторы  bye и tie ассоциируются со значением 0, а
         win принимает значение 1. Во втором примере об"является структур-
         ный тип, поименованный student. Теперь можно  использовать  такое
         об"явление, как struct student employee, чтобы об"явить структур-
         ную переменную employee типа student.
                
                
                
                
               4.8.2. Об"явления typedef
                
               Синтаксис:
                
           typedef [,...];
                
               Об"явления typedef являются аналогом об"явления переменной,
         за исключением того, что ключевое слово typedef заменяет специфи-
         катор класса памяти.
               Об"явление  интерпретируется тем же самым путем, как об"яв-
         ления переменной или функции, но  вместо того,  чтобы
         стать переменной типа, специфицированного об"явлением, становится
         синонимом  имени  типа.  Об"явление typedef не создает типов. Оно
         создает синонимы для существующих имен типов, которые были специ-
         фицированы другим способом.  Любой  тип  может  быть  об"явлен  с
         typedef,  включая типы указателя, функции и массива. Имя с ключе-
         вым словом typedef для типов указателя, структуры или  совмещения
         может  быть  об"явлено прежде чем эти типы будут определены, но в
         пределах видимости об"явления.
                
               Примеры:
                
                /******************** Example 1 ********************/
                
                typedef int WHOLE;
                
                

                                      - 63 -
         
                /******************** Example 2 ********************/
         
                typedef struct club {
                        char name[30];
                        int sise, year;
                        } GROUP;
                
                /******************** Example 3 ********************/
                
                typedef GROUP *PG;
                
                /******************** Example 4 ********************/
                
                typedef void DRAWE(int, int);
                
               В первом примере об"является WHOLE как синоним для int .
               Во втором примере об" является GROUP как структурный тип  с
         тремя  элементами.  Так как специфицирован также тег clab, то имя
         GROUP и тег club могу быть использованы в об"явлениях.
               В  третьем  примере используется предидущее имя typedef для
         об"явления адресного типа. Тип PG об"является  как  указатель  на
         тип GROUP, который в свою очередь определен как структурный тип.
               В  последнем  примере  представлен тип DRAWE для функции не
         возвращающей значения и требующей два аргумента типа int. Это оз-
         начает, например, что об"явление DRAWE box; эквивалентно об"явле-
         нию void box(int, int);
         
               
               4.9. Имена типов
               
               Имя типа специфицирует особенности типа данных. Имена типов
         используются в трех контекстах: в списках типов  аргументов,  при
         об"явлении функций, в вычислениях cast (преобразованиях типов), и
         в  sizeof  операциях.  Списки  типов аргументов рассматривались в
         разделе 4.5. "Об"явления функций". Преобразования cast и операция
         sizeof обсуждаются в разделах  5.7.2.  и  5.3.4.  соответственно.
         Именами  для  основных,  перечисляющих, структурных и совмещающих
         типов являются спецификаторы типа для каждого из них.  Имена  для
         типов  указателя,  массива и функции задаются следующей синтакси-
         ческой формой:
                
                
                
               Абстрактный декларатор - это  деклара-
         тор без идентификатора, состоящий из одного или более модификато-
         ров  указателей,  массивов  и  функций. Модификатор указателя (*)
         всегда появляется перед идентификатором в деклараторе, в то время
         как модификатор массива ([]) или функции ( () ) появляются  после
         идентификатора.  Таким  образом, чтобы правильно интерпретировать
         абстрактный декларатор, нужно начинать интерпретацию с подразуме-
         ваемого идентификатора.
               Абстрактные деклираторы могут  быть  составными.  Скобки  в
         составном абстрактном деклараторе специфицируют порядок интерпре-
         тации,  подобно тому как это делается при интерпретации составных
         деклараторов об"явлений.  Абстрактный  декларатор,  состоящий  из
         пустых  круглых  скобок () не допускается, поскольку это двусмыс-
         ленно. В этом случае невозможно определить находится ли  подразу-

                                      - 64 -
         
         
         меваемый идентификатор внутри скобок, и в таком случае- это немо-
         дифицированный  тип,  или перед скобками, тогда- это тип функции.
         Спецификаторы типа, установленные посредством об"явлений typedef,
         также рассматриваются как имена типов.
               Примеры:
                
               long *                    /* Example 1 */
                
               int (*) [5]               /* Example 2 */
                
               int (*) (void)            /* Example 3 */
                
               В первом примере задано имя типа как указатель на тип long.
               Во втором и третьем примерах показано каким образом  скобки
         модифицируют составные абстрактные деклараторы. В примере 2 зада-
         но  имя типа для указателя на массив иэ пяти злементов. В третьем
         примере именуется указатель на функцию, не требующую аргументов и
         возвращающую значение типа int.


?????? ???????????