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



 

Часть 5

                                      - 96 -
                                         
               
                                   6. ОПЕРАТОРЫ
               
                
                
               6.1. Введение
                
               Операторы Си управляют процессом  выполнения  программы.  В
         Си, как и в других языках программирования, имеются условные опе-
         раторы,  операторы  цикла,  выбора,  передачи управления и т.д. В
         этом разделе описываются операторы Си в алфавитном порядке.  Ниже
         представлен список этих операторов:
               break
               
               continue
               do
               
               for
               goto
               if
               
               return
               switch
               while
               
               Операторы  Си  состоят из ключевых слов, выражений и других
         операторов. В операторах Си допустимы следующие ключевые слова:
                
               break
               default
               for
               return
               case
               dо
               goto
               switch
               continue
               else
               if
               while
                
               Выражениями в операторах Си являются выражения, описанные в
         разделе  5  "Выражения  и присваивания". Операторами, допустимыми
         внутри операторов Си, могут быть  любые  операторы,  описанные  в
         этом разделе. Оператор, который является компонентом другого опе-
         ратора,  называется  "телом"  включающего  оператора.Часто опера-
         тор-тело является составным оператором, состоящим из  одного  или
         более  операторов.  Составной  оператор  ограничивается фигурными
         скобками. Все другие операторы Си заканчиваются точкой с  запятой
         (;).
               Любой  из  операторов Си может быть спереди помечен меткой,
         состоящей из имени  и  двоеточия.  Операторные  метки  опознаются
         только  оператором  goto  и поэтому рассматриваются в разделе 6.8
         "goto и помеченные операторы".
               Порядок выполнения программы Си совпадает с порядком распо-
         ложения  операторов в тексте программы, за исключением тех случа-

                                      - 97 -
           
           
         ев, когда оператор явно передает управление в другую часть  прог-
         рамм .
                
               6.2. Оператор break
                                          
                
               Синтаксис:
               breakk;
                
               Выполнение:
               Оператор  break  прерывает  выполнение  операторов do, for,
         switch или while, в которых он появляется. Управление  передается
         оператору,  следующему  за  прерванным. Появление оператора break
         вне операторов do, for, switch, while приводит к ошибке.
               Внутри вложенных операторов оператор break завершает только
         операторы do, for, switch или while.  Чтобы  передать  управление
         вне вложенной структуры, могут быть использованы операторы return
         и goto.
                
               Пример
                for (i=0; i]
                .
                .
                .
                
               
                []
                .
                .
                .
                }

                                      - 98 -
           
           
                
               Выполнение:
               Действия при выполнении составного оператора состоят в том,
         что выполнение его операторов осуществляется в порядке их появле-
         ния,  за исключением случаев, когда очередной оператор явно пере-
         дает управление в другое место.
               Синтаксические   правила   и   семантика    об"явлений    -
         ,  которые  могут  появиться  в заголовке составного
         оператора, описаны в разделе 4 "Об"явления".
                
                Пример
                if (i>0)  {
                     linne [i]=x;
                     x++;
                     i--;
                }
                
               Типично появление составного оператора в качестве тела дру-
         гого оператора, например, такого, как оператор if. В  приведенном
         примере,  если i больше нуля, то последовательно выполняются опе-
         ратры составного оператора.
                
               Помеченные операторы
                
               Подобно  другим  операторам  Си, любой оператор в составном
         операторе может быть помечен. Поэтому передача управления  внутрь
         составного оператора возможна. Однако, передачи управления внутрь
         составного  оператора  опасны, когда  составной оператор содержит
         об"явления,  которые  инициализируют  переменные.  Об"явления   в
         составном  операторе предшествуют выполняемым операторам, так что
         передача  управления  непосредственно  на  выполняемый   оператор
         внутри составного оператора минует инициализацию. Результат будет
         непредсказуем.
                
                
               6.4. Оператор continue
                
               Синтаксис:
         
               continue;
             
               Выполнение:
               Оператор cоntinue передает управление на следующую итерацию
         в оператoрах цикла do, for, while, в которых он может  появиться.
         Оставшиеся  операторы  в теле   вышеперечисленных циклов при этом
         не выполняются. Внутри do или while циклов следующая итерация на-
         чинается с перевычисления выражения do или while операторов.  Для
         оператора  for  следующая  итерация  начинается с выражения цикла
         оператора for.
                
               Пример:
                while (i-->0)  {
                     x=f(i);
                     if (x==1)
                        continue;

                                      - 99 -
           
           
                     y=x*x;
                }
                
               Тело оператора выполняется , если i>0. Сначала f(i) присва-
         ивается x,  затем,  если  x  равно  1,  то  выполняется  оператор
         continue.  Остальные операторы тела игнорируются и выполнение во-
         зобновляется с заголовка цикла, т.е. вычисляется выражение i-->0.
         
               
               6.5. Оператор do
                
                Синтаксис:
                do
                  
                while ();
                
               Выполнение:
               Тело оператора do выполняется один или несколько раз до тех
         пор, пока выражение   станет  ложным  (равным  нулю).
         Вначале  выполняется оператор  тела, затем вычисляется
         выражение . Если выражение ложно, то оператор do  за-
         вершается и управление передается следующему оператору в програм-
         ме.  Если  выражение  истинно  (не равно нулю), то тело оператора
         выполняется снова и снова проверяется выражение. Выполнение  тела
         оператора  продолжается  до  тех  пор,  пока  выражение не станет
         ложным. Оператор do может также завершить выполнение при выполне-
         нии операторов break, goto или return внутри тела оператора do.
                
               Пример:
                do {
                    y=f(x);
                    x--;
                } while (x>0);
                
               Вначале выполняются два оператора y=f(x); и x--; не обращая
         внимание на начальное значение  x.  Затем  вычисляется  выражение
         x>0. Если x>0, то тело оператора выполняется снова, и снова пере-
         вычисляется выражение x>0. Тело оператора выполняется до тех пор,
         пока x не станет меньше или равным нулю.
                
                
               6.6. Оператор-выражение
                
               Синтаксис:
                
               expression;
                
               Выполнение:
               Выражение   вычисляется в соответствии с прави-
         лами, изложенными в разделе 5 "Выражения и присваивания".
                
               Примеры
               x=(y+3);     / *example 1* /
               x++;         / *example 2* /
               f(x);        / *example 3* /

                                      - 100 -
           
           
                
               В Си присваивания являются выражениями. Значением выражения
         является  значение,   которое   присваивается   (так   называемое
         "право-сторонее значение").
               В первом примере x присваивается значение y+3.
               Во втором примере x инкрементируется.
               В  третьем примере показано выражение функционального вызо-
         ва.Значением выражения является значение, возвращаемое  функцией.
         Если  функция  возвращает  значение, то обычно оператор-выражение
         содержит операцию присваивания, чтобы запомнить значение возврата
         вызванной функции. Еси возвращаемая величина не используется, как
         в данном примере, вызов функции выполняется, но возвращаемая  ве-
         личина (если она есть) не используется.
         
               
               6.7. Оператор for
                
               Синтаксис:
           
         for ([];[];[])
                statement
                
               Тело  оператора  for  выполняется  нуль и более раз, до тех
         пор, пока условное выражение  не станет  ложным.
         Выражения     инициализации          и     цикла
          могут быть использованы для инициализации и мо-
         дификации величин во время выполнения оператора for.
               Первым шагом при выполнении оператора for является вычисле-
         ние выражения инициализации, если оно имеется.  Затем  вычисление
         условного выражения с тремя возможными результатами:
               1.  Если  условное  выражение  истинно  (не равно нулю), то
         выполняется тело оператора.  Затем  вычисляется  выражение  цикла
         (если оно есть). Процесс повторяется снова с вычислением условно-
         го выражения.
               2.  Если условное выражение опущено, то его значение прини-
         мается за истину и процесс выполнения продолжается, как  показано
         выше.  В  этом  случае  оператор for может завершиться только при
         выполнении в теле оператора операторов break, goto, return.
               3. Еси условное выражение ложно,  то  выполнение  оператора
         for  заканчивается и управление передается следующему оператору в
         программе.
               Оператор for может завершиться  при  выполнении  операторов
         break, return, goto в теле оператора.
                
               Пример
               for (i=space=tab=0; i;
               .
               .
               .
               : 
                
               Выполнение:
                                     
               Оператор goto передает управление непосредственно на опера-
         тор,  помеченный  .  Помеченный  оператор выполняется сразу
         после выполнения оператора goto. Если оператор  с  данной  меткой
         отсутствует или существует более одного оператора, помеченных од-
         ной  и  той  же  меткой, то это приводит к ошибочному результату.
         Метка оператора имеет отношение только к оператору goto. Если по-
         меченный оператор встречается в любом другом контексте, то он вы-
         полняется без учета метки.
                
               Пример
               if (errorcode>0)
                   goto exit;
               .
               .
               .
               exit:return (errorcode);
                
               В  примере  оператор  goto передает управление на оператор,
         помеченный меткой exit, когда происходит ошибка.
                
               Формат меток
                
               Метка  -  это  простой  идентификатор,  синтаксис  которого
         описан  в разделе 2.4. Каждая метка должна быть отлична от других
         меток в той же самой функции.
                
               6.9. Оператор if
                
               Синтаксис:
                
               if ()
                   
               [else

                                      - 102 -
           
           
                   ]
                
               Выполнение:
               Тело оператора if выполняется селективно, в зависимости  от
         значения  выражения  . Сначала вычисляется выражение.
         Если значение выражения истина (не нуль), то выполняется оператор
         . Если  выражение  ложно,  то  выполняется  оператор
         , непосредственно следующий за ключевым словом else.
         Если выражение  ложно и предложение else ... опущено,
         то  управление  передается на выполнение оператора, следующего за
         оператором if.
                
               Пример
               if (i>0)
                     y=x/i;
                                    
               else {
                     x=i;
                     y=f(x);
                }
                
               В примере выполняется оператор y=x/i;, если i больше  нуля.
         Если  i  меньше  или  равно  нулю,  то  значение  i присваивается
         переменной x и возврат функции f(x) присваивается переменной y.
                
               Вложения
                
               Си не поддерживает оператор "else  if",  но  тот  же  самый
         эффект  достигается посредством сложенных операторов if. Оператор
         if может быть вложен в предложение if или предложение else друго-
         го оператора if. Когда операторы if вкладываются, то используются
         фигурные скобки, чтобы сгруппировать составные операторы, которые
         проясняют ситуацию.
               Если  фигурные  скобки  отсутствуют,  то  компилятор  может
         принять неверное решение, сочетая каждое else с более близким if,
         у которого отсутсвует else.
                
               Примеры
                
               /****** example 1 ******/
               if (i>0) /* without braces */
                     if (j>i)
                          x=j;
                     else
                          x=i;
                
               /****** example 2 ******/
                if (i>0) {     /* with braces */
                     if (j>1)
                         x=j;
                }
                else
                    x=i;
                
               В первом примере else ассоциируется с внутренним оператором

                                      - 103 -
           
           
         if.  Если i меньше или равно 0, то нет значения, которое присваи-
         вается x.
               Во втором примере фигурные скобки  ограничивают  внутренний
         оператор  if  и тем самым делают предложение else частью внешнего
         оператора if. Если i меньше или равно нулю, то значение i присва-
         ивается переменной x.
                
                
               6.10. Оператор null
                        
                
               Синтаксис:
                   ;
                
               Выполнение:
               Оператор null - это оператор, состоящий только из  точки  с
         запятой.   Он  может  появиться  в  любом  месте,  где  требуется
         оператор. Когда выполняется оператор null, ничего не происходит.
                
               Пример
               for (i=0; i<10; linee [i++]=0)
                         ;
                
               Такие операторы, как do, for, if, while, требуют,  чтобы  в
         теле  оператора был хотя бы один оператор. Оператор null удовлет-
         воряет требованиям синтаксиса в случаях, когда не требуется  тела
         оператора.  В  приведенном примере третье выражение оператора for
         инициализирует первые 10 элементов массива line нулем. Тело  опе-
         ратора  включает  оператор  null, т.к. нет необходимости в других
         операторах.
                
               Помеченный оператор null
                
               Оператор  null,  подобно любому другому Си оператору, может
         быть помечен меткой. Чтобы пометить об"ект, который  не  является
         оператором,  такой  как  закрывающаяся фигурная скобка составного
         оператора, можно  вставить  перед  об"ектом  помеченный  оператор
         null.
         
               
               6.11. Оператор return
                
               Синтаксис:
                
               return [];
                
               Выполнение:
               Оператор return заканчивает выполнение функции,  в  которой
         он  появляется, и возвращает управление в вызывающую функцию. Уп-
         равление передается в вызывающую функцию в точку, непосредственно
         следующую за вызовом. Значение выражения ,  если  оно
         есть,   возвращается   в   вызывающую   функцию.  Если  выражение
          опущено, то возвращаемая функцией величина не  опре-
         делена.
                

                                      - 104 -
           
           
               Пример
               main ()
               {
                       void draw (int,int);
                       long sq (int);
                                     
                       .
                       .
                       .
                       y=sq (x);
                       draw (x,y);
                       .
                       .
                       .
                }
                long sq (x)
                int x;
                {
                   return (x*x);
                }
                void draw (x,y)
                int x,y;
                {
                   .
                   .
                   .
                   return;
                }
                
               Функция  main  вызывает  две функции, sq и draw. Функция sq
         возвращает значение x*x в main. Величина  возврата  присваивается
         переменной y. Функция draw об"является как функция void и не воз-
         вращает значения. Попытка присвоить возвращаемое значение функции
         draw привело бы к ошибке.
               Выражение  оператора return заключено в скобки,
         как показано в примере. Язык не требует скобок.
                
               Отсутствие оператора return
                
               Если  оператор return не появился в определении функции, то
         управление автоматически передается в  вызывающую  функцию  после
         выполнения  последнего  оператора  в  вызванной функции. Значение
         возврата вызванной функции при этом не определено. Если  значение
         возврата  не  требуется, то функция должна быть об"явлена с типом
         возврата void.
         
               
               6.12. Оператор switch
                
               Синтаксис:
                
               switch () {
                     []
                     .
                     .

                                      - 105 -
           
           
                     .
                     [case :]
                                     
                     .
                     .
                     .
                          []
                          .
                          .
                          .
                     [default:
                              ]
                     [case :]
                     .
                     .
                     .
                          []
                          .
                          .
                          .
                }
                
               Выполнение:
               Оператор switch передает управление  одному  из  операторов
           своего  тела. Оператор, получающий управление, - это
         тот      оператор,      чье      case-константное       выражение
              равно     значению     switch-выражения
          в круглых скобках.
               Выполнение тела оператора начинается с выбранного оператора
         и продолжается до конца тела  или  до  тех  пор,  пока  очередной
         оператор  передает управление за пределы тела.
               Оператор  default выполнится, если case-константное выраже-
         ние    не  равно  значению  switch-выражения
         .  Если  default-оператор  опущен,  а соответствующий
         case  не  найден,  то  выполняемый   оператор   в   теле   switch
         отсутствует.  Switch-выражение   - это целая величина
         размера int или короче. Оно может быть также величиной типа enum.
         Если  короче чем int, оно расширяется до int.
               Каждое  case-константное  выражение   
         преобразуется   к   типу   switch-выражения.   Значение   каждого
         case-константного выражения должно быть  уникальным  внутри  тела
         оператора.
               Case  и  default  метки в теле оператора switch существенны
         только при начальной проверке, когда определяется стартовая точка
         для выполнения тела оператора. Все операторы  появляющиеся  между
         стартовым  оператором и концом тела, выполняются, не обращая внима-
         ния на свои метки, если какой-то из операторов не передает управ-
         ления из тела оператора switch.
               В  заголовке составного оператора, формирующего тело опера-
         тора switch, могут появиться об"явления, но инициализаторы, вклю-
         ченные в об"явления, не  будут  выполнены.  Назначение  оператора
         switch  состоит  в том, чтобы передать управление непосредственно
         на выполняемый оператор внутри тела, обойдя строки,  которые  со-
         держат инициализацию.
                

                                      - 106 -
           
           
               Примеры:
               /***.....* example 1 *.....***/
               switch (c) {
                     case 'A':
                        capa++;
                     case 'a':
                        lettera++;
                     default:
                        total++;
                  }
                
               /***.....* example 2 *.....***/
               switch (i) {
                     case -1:
                             n++;
                             break;
                     case 0:
                             z++;
                             break;
                     case 1:
                             p++;
                             break;
                  }
                
               В первом примере все три оператора в теле switch выполняют-
         ся,  если c равно 'A'. Передача управления осуществляется на пер-
         вый оператор capa++, далее операторы  выполняются  в  порядке  их
         следования в теле.
               Если   c   равно   'a',   то  переменные  lettera  и  total
         инкрементируются. Наконец, если c не равно  ни  'A'  ни  'a',  то
         инкрементируется только переменная total.
               Во  втором  примере  в  теле switch после каждого оператора
         следует   оператор    break.    Оператор    break    осуществляет
         принудительный  выход  из  switch после выполнения одного из этих
         операторов. Последний оператор break  не  является  обязательным,
         поскольку  без  него управление было бы передано из тела на конец
         составного оператора, но он включен для единообразия.
                
               Множественные метки
                
               Оператор тела  switch  может  быть  помечен  множественными
         метками, как показано в нижеследующем примере:
                                     
               case 'a':
               case 'b':
               case 'c':
               case 'd':
               case 'e':
               case 'f': hexcvt (c);
                
               Хотя любой оператор внутри тела оператора switch может быть
         помечен,  однако  не  требуется оператора, чтобы появилась метка.
         Операторы без меток могут быть смешаны с помеченными операторами.
         Следует помнить, однако, что если switch оператор передал  управ-
         ление  одному  из операторов своего тела, то все следующие за ним

                                      - 107 -
           
           
         операторы в блоке выполняются, не обращая внимания на свои метки.
                   
                
               6.13. Оператор while
                
               Синтаксис:
               while ()
                      
                
               Выполнение:
               Тело оператора while выполняется нуль или более раз до  тех
         пор,  пока  выражение    станет ложным (равным нулю).
         Вначале вычисляется выражение . Если  из-
         начально ложно, то тело оператора while не выполняется и управле-
         ние передается на следующий оператор программы. Если 
         является истиной (не нуль), то выполняется тело оператора.  Перед
         каждым   следующим   выполнением   тела   оператора  
         перевычисляется. Повторение выполнения тела оператора  происходит
         до  тех  пор, пока  остается истинным. Оператор while
         может также завершиться при выполнении  операторов  break,  goto,
         return внутри тела while.
                
               Пример
               while (i>=0) {
                      string1 [i] = string2 [i];
                      i--;
               }
                
               В  вышеприведенном  примере копируются символы из string2 в
         string1. Если i больше или равно  нулю,  то  string2[i]  присваи-
         вается индексной переменной string1[i] и i декрементируется. Ког-
         да  i  становится  меньше  нуля,то выполнение оператора while за-
         вершается.
                
                
                                       
                                    7. ФУНКЦИИ
                     
               
               7.1. Введение
                
               Функция  -  это  независимая  совокупность   об"явлений   и
         операторов,  обычно  предназначенная  для выполнения определенной
         задачи. Программы на Си состоят по крайней мере из одной  функции
         main,  но могут содержать и больше функций. В подразделах данного
         раздела описывается как определять, об"являть и вызывать  функции
         Си.
               Определение   функции   специфицирует   имя   функции,   ее
         формальные параметры, об"явления и операторы, которые  определяют
         ее  действия.  В  определении  функции может быть задан также тип
         возврата и ее класс памяти.
               В об"явлении задается имя,  тип  возврата  и  класс  памяти
         функции,   чье  явное  определение  произведено  в  другой  части
         программы. В об"явлении функции могут быть также  специфицированы
         число  и типы аргументов функции. Это позволяет компилятору срав-

                                      - 108 -
           
           
         нить  типы  действительных  аргументов  и  формальных  параметров
         функции.  Об"явления не обязательны для функций, возвращающих ве-
         личины типа int. Чтобы обеспечить корректное обращение при других
         типах возвратов, необходимо об"явить функцию перед ее вызовом.
               Вызов функции передает управление из вызывающей  функции  к
         вызванной. Действительные аргументы, если они есть, передаются по
         значению  в  вызванную функцию. При выполнении оператора return в
         вызыванной функции  управление  и,  возможно,  значение  возврата
         передаются в вызывающую функцию.
                
                
               7.2. Определение функции
                
               Определение функции специфицирует имя, формальные параметры
         и  тело  функции. Оно может также определять тип возврата и класс
         памяти функции. Синтаксис определения функции следующий:
                
               [][]
                                ([])
                                []
                                
                
               Спецификатор класса памяти  задает класс  па-
         мяти  функции, который может быть или static или extern. Специфи-
         катор типа  и декларатор   специфци-
         руют    тип   возврата   и   имя   функции.   Список   параметров
          - это список (возможно пустой) формальных  пара-
         метров,  которые  используются  функцией.  Об"явления  параметров
          задают типы формальных параметров.  Тело
         функции    -  это  составной  оператор, содержащий
         об"явления локальных переменных и операторы. В следующих разделах
         детально описываются составные части определения функции.
               
               
               7.2.1. Класс памяти
                
               Спецификатор класса памяти в определении функции определяет
         функцию  как  static  или extern. Функция с классом памяти static
         видима только в том исходном файле, в котором она определена. Все
         другие функции с классом памяти extern, заданным явно или неявно,
         видимы во всех исходных файлах, которые образуют программу.
               Если спецификатор класса памяти  опускается  в  определении
         функции,  то  подразумевается  класс  памяти extern. Спецификатор
         класса памяти extern может быть явно задан в определении функции,
         но этого не требуется.
               Спецификатор  класса памяти требуется при определении функ-
         ции только в одном случае, когда функция об"является где-нибудь в
         другом месте в том же самом исходном файле с спецификатором клас-
         са памяти static. Спецификатор класса памяти  static  может  быть
         также  использован,  когда  определяемая  функция  предварительно
         об"явлена в том же самом исходном файле без спецификатора  класса
         памяти. Как правило, функция, об"явленная без спецификатора клас-
         са  памяти,  подразумевает класс extern. Однако, если определение
         функции явно специфицирует класс static, то функции дается  класс
         static.

                                      - 109 -
           
           
               
               
               7.2.2. Тип возврата
                
               Тип возврата функции определяет размер и тип  возвращаемого
         значения. Об"явление типа имеет следующий синтаксис:
                
               [] ,
                
               где  спецификатор типа  вместе с декларато-
         ром  определяет тип  возврата  и  имя  функции.  Если
           не  задан, то подразумевается, что тип возврата
         int. Спецификатор типа может специфицировать основной,  структур-
         ный и совмещающий типы. Декларатор состоит из идентификатора фун-
         кции, возможно модифицированного с целью об"явления адресного ти-
         па.  Функции не могут возвращать массивов или функций, но они мо-
         гут возвращать указатели на любой тип, включая массивы и функции.
         Тип возврата, задаваемый в определении функции, должен соответст-
         вовать типам возвратов, заданных в об"явлениях этой функции, сде-
         ланных где-то в программе. Функции с типом возврата int могут  не
         об"являться  перед вызовом. Функции с другими типами возвратов не
         могут быть вызваны прежде, чем они будут определены или  об"явле-
         ны.
               Тип  значения  возврата  функции используется только тогда,
         когда функция возвращает значение, которое  вырабатывается,  если
         выполняется  оператор return, содержащий выражение. Выражение вы-
         числяется, преобразуется к типу возврата, если это необходимо,  и
         возвращается  в точку вызова. Если оператор return не выполняется
         или если выполняемый оператор return не  содержит  выражения,  то
         значение возврата функции не определено. Если в этом случае вызы-
         вающая  функция ожидает значение возврата, то поведение программы
         также не определено.
                
               Примеры
               /*.....* example 1 *.....*/
                     /* return type is int */
               static add (x,y)
               int x,y
               {
                    return (x+y);
                }
                
               /*.....* example 2 *.....*/
               typedef struct {
                    char name [20];
                    int id;
                    long class;
               } STUDENT
                    /* return type is STUDENT */
                    STUDENT sortstu (a,b)
                    STUDENT a,b;
               {
                    return ((a.id0){
                      {
                      .
                      .
                      .
                      }
                }
                match (r,n)
                struct student *r;
                char *n;
                {
                     int i=0;
                     while (r->name [i] == n[i])
                            if (r->name [i++] == '\0')
                                   return (r->id);
                     return (0);
                }
                
                

               В   примере  содержатся  :  об"явление  структурного  типа,
         forward-об"явление  функции  match,  вызов  match  и  определение
         функции  match.  Заметим, что одно и то же имя student может быть
         использовано  без  противоречий  для  тела  структуры   и   имени
         структурной переменной.
               Функция  match  об"явлена с двумя аргументами. Первый аргу-
         мент - это указатель на структуру типа student, второй указа тель
         на об"ект типа char.
               У функции match два формальных параметра z и n.
               Параметр n об"явлен как указатель на об"ект типа char.
               Функция  вызывается  с  двумя  аргументами.  Оба  аргумента
         являются элементами переменной student структурного типа student.
               Поскольку  имеется  forward-об"явление  функци  match, ком-
         пилятор проверит соответствие типов действительных аргументов  со
         списками  типов  аргументов,  а затем действительных аргументов с
         формальными параметрами.
               Так  как  типы  соответствуют,  то  в  предупреждениях  или
         преобразованиях нет необходимости.
               Заметим,  что  имя  массива,  заданное  в  качестве второго
         аргумента в вызове функции преобразуется  к  указателю  на  char.
         Соответствующий  формальный параметр также об"явлен как указатель
         на char и используется в выражении как идентификатор массива. Так
         как идентификатор массива рассматривается как адресное выражение,

                                      - 113 -
           
           
         то результат об"явления формального параметра как char  *n  будет
         тем же самым, что и char n [].
               Внутри   функции  локальная  переменная  i  определяется  и
         используется  в  качестве  индекса  массива.  Функция  возвращает
         структурный  элемент  id, если элемент структуры name сравнился с
         массивом n, в противном случае функция возвращает нуль.
               
               
               7.2.4. Тело функции
                
               Тело функции - это  просто  составной  оператор.  Составной
         оператор содержит операторы, которые определяют действия функции,
         и  может  также  содержать  об"явления переменных, используемых в
         этих операторах. Информацию о составных операторах смотри в  раз-
         деле  6.3.  Все переменные, об"явленные в теле функции, имеют тип
         памяти auto,  если  они  не  об"явлены  иначе.  Когда  вызывается
         функция,  то создается память для локальных переменных и произво-
         дится их инициализация (если она задана).  Управление  передается
         первому  оператору  составного  оператора  и  начинается  процесс
         выполнения, который продолжается до тех пор, пока  не  встретится
         оператор  return  или  конец  тела  функции.  Управление при этом
         возвращается в точку вызова.
               Если функция возвращает значение, то должен  быть  выполнен
         оператор  return,  содержащий  выражение.  Значение  возврата  не
         определено, если не выполнен оператор return или  еслив  оператор
         return не было включено выражение.
               
               
               7.3. Об"явления функции
                
               Об"явление функции определяет имя, тип возврата и класс па-
         мяти данной функции и может задавать тип некоторых или всех аргу-
         ментов функции. Детальное описание синтаксиса об"явлений  функции
         дано  в  разделе  4.  Функции  могут  быть об"явлены неявно или в
         forward-об"явлениями. Тип возврата функции, об"явленный или неяв-
         но или в forward-об"явлении, должен соответствовать типу возврата
         в определении функции. Неявное об"явление имеет место всякий раз,
         когда функция вызывается без предварительного об"явления или  оп-
         ределения.  Си-компилятор  неявно  об"являет вызываемую функцию с
         типом возврата int. По умолчанию функция  об"является  с  классом
         памяти extern. Определение функции может переопределить класс па-
         мяти на static, обеспечив себе появление ниже об"явлений в том же
         самом исходном файле. Forward-об"явление функции устанавливает ее
         атрибуты,  позволяя вызывать об"явленную функцию перед ее опреде-
         лением или из другого исходного файла.
               Если   спецификатор   класса   памяти   static  задается  в
         forward-об"явлении,  то  функция  имеет  класс  static.   Поэтому
         определение  функции  должно  быть  также специфицировано классом
         памяти static.
               Если  задан   спецификатор   класса   памяти   extern   или
         спецификатор опущен, то функция имеет класс памяти extern. Однако
         определение  функции может переопределить класс памяти на static,
         обеспечив себе появление ниже об"явлений в том же самом  исходном
         файле
               Forward-об"явление имеет важные различные  применения.  Они

                                      - 114 -
           
           
         задают  тип  возврата  для  функций, которые возвращают любой тип
         значений, за исключением int. (Функции, которые возврщают  значе-
         ние  int,  могут также иметь forward-об"явления, но делать это не
         требуется).
               Если функция с типом возврата не int  вызывается  перед  ее
         определением   или   об"явлением,   то   результат   неопределен.
         Forward-об"явления могут быть использованы для задания типов  ар-
         гументов, ожидаемых в функциональном вызове.
               Список типов аргументов  forward-об"явления  задает  тип  и
         число   предполагаемых   аргументов.   (Число   аргументов  может
         меняться). Список типов  аргументов  -  это  список  имен  типов,
         соответствующих списку выражений в функциональном вызове.
               Если  список  типов аргументов не задан, то не производится
         контроль  типов.  Несоответствие  типов   между   действительными
         аргументами  и  формальными параметрами разрешено. Более детально
         контроль  типов  рассмотрен  в  разделе  7.4.1.   "Действительные
         аргументы".
                
               Пример:
                main ()
                {
                   int a=0, b=1;
                                     
                   float x=2.0, y=3.0;
                   double realadd (double,double);
                   a=intadd (a,b);
                   x=realadd (x,y);
                }
                intadd (a,b)
                int a,b;
                {
                   return (a+b);
                }
                double realadd (x,y)
                double x,y
                {
                   return (x+y);
                }
                
               В  примере функция intadd об"явлена неявно с возвратом типа
         int, т.к. она вызвана перед  своим  определением.  Компилятор  не
         проверит  типы аргументов в вызове, т.к. отсутствует список типов
         аргументов.
               Функция   realadd   возвращает   значение   типа    double.
         Forward-об"явление  realadd  в  функции  main  позволяет  вызвать
         realadd перед  ее  определением.  Заметим,  что  тип  возврата  в
         определении  (double)  соответствует  типу  возврата, заданному в
         forward-об"явлении. В forward-об"явлении  также  определены  типы
         двух  параметров  функции realadd. Типы действительных аргументов
         соответствуют  типам  заданным  в  forward-об"явлении   и   также
         соответствуют типам формальных параметров.
                 
               
                

                                      - 115 -
                
                
               7.4. Вызовы функций
           
           
               Вызов  функции - это выражение, которое передает управление
         и фактические аргументы (если они есть)  функции.  Вызов  функции
         имеет следующее синтаксическое представление:
                
                ([])
                
               Выражение   вычисляется как адрес функции. Спи-
         сок выражение , в котором выражения следуют  че-
         рез запятую, представляет список фактических аргументов, посылае-
         мых функции. Список выражений может быть пустым.
               При  выполнении вызова функции происходит замена формальных
         аргументов на фактические. Перед заменой  каждый  из  фактических
         аргументов вычисляется. Первый фактический аргумент соответствует
         первому формальному аргументу, второй - второму и т.д.
               Вызванная функция работает с копией действительных аргумен-
         тов,  поэтому  любое изменение, сделанное функцией с аргументами,
         не отразится на оригинальных величинах, с  которых  была  сделана
         копия.
               Передача  управления  осуществляется  на  первый   оператор
         функции.  Выполнение  оператора  return в теле функции возвращает
         управление и, возможно, значение возврата в  вызывающую  функцию.
         Если  оператор  return  не  выполнен,  то управление возвращается
         после выполнения последнего оператора тела функции. При этом  ве-
         личина возврата не определена.
                
               Важно:
               Выражения   в   списке   аргументов  вызова  функции  могут
         выполняться в  любом  порядке,  так  что  выражения  с  побочными
         эффектами   могут  дать  непредсказуемые  результаты.  Компилятор
         только гарантирует, что  все  побочные  эффекты  будут  вычислены
         перед передачей управления в вызываемую функцию.
                
               Выражение перед скобками должно быть преобразовано к адресу
         функции. Это означает, что функция может быть вызвана через любое
         выражение  типа  указателя  на  функцию.  Это  позволяет вызывать
         функцию в той же самой манере, что и  об"являть.  Это  необходимо
         при  вызове  функции  через указатель. Например, предположим, что
         указатель на функцию об"явлен как следующий:
                
               int (* fpointer)(int,int);
                
               Идентификатор  fpointer  указывает  на  функцию   с   двумя
         аргументами  типа  int  и  возвращающую  значение типа int. Вызов
         функции в этом случае будет выглядеть следующим образом:
                
               (* fpointer)(3,4);
                
               Здесь  используется  операция   разадресации   (*),   чтобы
         получить  адрес функции, на которую ссылается указатель fpointer.
         Адрес функции затем используется для ее вызова.
                
               Примеры:
                /*.....* example 1 *.....*/
                double *realcomp (double,double);
                double a,b,*rp;

                                      - 116 -
           
           
                .
                .
                .
                rp=realcomp (a,b);
                
                          /*.....* example 2 *.....*/
                main ()
                {
                     long lift(int), step(int), drop(int);
                     void work (int, long (*) (int));
                     int select, count;
                     .
                     .
                     .
                     select=1;
                     switch (select)  {
                            case 1: work (count, lift);
                                    break;
                            case 2: work (count, step);
                                    break;
                            case 3: work (count, drop);
                
                            default:
                                    break;
                
                     }
                }
                
                void work (n, func)
                int n;
                long (*func)(int);
                {
                     int i;
                     long j;
                
                     for (i=j=0; i
               # define ()
                
               Директива  #  define  заменяет все вхождения идентификатора
          в  исходной  программе  на  ,  определенный  в
         директиве  справа  от  идентификатора.  Идентификатор заменяется,
         если он оформлен в виде лексемы. Например, идентификатор  не  из-
         меняется,  если  он представлен внутри строки или как часть более
         длинного идентификатора. Если после идентификатора следует список
         параметров , то директива # define заменяет  каж-
         дое  вхождение  выражения ,
         модифицированный заменой формальных аргументов фактическими.
                представляет собой набор лексем, таких как  ключевые
         слова,   константы   или  составные  операторы.  Один  или  более
         пробельных символов могут разделять  от    (или
         от  заключенных  в скобки параметров). Если текст больше чем одна
         строка,  то  он  может  быть  продолжен   на   следующей   строке
         посредством  печати  символа новой строки с последующей наклонной
         чертой влево.
                может быть опущен. В этом случае  все  представители
         идентификатора    будут  удалены  из исходного текста
         программы. Тем не менее,  рассматривается как опреде-
         ленный и принимает значение 1, если  проверяется  директивой  #if
         (смотри раздел 8.4.1).
               Когда  задан  список  параметров  ,  то  он
         содержит  один  или  более  формальных  параметров,   разделенных
         запятыми.  Каждое  имя  в  списке должно быть уникальным и список
         должен быть заключен в круглые  скобки.  Не  допускаются  пробелы
         между     и  открывающей  скобкой.  Имена  формальных
         параметров в тексте    отмечают  места,  куда  должны  быть
         подставлены   фактические   значения.   Каждое   имя  формального
         параметра может появиться в тексте  более  одного  раза  в  любом
         порядке.
               Фактические   аргументы,   следующие   непосредственно   за
         идентификатором    в  исходном  файле,  соответствуют
         формальным  параметрам  списка  параметров  и мо-

                                      - 122 -
           
           
         дифицируют  путем замены каждого формального  параметра  на
         соответствующий  фактический.  Списки  фактических  и  формальных
         параметров должны содержать одно и то же число аргументов.
               Аргументы  с  побочными эффектами могут стать причиной неп-
         редсказуемых результатов. Макроопределение может содержать  более
         одного  вхождения данного формального параметра, и если этот фор-
         мальный параметр представлен выражением с побочным  эффектом,  то
         это выражение будет вычисляться более чем один раз.
                
               Примеры:
                /*.....* example 1 *.....*/
                #define WIDTH    80
                #define LENGTH   (WIDTH + 10)
                
                /*.....* example 2 *.....*/
                #define FILEMESSAGE "Attempt too create file \
                failed because of insufficient space"
                
                /*.....* example 3 *.....*/
                #define REG1     register
                #define REG2     register
                #define REG3
                
                /*.....* example 4 *.....*/
                #define MAX(x,y)    ((x) > (y)) ? (x) : (y)
                
                /*.....* example 5 *.....*/
                #define MULT (a,b)  ((a) * (b))
                
               В  первом  примере  определяется  идентификатор  WIDTH, как
         целая константа 80,  и  определяется  идентификатор  LENGTH,  как
         (WIDTH + 10). Каждое вхождение LENGTH заменяется на (WIDTH + 10),
         которое  в свою очередь заменяется на выражение (80 + 10). Скобки
         являются  важными,  поскольку  они  управляют  интерпретацией   в
         операторах, подобных следующему:
                
               var = LENGTH * 20;
                
               После препроцессирования оператор будет таким:
                
               var = (80 + 10) * 20;
                
               Значение,  которое  присваивается,  равно  1800. Без скобок
         значение 80+10*20 равнялось бы 280.
               Во втором примере определяется  идентификатор  FILEMESSAGE.
         Определение  продолжается  на  вторую  строку путем использования
         символа "\".
               В  третьем  примере  определены  три  идентификатора, REG1,
         REG2, REG3. REG1 и REG2 определены как ключевые  слова  register.
         Определение  REG3  опущено и, таким образом, любое вхождение REG3
         будет удалено из исходного файла. Эти директивы  могут  быть  ис-
         пользованы  для того, чтобы обеспечить наиболее важным переменным
         программы  (заданным  с  REG1  и  REG2)  задание  класса   памяти
         register. В разделе 8.4.1 дана расширенная версия этого примера.
               В  четвертом  примере  дано макроопределение, поименованное

                                      - 123 -
           
           
         MAX. Каждое текущее вхождение макро-вызова MAX в  исходном  файле
         заменяется выражением ((x)>(y))?(x):(y), в котором формальные па-
         раметры x и y заменяются на фактические. Например, вхождение
                MAX(1,2)
         заменяется на
                ((1)>(2)?(1):(2),
         а вхождение
                MAX (i, s[i])
         заменяется на
                ((i)>(s[i]))?(i):(s[i])
               Макро-вызов  проще  читать,  чем соответствующее выражение,
         которое подставляется. Исходная программа  становится  проще  для
         понимания.
               Заметим,  что  в этом макро аргументы с побочными эффектами
         могут быть причиной непредсказуемых результатов.
               Например, макро-вызов MAX (i,s[i++]) заменится на
                
               ((i)>(s[i++]))?(i):(s[i++])
               
               Выражение s[i++] вычисляется дважды.  Результат  тернарного
         выражения  неопределен, т.к. его операторы могут быть вычислены в
         любом  порядке,  а  значение  переменной  i  зависит  от  порядка
         вычисления.
               В   пятом   примере   определяется  макро  с  именем  MULT.
         Макровызов MULT (3,5) в тексте программы заменяется  на  (3)*(5).
         Круглые  скобки,  в  которые заключаются  фактические  параметры,
         важны,   поскольку   они   управляют   интерпретацией   составных
         аргументов.  Например,  макровызов  MULT  (3+4,5+6)  заменится на
         (3+4)*(5+6), что эквивалентно 76. Без скобок результат подстанов-
         ки 3+4*5+6 равен 29.
               
         
               8.1.2. Директива #undef
                
               Синтаксис:
                
               #undef 
                
               Директива #undef отменяет текущее определение #define иден-
         тификатора . Чтобы отменить макроопределение посредс-
         твом директивы #undef, достаточно задать его идентификатор. Зада-
         ние списка параметров не требуется.
               Директива #undef может  быть  применена  к  идентификатору,
         который ранее не определен. Это дополнительная гарантия того, что
         идентификатор не определен.
               Директива  #undef обычно используется с директивой #define,
         чтобы создать область исходной программы, в которой идентификатор
         имеет специальный смысл. Директива #undef  используется  также  с
         директивой  #if  (смотри раздел 8.4.1) для управления сравнениями
         участков исходной программы.
                
               Пример:
               
                #define WIDTH         80
                #define ADD(X,Y)      (X)+(Y)

                                      - 124 -
           
           
                .
                .
                .
                #undef WIDTH
                #undef ADD
                
               В этом примере директива #undef отменяет определение поиме-
         нованной  константы и макроса. Заметим, что в директивах задаются
         только идентификатор и имя макроса.
               
               
               8.2. #include файлы
                
               Синтаксис:
                
               #include ""
               #include <>
                
               Директива #include добавляет содержимое  заданного  include
         файла к другому файлу. Определения констант и макросов могут быть
         организованы  в  "include"  файлах и добавлены к любому исходному
         файлу #include директивой.  "include"  файлы  также  полезны  для
         об"явлений  общих  внешних  переменных  и составных типов данных.
         Типы, которые требуется об"явить  и  поименовать  однажды,  также
         создаются в #include-файлах.
               Директива #include сообщает препроцессору об обработке фай-
         ла,  как если бы этот файл появился в исходной программе в точке,
         где записана директива. Обработанный текст также может  содержать
         директивы  препроцессора. Препроцессор выполняет директивы из но-
         вого текста, а затем продолжает  процессирование  первоначального
         текста исходного файла.
               Имя  файла    -  это  имя  файла с предшествующей
         спецификацией директория. Синтаксис спецификации файла зависит от
         специфики операционной системы, в которой компилируется  програм-
         ма.
               Препроцессор использует концепцию "стандартного" директория
         или  директориев  для  поиска #include-файлов. Расположение стан-
         дартных директориев для #include-файлов зависит от  реализации  и
         операционной   системы.   Смотрите   системную  документацию  для
         получения информации о стандартных директориях.
         
               Препроцессор останавливает поиск при первом появлении файла
         с заданным именем. Если  задано  полное  однозначное  ,
         заключенное  в  двойные кавычки (" ") или в угловые скобки (< >),
         то препроцессор ищет только это    и  игнорирует  стан-
         дартные  директории.  Если  спецификация  файла  не задана полным
         , но неполная спецификация файла  заключена  в  двойные
         кавычки,  то  препроцессор  начинает  поиск  включаемого  файла в
         текущем рабочем директории. Затем препроцессор продолжает поиск в
         директориях, специфицированных в команданой строке компиляции, и,
         наконец, ищет в стандартных директориях.
               Если  спецификация  файла  заключена  в  угловые скобки, то
         препроцессор  не  будет  осуществлять  поиск  в  текущем  рабочем
         директории.  Он  начнет  поиск в директориях, специфицированных в
         командной строке компиляции, а затем в стандартных директориях.

                                      - 125 -
           
           
               Директива #include может быть вложенной,  другими  словами,
         директива  может появиться в файле, поименованном другой #include
         директивой.    Когда     препроцессор     встречает     вложенную
         #include-директиву,  то  он  обрабатывает  файл  этой директивы и
         вставляет его в текущий файл. Препроцессор использует те же самые
         описанные выше процедуры для  поиска  вложенных  #include-файлов.
         Новый  файл также может содержать директивы #include. Допускается
         вложение до десяти уровней. Как только  вложенные  #include-файлы
         обработаны, препроцессор вставляет этот файл в исходный текстовый
         файл программы.
                
                Примеры:
                
               #include  /* example 1 */
               #include "defs.h" /* example 2 */
                
               В первом примере в исходную  программму  вставляется  файл,
         поименованный stdio.h. Угловые скобки сообщают препроцессору, что
         поиск  файла  нужно  осуществлять в стандартных директориях после
         поиска в директории, специфицированном в командной строке.
               Во  втором  примере  в исходную программу вставляется файл,
         поименованный defs.h. Двойные кавычки означают,  что  при  поиске
         файла вначале должен быть просмотрен текущий директорий.
               
               
               8.3. Условная компиляция
                
               В разделе используется синтаксис и использование  директив,
         которые  управляют  условной компиляцией. Эти директивы позволяют
         отменить компиляцию частей исходного файла  посредством  проверки
         константных  выражений или идентификаторов, при которой определя-
         ется нужно ли передавать на выход или пропустить данную часть ис-
         ходного файла на стадии препроце ссирования.
         
               
               8.3.1. Директивы #if, #elif, #else, #endif
                
               Синтаксис:
                
                #if 
                   []
                [#elif ]
                [*elif ]
                .
                .
                .
                [#else ]
                #endif
                
               Директива  #if  вместе с директивами #elif, #else и #endiif
         управляет компиляцией частей исходного  файла.  Каждой  директиве
         #if в исходном файле должна соответствовать закрывающая директива
         #endif. Между директивами #if и #endif допускается нуль или более
         директив #elif и не более одной директивы #else. Директива #else,
         если  она  есть,  должна  быть  расположена непосредственно перед

                                      - 126 -
           
           
         директивой #endif.
               Препроцессор выбирает один из  участков  текста-  для
         дальнейшей   обработки.  Участок    -  это  любая  последо-
         вательность текста. Он может занимать более одной строки.  Обычно
         это участок программного текста, который  имеет  смысл  для  ком-
         пилятора  или препроцессора. Однако, это не обязательное требова-
         ние. Препроцессор можно использовать для обработки любого текста.
               Выбранный текст обрабатывается препроцессором и  посылается
         на  компиляцию.  Если  содержит директивы препроцессора, то
         эти директивы выполняются.
               Любой участок  текста,  не  выбранный  препроцессором,  иг-
         норируется на стадии препроцессирования и впоследствии  не компи-
         лируется.
               Препроцессор  выбирает  отдельный участок текста  на основе
         вычисления       ограниченного       константного       выражения
         ,  следующего  за  каждой #if или
         #elif директивой, пока не будет найдено  выражение  со  значением
         истина  (не  нуль).  Выбирается  ,  следующий  за  истинным
         константным выражением, до ближайшего знака номера (#).
               Если ограниченное константное  выражение  не  истинно   или
         отсутствует  директива  #elif,  то  препроцессор  выбирает 
         после записи  #else.  Если  запись  #else  опущена,  а  выражение
         директивы #if ложно, то текст не выбирается.
                                     
               Ограниченное              константное             выражение
          описано в разделе 5.2.10.  Такое
         выражение  не  может содержать sizeof выражений, кастовых выраже-
         ний,  перечислимых  констант,  но  может  содержать   специальные
         константные  выражения  defined  ().  Это константное
         выражение истинно, если  заданный  идентификатор    в
         текущий  момент  определен,  в  противном случае выражение ложно.
         Идентификатор  ,  определенный   как   пустой  текст,
         рассматривается как определенный.
               Директивы  #if, #elif, #else, #endif могут быть вложенными.
         Каждая из вложенных директив #else, #elif, #endif  принадлежит  к
         ближайшей предшествующей директиве #if.
                
               Примеры:
                
               /*.....* example 1 *.....*/
                #if defined (CREDIT)
                    credit ();
                #elif defined (DEBIT)
                    debit ();
                #else
                    printerror ();
                #endif
                
                /*.....* example 2 *.....*/
                #if DLEVEL>5
                    #define SIGNAL  1
                    #if STACKUSE == 1
                                    #define STACK  200
                    #else
                                    #define STACK  100

                                      - 127 -
           
           
                    #endif
                #else
                    #define SIGNAL  0
                    #if STACKUSE == 1
                                    #define STACK  100
                    #else
                                    #define STACK  50
                    #endif
                #endif
                
                /*.....* example 3 *.....*/
                #if DLEVEL == 0
                    #define STACK 0
                #elif DLEVEL == 1
                    #define STACK 100
                #elif DLEVEL > 5
                    display ( debugptr );
                #else
                                
                    #define STACK 200
                #endif
                
                /*.....* example 4 *.....*/
                #define REG1  register
                #define REG2  register
                
                #if defined(M_86)
                    #define REG3
                    #define REG4
                    #define REG5
                #else
                    #define REG3   register
                    #if defined(M_68000)
                            #define REG4   register
                            #define REG5   register
                    #endif
                #endif
                
               В  первом  примере  директивы  #if,  #endif  управляют ком-
         пиляцией одним из трех вызовов функций. Вызов функции credit ком-
         пилируется, если идентификатор CREDIT определен.  Если  определен
         идентификатор DEBIT, то компилируется функциональный вызов debit.
         Если  ни  один идентификатор не определен, то компилируется вызов
         printerror.  Заметим,  что  CREDIT  и  credit  -  это   различные
         идентификаторы в Си.
               В  следующих  двух  примерах  предполагается, что константа
         DLEVEL предварительно определена.
               Во втором примере показаны две последовательности вложенных
         #if, #else, #endif директив. Первая  последовательность  директив
         обрабатывается, если DLEVEL >5. В противном случае обрабатывается
         вторая последовательность.
               В третьем  примере  используются  директивы  #elif,  #else,
         чтобы  сделать  один  из  четырех выборов, основанных на значении
         константы DLEVEL. Здесь определяется константа  STACK  равной  0,
         100  или 200, в зависимости от значения DLEVEL. Если DLEVEL>5, то

                                      - 128 -
           
           
         компилируется вызов функции display (debugptr), а константа STACK
         не определяется.
               В четвертом примере  директивы  препроцессора  используются
         для  управления  об"явлениями  спецификатора  регистровой  памяти
         register в переносимом исходном файле.  Если  программа  содержит
         больше  об"явлений  переменных  класа  памяти register, чем может
         предоставить машина, то компилятор не об"явит  лишние  переменные
         как  регистровые.  REG1  и  REG2  определяются как ключевые слова
         register, чтобы об"явить регистровую  память  для  двух  наиболее
         важных  переменных  в  программе. Например, в следующем фрагменте
         переменные b и c имеют больший приоритет, чем a или d.
         
                func (a)
                REG3 int a;
                {
                   REG1 int b;
                   REG2 int c;
                   REG4 int d;
                   .
                   .
                   .
                   }
                
               Когда определен идентификатор  М_86,  препроцессор  удаляет
         идентификаторы  REG3  и  REG4 из файла путем замены его на пустой
         текст. Регистровую память в этом случае получат переменные b и c.
         Когда определен идентификатор М_68000, то все  четыре  переменные
         об"являются с классом памяти register.
               Когда  не  определены  оба идентификатора, то об"являются с
         регистровой памятью три переменные a,b и c.
               
               
               8.3.2. Директивы #ifdef и #ifndef
                
               Синтаксис:
               
               #ifdef 
               #ifndef 
                
               Директивы #ifdef и #ifndef выполняют те  же  самые  задачи,
         что  и  директива  #if,  использующая  defined(). Эти
         директивы  могут  быть  использованы  там  же,  где  используется
         директива  #if, и используются исключительно для компактности за-
         писи.
               Когда препроцессор обрабатывает  директиву  ifdef,  то  де-
         лается  проверка  идентификатора    на истинность (не
         нуль).
               Директива  #ifdef  является  отрицанием  директивы  #ifdef.
         Другими  словами, если  не определен (или его опреде-
         ление отменено директивой #undef), то его  значение  истинно  (не
         нуль). В противном случае значение ложно (нуль).
               
               
                

                                      - 129 -
           
               8.4. Управление нумерацией строк
           
               Синтаксис:
                
               #line ["filename"]
                
               Директива #line инструктирует компилятор об изменении внут-
         ренней нумерации строк и имени файла на заданный номер  строки  и
         имя файла, для того чтобы сослаться на них в случае ошибок, обна-
         руженных в процессе компиляции. Номер строки обычно соответствует
         номеру  текущей входной строки. Имени файла соответствует имя те-
         кущего входного файла. Номер строки увеличивается после обработки
         каждой строки. В случае изменения номера строки  и  имени  файла,
         компилятор  игнорирует  предыдущие их значения и продолжает обра-
         ботку с новыми значениями.
               Директива #line обычно  используется  для  программной  ге-
         нерации  сообщений  об  ошибках со ссылками на номер строки и имя
         файла.
               Значение константы  в директиве #line - это любая
         целая константа. Имя файла   может  быть  любой  комби-
         нацией символов, заключенной в двойные кавычки ("). Если имя фай-
         ла опущено, предполагается, что имя файла осталось текущим.
               Текущие номер строки и имя файла доступны через предопреде-
         ленные   идентификаторы   __LINE__   и  __FILE__.  Идентификаторы
         __LINE__  и  __FILE__  могут  быть  использованы  при  вставке  в
         исходный файл программного текста выдачи сообщений об ошибке.
               Переменная  __FILE__  содержит  строку,  представляющую имя
         фйла, заключенного в двойные  кавычки.  Таким  образом,  нет  не-
         обходимости  заключать  идентификатор __FILE__ в двойные кавычки,
         когда он используется.
                
                Примеры:
                
               /*.....* example 1 *.....*/
                #line 151 "copy.c"
                
                /*..... example 2 *.....*/
                #define ASSERT(cond)      if(!cond)\
                {printf("assertion error line %d, file(%s)\n", \
                __LINE, __FILE);}else;
                
               В первом примере номер строки устанавливается равным 151  и
         имя файла изменяется на copy.c.
               Во  втором  примере  в макроопределении ASSERT используются
         предопределенные идщентификаторы __LINE__ и __FILE__  для  печати
         сообщения об ошибке, содержащего координаты исходного файла, если
         заданное  "утверждение"  ложно.  Заметим, что двойные кавычки при
         задании предопределенных идентификаторов не требуются.
               
               

         


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