Введение

      Документ "Начало работы" содержит обзор всего комплекта до-
 кументации к Turbo C++. Информация о том, как с наибольшей эффек-
 тивностью использовать Руководства по Turbo C++, содержится во вве-
 дении и главе 2 этого документа.

      Данное руководство содержит материалы для углубленного изуче-
 ния программирования и предназначено для тех, кто уже хорошо умеет
 программировать (на C, либо на другом языке программирования).
 Здесь содержится справочник по языку C, перекрестные ссылки по биб-
 лиотеке исполняющей системы и информациЯ по программированию с ис-
 пользованием потоков C++, моделей памяти, плавающей точки, оверле-
 ев, видео-функций, интерфейса с языком ассемблера, а также сообще-
 ния об ошибках исполняющей системы и компилятора.

      Прочесть документ "Начало работы" следует, если:

 1. Вы ранее никогда не программировали ни на одном языке.
 2. Вы ранее программировали, но не на C, и желаете прочесть введе-
    ние в язык C.
 3. Вам нужна информация о том, как установить на компьютере Turbo
    C++.

      В Руководстве пользователя находится информация об интегриро-
 ванной среде Turbo (включая редактор), администраторе управляющей
 системы, компиляторе командной строки, дополняющих Turbo C++ утили-
 тах и макроязыке редактора Turbo.

      Справочник по библиотеке содержит в алфавитном порядке листинг
 функций и глобальных переменных Turbo C++.
                        Содержание данного руководства

      Глава 1:  "Стандарт языка Turbo C++" описывает язык Turbo C++.
 Здесь приводятся все отличия от стандарта ANSI C. Данная глава так-
 же включает справочник по языкам C и C++.

      Глава 2: "Перекрестные ссылки по библиотеке управляющей систе-
 мы" содержит информацию об исходных кодах библиотеки исполняющей
 системы. Здесь также описаны файлы заголовков и приводятся перек-
 рестные ссылки по библиотеке управляющей системы, организованные по
 субъектам библиотеки. Например, если вы хотите выяснить, какие фун-
 кции относятся к графике, вы должны обратиться к разделу данной
 главы "Графика".

      Глава 3:  "Потоки C++" рассказывает, как использовать библиоте-
 ку потоков C++.

      Глава 4:  "Модели памяти, операции с плавающей точкой и оверлеи"
 рассматривает модели памяти, программирование со смешанными моделя-
 ми памяти, вычисления с плавающей точкой и оверлеями.

      Глава 5:  "Видео-функции" посвящена обработке в Turbo C++ текс-
 тов и графических изображений.

      Глава 6:  "Интерфейс с языком ассемблера" говорит о том, как
 нужно писать программы на языке ассемблера, чтобы они правильно ра-
 ботали при вызове из программ на Turbo C++.

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

      Приложение А: "Стандарты ANSI, зависимые от реализации" описы-
 вает те аспекты стандарта ANSI C, которые были определены с некото-
 рой степенью свободы или не были определены вообще в стандарте ANSI.
 Следовательно, эти аспекты могут варьироваться в зависимости от
 конкретной реализации. Данное приложение сообщает о том, как Turbo
 C++ поступает в отношении каждого из этих аспектов.





                 Глава 1             Стандарт языка Turbo C++


      В   данной  главе  дается  подробное  справочное  руководство
 программиста по языку Turbo C++. Оно не является  руководством  по
 изучению  языка, а скорее формальным описанием языков C и C++ в их
 реализации  программным  продуктом  Turbo  C++.  В  данной   главе
 описываются грамматические правила записи структуры фраз и лексики
 языка,  а  также  дается  подробное  изложение  имеющихся директив
 препроцессора.   При    формулировке    синтаксиса    используется
 модифицированная    запись    Бэкуса-Науэра,   при   необходимости
 сопровождаемая кратким описанием и примерами программ.

      Turbo  C++   реализует   стандарт   ANSI   C,   разработанный
 техническим  комитетом X3J11 между июнем 1983 и декабрем 1988 гг.,
 с некоторыми расширениями,  оговариваемыми  в  данном  тексте.  Вы
 имеете  возможность устанавливать опции компилятора, которые будут
 предупреждать вас о том,  что  такие  расширения  встретились  при
 работе  компилятора.  Вы  можете  также настроить компилятор таким
 образом, чтобы он рассматривал ключевые слова расширений Turbo C++
 в качестве нормальных идентификаторов (см.  Главу  4,  "Компилятор
 командной строки", в Руководстве пользователя).

      Существуют   также   "согласующие"   расширения,   включаемые
 посредством директив  #pragma,  предлагаемых  стандартом  ANSI  C,
 предназначенные   для   работы  с  нестандартными,  зависимыми  от
 конкретной реализации средствами языка.

      Turbo C++ является также полной реализацией AT&T  C++  версии
 2.00,   объектно-ориентированного  надмножества  C, разработанного
 Бьерном Строструпом из AT&T Bell Laboratories.  Помимо  того,  что
 C++  дополняет  C  многими  новыми  средствами и возможностями, он
 также и отличен от него в большей или меньшей степени. В настоящей
 главе содержатся замечания по этим отличиям.  Все  средства  языка
 Turbo   C++,   имеющие   отклонения   от   C++,   более   подробно
 рассматриваются, начиная со страницы 98.
                           Синтаксис и терминология

      Синтаксические определения состоят из имени  определяемого  в
 настоящий  момент  и  не  определенного  где-либо выше термина, за
 которым следует двоеточие (:). Альтернативы обычно следуют за этим
 в отдельных строках, но могут также помещаться и в одной строке;  в
 таком случае им предшествует фраза "одно из". Например,

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

    восьмеричная цифра: одно из
         0 1 2 3 4 5 6 7

      Опциональные элементы конструкции заключаются в угловые скобки:

    целочисленный-суффикс:
         суффикс-целого-без-знака  <суффикс-длинного-целого>

      В  данной  главе  слово  "аргумент"  везде  используется  для
 обозначения действительного  значения,  передаваемого  при  вызове
 функции.   "Параметр"   обозначает   переменную,   определяемую  в
 заголовке функции, которая будет  содержать  в  себе  передаваемое
 значение.
             Грамматические правила структуры фраз и лексики языка

      Грамматические  правила  лексики  языка  Turbo C++ описаны на
 стр. 5 - 58 оригинала; грамматические правила структуры фраз Turbo
 C++ описаны на стр. 58 - 98 оригинала.

      Грамматические правила лексики языка рассматриваются с  точки
 зрения  существования  различных  категорий  слово-ориентированных
 языковых    единиц,    называемых    лексемами,     распознаваемых
 компилятором.   Грамматические  правила  структуры  фраз  подробно
 определяют  допустимые  способы  группирования   этих   лексем   в
 выражения, операторы и прочие смысловые единицы языка.

      Лексемы  Turbo C++ образуются из последовательности операций,
 выполняемых  с  вашей  программой  компилятором  и  препроцессором
 языка.

      Программа  на  Turbo  C++  начинает  свое  существование  как
 последовательность   ACSII-символов,   представляющих   собой   ее
 исходный код, создаваемый нажатиями клавиш при работе в подходящем
 текстовом редакторе (например, в собственном редакторе Turbo C++).
 Базовая  программная  единица в Turbo C++ представляет собой файл.
 Обычно  такой  файл  соответствует  файлу  DOS,   находящемуся   в
 оперативной  памяти  или на диске и имеющему созданное по правилам
 DOS имя и расширение .C или .CPP.

      Сначала выполняется просмотр текста программы препроцессором,
 который ищет в нем специальные директивы препроцессора  (см.  стр.
 133  оригинала).  Например,  директива  #include <включаемый_файл>
 добавляет  (или  включает)  в  программу  перед  фазой  компиляции
 содержимое  файла  <включаемый_файл>. Препроцессор также выполняет
 расширение любых встреченных в  программах  или  файлах  включения
 макросов.
                              Пробельные символы

      На  фазе компиляции, отвечающей за распознавание лексем, файл
 исходного кода программы подвергается лексическому  анализу  (т.е.
 разбиению  на  лексемы  и  пробелы).  Пробельными  будут обобщенно
 именоваться  собственно   символы   пробелов,   горизонтальные   и
 вертикальные   символы   табуляции,   символы   новой   строки   и
 комментарии. Пробельные символы служат для обозначения мест начала
 и конца лексем, и сверх этой функции, для исключения из компиляции
 всех избыточных символов, не входящих в состав  лексем.  Например,
 следующие две последовательности,

    int   i; float f;

 и

    int i ;
        float    f;

 лексически идентичны, а их лексический анализ в обоих случаях дает
 шесть лексем:

    int i ; float f ;

      ASCII-символы,  обычно  рассматриваемые как пробельные, могут
 входить в строки литералов, и в данном случае  будут  защищены  от
 нормального  процесса  разбиения  на  лексемы  и  пробелы; другими
 словами, они станут представлять собой часть строки:

    char name[] = "Borland International";

 разбивается на семь лексем, включая и лексему строки литералов
 "Borland International".

        "Склеивание" строк символом \

      Имеется специальный случай, когда  перед  последним  символом
 новой  строки  находится символ наклонной черты влево (\). В таком
 случае  символы  наклонной  черты  влево  и  символ  новой  строки
 отбрасываются,  что  позволяет рассматривать две физические строки
 текста как единое целое.

    "Borland \
     International"

      будет    рассматриваться    как    "Borland    International"
 (дополнительную  информацию  см.  на стр. 17 оригинала, "Строковые
 литералы"

         Комментарии

      Комментарии    представляют    собой     текстовые     части,
 предназначенные    для    аннотирования   программы.   Комментарии
 используются исключительно самим программистом; перед  лексическим
 анализом они исключаются из исходного текста программы.

      Существует  два способа указания на комментарии: традиционный
 метод C и метод C++. Они оба поддерживаются  Turbo  C++,  и  кроме
 того, имеется дополнительное, опциональное расширение, позволяющее
 организовывать  вложенные  комментарии.  Разрешается  смешанное  и
 комбинированное   использование   комментариев   любого   типа   в
 программах C и C++.
                                 Комментарии C

      Традиционный   комментарий   C   представляет   собой   любую
 последовательность символов, помещаемую после  пары  символов  /*.
 Признаком  конца  комментария  служит  первая  пара  символов  */,
 встретившаяся   после   исходной   пары   /*.   После   выполнения
 макрорасширения вся эта последовательность целиком, включая четыре
 разделительных  символа  комментария,  заменяется  на один пробел.
 Отметим,  что  некоторые  реализации  C  удаляют  комментарии,  не
 заменяя их на пробелы.

      Turbo  C++  не  поддерживает  не-мобильной  стратегии вставки
 лексем с помощью /**/. Вставка лексем в Turbo C++ выполняется  при
 помощи заданной ANSI пары символов ##, следующим образом:

    #define VAR(i,j)  (i/**/j)     /* не будет работать */
    #define VAR(i,j)  (i##j)       /* в Turbo C++ будет работать */
    #define VAR(i,j)  (i ## j)     /* также будет работать */

 В Turbo C++

    int /* объявление */ i /* как счетчика */;

 после лексического анализа даст

    int  i  ;

 то есть три лексемы:  Int I ;

Вложенные комментарии

      ANSI   C   не  разрешает  вложенность  комментариев.  Попытка
 комментировать приведенную выше строку в виде

    /*  int /* объявление */ i /как счетчика */; */

 окончится неудачей, поскольку область действия первой пары /* будет
 ограничена первой парой */. Это даст в результате лексического ана-
 лиза

    i ; */

 что приведет к генерированию состояния синтаксической ошибки.

      По умолчанию Turbo C++ не позволяет вложенность комментариев,
 однако это умолчание может быть переопределено опцией компилятора.
 Разрешить вложенность комментариев можно либо при помощи опции  -C
 (компилятора  командной  строки),  либо через меню интегрированной
 среды программирования O|C|Source Options.

Комментарии C++

      Для  создания  в  коде  C  комментариев   вы   можете   также
 использовать символы //. Это средство специфично для Turbo C++.

      C++   позволяет   создание   размещаемого   в   одной  строке
 комментария при помощи двух подряд  следующих  символов  наклонной
 черты  (//).  Такой  комментарий  может начинаться в любой позиции
 строки, и включает в себя все, что расположено  до  символа  новой
 строки:

    class X {  // это комментарий
    ... };

Разделители комментариев и пробелы

      В  редких  случаях пробелы перед /* и // или после */, хотя и
 не являются синтаксически  обязательными,  но  позволяют  избежать
 проблем, связанных с мобильностью. Например, код C++

    int i = j//* деление на k*/k;
    +m;

 при лексическом анализе даст   int i = j +m;  а не

    int i = j/k;
    +m;

 как это можно было бы ожидать по традиционным соглашениям C. Более
 удобочитаемая форма

    int i = j/ /* деление на k*/ k;
    +m;

 позволяет избежать этой проблемы.

Лексемы

      Turbo  C++  распознает лексемы шести классов: ключевые слова,
 идентификаторы, константы, строковые литералы,  операции  и  знаки
 пунктуации  (также  называемые разделителями). Формальное описание
 лексемы имеет следующий вид:

    лексема:
         ключевое слово
         идентификатор
         константа
         строковый литерал
         операция
         знак пунктуации

      Во  время  лексического  анализа   исходного   кода   лексемы
 выделяются  методом,  при  котором  из строки символов обязательно
 выбирается лексема максимальной длины.  Например,  слово  external
 будет  рассматриваться  как  отдельный  идентификатор,  а  не  как
 ключевое слово extern, за которым следует идентификатор al.
                                Ключевые слова

      Ключевыми словами  называются  слова,  зарезервированные  для
 специальных  целей,  которые  не  должны использоваться в качестве
 обычных имен идентификаторов. В следующих двух таблицах приводятся
 ключевые слова Turbo C++. Вы можете использовать опции компилятора
 командной строки (или опции в IDE), чтобы выбрать только  ключевые
 слова  ANSI,  ключевые слова UNIX и т.д. Информацию об этих опциях
 см. в главах 1,  "Справочник  IDE",  и  4,  "Компилятор  командной
 строки" в Руководстве пользователя.

 Все ключевые слова Turbo C++                          Таблица 1.1
 -----------------------------------------------------------------
 asm               _ds               interrupt         short
 auto              else              _loadds           signed
 break             enum              long              sizeof
 case              _es               near              _ss
 catch             _export           new               static
 cdecl             extern            operator          struct
 char              far               pascal            switch
 class             float             private           template
 const             for               protected         this
 continue          friend            public            typedef
 _cs               goto              register          union
 default           huge              _regparam         unsigned
 delete            if                return            virtual
 do                inline            _saverages        void
 double            int               _seg              volatile
                                                       while
 -----------------------------------------------------------------

 Расширения Turbo C++ относительно ANSI C              Таблица 1.2
 -----------------------------------------------------------------
 cdecl             _export           _loadds           _saveregs
 _cs               far               near              _seg
 _ds               huge              pascal            _ss
 _es               interrupt         _regparam
 -----------------------------------------------------------------

 Ключевые слова, специфичные для C++                   Таблица 1.3
 -----------------------------------------------------------------
 catch             friend            operator          public
 class             inline            private           template
 delete            new               protected         this
                                                       virtual
 -----------------------------------------------------------------

 Регистровые псевдопеременные Turbo C++                Таблица 1.4
 -----------------------------------------------------------------
 _AH               _BL               _CL               _DL
 _AL               _BP               _CX               _DX
 _AX               _BX               _DH               _FLAGS
 _BH               _CH               _DI               _SI
                                                       _SP
 -----------------------------------------------------------------
                                Идентификаторы

      Формальное определение идентификатора имеет следующий вид:

    идентификатор:
       не-цифра
       идентификатор не-цифра
       идентификатор цифра

    не-цифра: одно из
       a b c d e f g h i j k l m n o p q r s t u v w x y z _
       A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

    цифра: одно из
       0 1 2 3 4 5 6 7 8 9

 Ограничения состава и длины идентификаторов

      Идентификаторы  представляют  собой  произвольные имена любой
 длины,  присваиваемые  классам,  объектам,  функциям,  переменным,
 определяемым  пользователем  типам  данных  и  т.д. Идентификаторы
 могут содержать буквы от A до Z и от a до z, символ  подчеркивания
 (_) и цифры от 0 до 9. Существует только два ограничения:

 1. Первый символ должен являться буквой или символом подчеркивания.

      Отметим, что идентификаторы в Turbo C++ значимы до любой длины.

 2. По умолчанию Turbo C++ распознает только первые 32 символа в ка-
    честве значимых. Число значимых символов может быть уменьшено
    при помощи меню или опций командной строки, но не может быть
    увеличено. Используйте опцию -In TCC, либо опцию меню
    O|C|S|Identifier Length, где 1 <= n <= 32.

 Идентификаторы и учет регистра

      Идентификаторы в Turbo C++ учитывают регистр, и таким образом,
 Sum, sum и suM - это различные идентификаторы.

      Глобальные  идентификаторы,  импортируемые из других модулей,
 подчиняются тем же правилам наименования и длины значимости имени,
 что и обычные идентификаторы. Однако, Turbo C++  включает  в  себя
 опцию  подавления учета регистра, обеспечивающую совместимость при
 компоновке с модулями на языках,  не  учитывающих  регистр.  Можно
 отменить учет регистра для глобальных идентификаторов, установив в
 диалоговом  поле Linker в соответствующее состояние поле Options |
 Linker | Case-Sensitive Link,  либо  использовав  опцию  командной
 строки /C при запуске TLINK. В таком режиме глобальные имена Sum и
 sum  рассматриваются  как  идентичные, и при компоновке может быть
 выдано   предупреждение   "Duplicate    symbol"    ("Повторяющиеся
 символические имена").

      Исключение  из  этих  правил  составляют  идентификаторы типа
 pascal, которые при компоновке всегда преобразовываются к верхнему
 регистру.

 Уникальность и контекст идентификаторов

      Хотя  имена  идентификаторов  могут  быть  произвольными   (в
 пределах  изложенных правил), в случае использования одного и того
 же имени для более чем одного  идентификатора  в  пределах  одного
 контекста  и  разделении  ими  одного  пространства имен возникает
 ошибка. Повторение имен в различных пространствах  имен  допустимо
 всегда,  независимо  от  контекста.  Правила  рассматриваются  при
 обсуждении контекста, начиная со стр. 29 оригинала.
                                   Константы

      Константами   называются   лексемы,   представляющие    собой
 фиксированные   числовые   или   символьные  значения.  Turbo  C++
 поддерживает  четыре  класса   констант:   с   плавающей   точкой,
 целочисленные, перечислимого типа и символьные.

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

 Константы - определение формата  Таблица 1.5
 -----------------------------------------------------------------
 константа:
    константа-с-плавающей-точкой
    целочисленная-константа
    перечислимая-константа
    символьная-константа

 константа-с-плавающей-точкой:
    дробная-константа <экспоненциальная-часть> <суффикс-константы-
    -с-плавающей-точкой>
    последовательность-цифр  экспоненциальная часть <суффикс-
    константы-с-плавающей-точкой>

 дробная константа:
    <последовательность-цифр>.<последовательность-цифр>
    <последовательность-цифр>.

 экспоненциальная-часть:
    e <знак> последовательность-цифр
    E <знак> последовательность-цифр

 знак: одно из
    + -

 последовательность-цифр:
    цифра
    последовательность-цифр  цифра

 суффикс-константы-с-плавающей-точкой: одно из
    f l F L

 целочисленная-константа:
    десятичная-константа <суффикс-целочисленной-константы>
    восьмеричная-константа <суффикс-целочисленной-константы>
    шестнадцатиричная-константа <суффикс-целочисленной-константы>

 десятичная-константа:
    цифра-кроме-нуля
    десятичная-константа  цифра

 восьмеричная-константа:
    0
    восьмеричная-константа  восьмеричная-цифра

 шестнадцатиричная-константа:
    0 x шестнадцатиричная-цифра
    0 X шестнадцатиричная-цифра
    шестнадцатиричная-константа  шестнадцатиричная-цифра

 цифра-кроме-нуля: одно из
    1 2 3 4 5 6 7 8 9

 восьмеричная-цифра: одно из
    0 1 2 3 4 5 6 7

 шестнадцатиричная-цифра: одно из
    0 1 2 3 4 5 6 7 8 9
    a b c d e f
    A B C D E F

 суффикс-целочисленной-константы:
    суффикс-константы-без-знака  <суффикс-длинной-константы>
    суффикс-длинной-константы  <суффикс-константы-без-знака>

 суффикс-константы-без-знака: одно из
    u U

 суффикс-длинной-константы: одно из
    l L

 перечислимая-константа:
    идентификатор

 символьная-константа:
    последовательность-символов-c

 последовательность-символов-c:
    символ-c
    последовательность-символов-c  символ-c

 символ-c:
    любой символ из исходного набора символов, за исключением симво-
    лов  одинарной кавычки ('), обратной наклонной черты (\) или
    управляющей-последовательности символа новой строки.

 управляющая-последовательность: одно из
    \*        \'        \?        \\
    \a        \b        \f        \n
    \o        \oo       \ooo      \r
    \t        \v        \Xh...    \xh...
 -----------------------------------------------------------------

 Целочисленные константы

      Целочисленные константы  могут  быть  десятичными  (основание
 системы   счисления   10),   восьмеричными   (основание   8)   или
 шестнадцатиричными (основание 16). При отсутствии переопределяющих
 суффиксов тип  данных  целочисленной  константы  выводится  из  ее
 значения,  как  показано  в  таблице 1.6. Отметим, что правила для
 десятичных и не-десятичных констант различны.

 Десятичные константы

      Допустимыми являются десятичные константы величиной от  0  до
 4,294,967,295. Константы, выходящие за указанные пределы, вызывают
 ошибку.   Десятичные   константы  не  могут  иметь  ведущие  нули.
 Целочисленная  константа  с  ведущим  нулем  рассматривается   как
 восьмеричная. Таким образом,

    int i = 10;   /* десятичное 10 */
    int i = 010;  /* десятичное 8 */
    int i = 0;    /* десятичное 0 = восьмеричному 0! */

      Отрицательные  константы  - это просто константы без знака, к
 которым применена унарная операция минус.

 Восьмеричные константы

      Все   константы   с   ведущим   нулем   рассматриваются   как
 восьмеричные.  Если  восьмеричная  константа содержит недопустимые
 цифры 8 или 9, выдается сообщение об ошибке.  Ошибка  будет  также
 выдаваться   при   превышении   восьмеричной  константой  значения
 037777777777.

 Шестнадцатиричные константы

      Все константы, начинающиеся с 0x (или 0X) рассматриваются как
 шестнадцатиричные.   Шестнадцатиричные   константы,    превышающие
 0xFFFFFFFF, приводят к ошибке.

 Суффиксы длинных констант и констант без знака

      Если  за  константой  следует  суффикс  L  (или  l), то такая
 константа будет представлена как длинная (типа long).  Аналогичным
 образом,  суффикс  U (или u) делает константу константой без знака
 (unsigned). Если численное значение константы превышает десятичное
 65,535, независимо от используемого основания  системы  счисления,
 то  такая  константа будет иметь тип unsigned long. Суффиксы можно
 указывать для одной и той же константы в  произвольном  порядке  и
 набирать в любом регистре: ul, lu, UL и т.д.

      Тип  данных  константы  при  отсутствии  каких-либо суффиксов
 вообще (U, u, L или l) берется из следующей  таблицы,  первый  же,
 который удовлетворяет требованиям величины этой константы:

 -----------------------------------------------------------------
 десятичная         int, long int, unsigned long int
 восьмеричная       int, unsigned int, long int, unsigned long int
 шестнадцатиричная  int, unsigned int, long int, unsigned long int
 -----------------------------------------------------------------

      Если  константа имеет суффикс U или u, то ее тип данных будет
 первым  из  типов  unsigned  int,  insigned  long   int,   который
 удовлетворит требованиям ее величины.

      Если  константа имеет суффикс L или l, то ее тип данных будет
 первым из типов long int, unsigned long int, который  удовлетворит
 требованиям ее величины.

      Если  константа  имеет оба суффикса u и l (ul, lu, Ul, lU,uL,
 Lu, LU или UL), то она будет иметь тип данных unsigned long int.

      В таблице 1.6 сведены  представления  целочисленных  констант
 для всех трех систем счисления. Указанные типы данных предполагают
 отсутствие переопределяющих суффиксов L или U.

 Целочисленные константы Turbo C++ без L или U         Таблица 1.6
 -----------------------------------------------------------------
    Десятичные константы

                  0 до 32,767            int
             32,768 до 2,147,483,647     long
      2,147,483,648 до 4,294,967,295     unsigned long

                     > 4294967295        Генерируется ошибка

    Восьмеричные константы

                 00 до 077777            int
            0100000 до 0177777           unsigned int
           02000000 до 017777777777      long
       020000000000 до 037777777777      unsigned long

                     > 037777777777      Генерируется ошибка

    Шестнадцатиричные константы

             0x0000 до 0x7FFF            int
             0x8000 до 0xFFFF            unsigned int
            0x10000 до 0x7FFFFFFF        long
         0x80000000 до 0xFFFFFFFF        unsigned long

                     > 0xFFFFFFFF        Генерируется ошибка
 -----------------------------------------------------------------

 Символьные константы

      Символьная   константа   -   это  один  или  более  символов,
 заключенных в одинарные кавычки, например  'F',  '=',  '\n'.  В  C
 константы  из  одного  символа  имеют  тип  Int и имеют внутреннее
 представление  16  бит,  в  то  время  как  старший   байт   слова
 заполняется  нулем  или  знаком. В C++ константа из одного символа
 имеет тип char. Многосимвольные константы как в C, так  и  в  C++,
 имеют тип данных Int.

 Управляющие последовательности

      Для  введения  управляющих  последовательностей,  позволяющих
 получить   визуальное   представление   некоторых    не    имеющих
 графического   аналога   символов,  используется  символ  обратной
 наклонной черты (\). Например, в  качестве  одного  символа  новой
 строки можно записать константу \n.

      Обратная  наклонная  черта  используется  с восьмеричными или
 шестнадцатиричными  числами  для  представления   ASCII-символами
 управляющего  кода,  соответствующему  этому  значению;  например,
 '\03' вместо Ctrl-C или '\x3F'  вместо  вопросительного  знака.  В
 управляющей  последовательности может содержаться строка длиной до
 трех восьмеричных или  любое  число  шестнадцатиричных  цифр,  при
 условии,  что  данное  значение лежит в допустимом для типа данных
 char диапазоне (от 0 до 0xff  для  Turbo  C++).  Большие  значения
 ведут  к  появлению ошибки компиляции "Numeric constant too large"
 ("числовая  константа  слишком  велика").  Например,  восьмеричное
 число \777 больше максимально допустимого значения \377, и вызовет
 генерирование    ошибки.    Первое    же    не-восьмеричное    или
 не-шестнадцатиричное  значение,  встреченное  в  восьмеричной  или
 шестнадцатиричной  управляющей  последовательности, означает конец
 данной последовательности.

      В исходной  версии  Turbo  C  допускалось  только  три  цифры
 шестнадцатиричной  управляющей  последовательности.  Новые правила
 ANSI C, реализованные в Turbo C версии  2.0  и  Turbo  C++,  могут
 вызвать проблемы со старыми кодами, предполагающими преобразование
 только  первых  трех  символов. Например, при использовании версии
 Turbo C 1.x для определения строки со звуковым сигналом (код ASCII
 7), после которого следуют  числовые  символы,  программист  может
 написать:

    printf("\x0072.1Простая операционная система");

      Предполагается,  что  эта  строка  будет интерпретирована как
 \x007 и "2.1Простая операционная система". Однако,  Turbo  C++  (и
 Turbo  C  версии  2.0)  компилируют ее как шестнадцатиричное число
 \x0072 и литеральную строку ".1Простая операционная система".

      Чтобы избежать этих проблем,  перепишите  ваш  код  следующим
 образом:

    printf("\x007" "2.1Простая операционная система");

      Неоднозначность  может возникнуть также и в случае, когда за
 восьмеричной     управляющей      последовательностью      следует
 невосьмеричная  цифра.  Например,  поскольку  8  и  9  не являются
 допустимыми   восьмеричными   цифрами,   константа   \258    будет
 интерпретирована  как  двухсимвольная  константа,  составленная из
 символов \25 и 8.

      В  следующей   таблице   показаны   допустимые   управляющие
 последовательности.

 Управляющие последовательности Turbo C++              Таблица 1.7
 -----------------------------------------------------------------
 Последовательность  Значение  Символы  Выполняемая функция
 -----------------------------------------------------------------
    \a                0x07      BEL     Звуковой сигнал
    \b                0x08      BS      Забой
    \f                0x0C      FF      Перевод бланка
    \n                0x0A      LF      Новая строка (перевод строки)
    \r                0x0D      CR      Возврат каретки
    \t                0x09      HT      Табуляция (горизонтальная)
    \v                0x0B      VT      Вертикальная табуляция
    \\                0x5c      \       Обратная наклонная черта


      Для   фактического   представления  символа  ASCII  "обратная
 наклонная черта",  используемого  например  в  команде  DOS  PATH,
 следует записывать ее как \\.

    \'                0x27      '       Одинарная кавычка (апостроф)
    \"                0x22      "       Двойная кавычка
    \?                0x3F      ?       Вопросительный знак
    \O                          любые   O = строка до трех восьми-
                                        ричных цифр
    \xH                         любые   H = строка шестнадцатирич-
                                        ных цифр
    \XH                         любые   H = строка шестнадцатирич-
                                        ных цифр
 -----------------------------------------------------------------

 Специальные двух-символьные константы Turbo C++

      Turbo   C++   поддерживает  также  двух-символьные  константы
 (например, 'An', '\n\t' и '\007\007'). Эти константы  представлены
 16-битовыми  значениями  типа  Int, где первый символ расположен в
 младшем байте, а второй символ - в старшем байте. Эти константы не
 могут быть перенесены на другие компиляторы C.


 Символьные константы со знаком и без знака

      В C одно-символьные константы, такие как 'A', '\t' и  '\007',
 также  представлены 16-битовыми значениями типа Int. В этом случае
 происходит расширение младшего байта  в  старший  байт  по  знаку;
 таким  образом,  если  значение  превышает  127 (основание 10), то
 старший байт устанавливается равным -1 (=0xFF). Это свойство можно
 отменить, объявив, что по умолчанию  тип  char  является  unsigned
 (при помощи опции -R TCC, либо выбором в меню Options | Compiler |
 Code   Generation  опцию  Unsigned  Characters),  что  приведет  к
 обнулению старшего байта независимо от значения младшего байта.


 Широкие символьные константы (только C)

      Символьная  константа,  которой  предшествует  L,  называется
 широкой   символьной   константой  и  имеет  тип  данных  wchar_t
 (интегральный тип, определяемый в stdef.h). Например,

    x = L 'AB';

 Константы с плавающей точкой

      Константа с плавающей точкой состоит из шести частей:

 - десятичное целое
 - десятичная точка
 - десятичное дробное
 - e или E и целочисленная экспонента со знаком (опционально)
 - суффикс типа: f или F, либо l или L (опционально)

      Десятичное целое или десятичное дробное (но не то  и  другое)
 можно опустить. Можно опустить либо десятичную точку, либо букву e
 (или E) с целочисленной экспонентой со знаком (но не то и другое).
 Эти  правила позволяют выполнять запись чисел как в обычной, так и
 в научной (экспоненциальной) форме.

      Отрицательные  константы  с  плавающей  точкой  берутся   как
 положительные константы с префиксом - унарной операцией минус (-).

 Примеры:
 -------------------------------------
    Константа        Значение
 -------------------------------------
                               6
    23.45e6          23.45 x 10

    .0               0

    0.               0
                             0
    1.               1.0 x 10  = 1.0

    -1.23            -1.23
                             -5
    2e-5             2.0 x 10
                             10
    3E+10            3.0 x 10
                              34
    .09E34           0.09 x 10
 -------------------------------------

 Константы с плавающей точкой - типы данных

      При  отсутствии  каких-либо  суффиксов  константы с плавающей
 точкой имеют  тип  данных  double.  Однако,  вы  можете  присвоить
 константе  с  плавающей  точкой  тип  данных  float, добавив к ней
 суффикс f или F. Аналогичным образом, суффикс  l  или  L  присвоит
 константе  тип  данных  long  double. В следующей таблице показаны
 диапазоны значений, которые могут  принимать  типы  данных  float,
 double и long double.

 Размеры и диапазоны
 констант с плавающей точкой Turbo C++                 Таблица 1.8
 -----------------------------------------------------------------
    Тип          Размер (в битах)     Диапазон значений
 -----------------------------------------------------------------
                                             -38              38
    float             32              3.4 x 10    до  3.4 x 10
                                             -308             308
    double            64              1.7 x 10    до  1.7 x 10
                                             -4932            4932
    long double       80              3.4 x 10    до  1.1 x 10
 -----------------------------------------------------------------

 Перечислимые константы

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

      Значения, принимаемые перечислимыми константами,  зависят  от
 формата  объявления  перечислимого типа и присутствия опциональных
 инициализаторов. В данном примере,

    enum team { giants, cubs, dodgers };

 giants, cubs и dodgers это перечислимые константы типа team, кото-
 рые могут быть назначены любым переменным типа team или любой дру-
 гой переменной целочисленного типа. Значения, принимаемые перечис-
 лимыми константами,

    giants = 0,  cubs = 1, dodgers = 2

 при условии отсутствия явных инициализаторов. В следующем примере,

    enum team { giants, cubs=3, dodgers = giants + 1 };

 константы установлены следующим образом:

    giants = 0,  cubs = 3,  dodgers = 1

 Значения констант не обязаны быть уникальными:

    enum team { giants, cubs = 1, dodgers = cubs - 1 };

 Допустимы также отрицательные инициализаторы.

 Строковые литералы

      Строковые литералы, известные также как строковые  константы,
 образуют специальную категорию констант, используемых для работы с
 фиксированными  последовательностями  символов.  Строковый литерал
 имеет  тип  данных  array  of  char  и  класс  памяти  static,   и
 записывается   как   последовательность  произвольного  количества
 символов, заключенных в двойные кавычки:

    "Это строковый литерал!"

 Нулевая (пустая) строка записывается как "".

      Символы внутри двойных  кавычек  могут  включать  управляющие
 последовательности (см. стр. 13 оригинала). Например, данный код,

    "\t\t\"Имя \"\\\tАдрес\n\n"

 распечатается следующим образом:

    "Имя "\  Адрес

      Слову  "Имя  "  будет  предшествовать  два символа табуляции;
 слову   Адрес   предшествует   один   символ   табуляции.   Строка
 заканчивается  двумя символами новой строки. Последовательность \"
 обеспечивает вывод внутренних кавычек.

      Строка   литерала   хранится   в    памяти    как    заданная
 последовательность  символов,  плюс конечный пустой символ ('\0').
 Нулевая строка хранится в виде одного символа '\0'.

      На фазе лексического  анализа  соседние  строковые  литералы,
 разделенные   только   пробелами,   конкатенируются.  В  следующем
 примере,

    #include 

    main()
    {
       char    *p;

       p = "Это пример того, как Turbo C++"
           " автоматически\nвыполняет для вас конкатенацию"
           " очень длинных строк,\nчто позволяет получить"
           " более красивые программы.";
       printf{p};
    }

      На выходе программы будет:

    Это пример того, как Turbo C++ автоматически
    выполняет для вас конкатенацию очень длинных строк,
    что позволяет получить более красивые программы.

      Для  расширения  строковой  константы  за  границы  строки  в
 качестве символа продолжения можно использовать обратную наклонную
 черту (\):

    put("В действительности \
    это однострочная строка символов");
                    Константы и их внутреннее представление

      ANSI C говорит о том, что размер и численный диапазон базовых
 типов  данных  ( и различных их модификаций) зависят от конкретной
 реализации компилятора и в целом  от  архитектуры  компьютера,  на
 котором  он  установлен.  Базовыми  компьютерами  для  Turbo  C++
 являются компьютеры семейства  IBM  PC  (и  совместимые  с  ними),
 поэтому  выбор  внутреннего представления различных типов данных в
 целом определяется архитектурой микропроцессоров 8088 и  80x86.  В
 следующей  таблице  сведены  размеры  и  соответствующие диапазоны
 значений   для   типов   данных,   определяемых   в   Turbo   C++.
 Дополнительную  информацию  об  этих  типах  данных см. на стр. 39
 оригинала. Внутреннее представление типов данных см. на рис.1.

 Типы данных, размеры и диапазоны значений             Таблица 1.9
 -----------------------------------------------------------------
 Тип          Размер  Диапазон            Примеры применения
 -----------------------------------------------------------------
 unsigned char  8     0 до 255            Малые числа и полный
                                          набор символов PC

 char           8     -128 до 127         Самые малые числа и
                                          ASCII-символы

 enum          16     -32,768 до 32,767   Упорядоченные наборы
                                          значений

 unsigned int  16     0 до 65,535         Большие числа и циклы

 short int     16     -32,768 до 32,767   Счетчики, малые числа,
                                          управление циклами

 int           16     -32,768 до 32,767   Счетчики, малые числа,
                                          управление циклами

 unsigned long 32     0 до 4,294,967,295  Астрономические расстояния

 long          32     -2,147,483,648 до 2,147,483,647
                                          Большие числа, население

                             -38              38
 float         32     3.4 x 10    до  3.4 x 10
                                          Научные расчеты (точность
                                          7 разрядов)

                             -308             308
 double        64     1.7 x 10    до  1.7 x 10
                                          Научные расчеты (точность
                                          15 разрядов)

                             -4932           4932
 long double   80     3.4 x 10    до 1.1 x 10
                                          Финансовые расчеты
                                          (точность 19 знаков)

 near pointer  16     Не существует       Манипулирование адресами
                                          памяти

 far pointer   32     Не существует       Манипулирование адресами
                                          памяти вне текущего
                                          сегмента
 -----------------------------------------------------------------

               <------- направление возрастания значимости

               ------------
          int  |s|значение|  (дополнение до 2)
               ------------
              15          0

               ----------------------
     long int  |s|      значение    |  (дополнение до 2)
               ----------------------
              31                    0

               ----------------------
               | |смещенный|мантисса|
        float  |s|порядок  |        |
               ----------------------
              31                    0

               --------------------------------
               | | смещенный |    мантисса    |
       double  |s| порядок   |                |
               --------------------------------
              63                              0

               ----------------------------------------
               | | смещенный | |      мантисса        |
  long double  |s| порядок   | |                      |
               ----------------------------------------
              79                                      0


               s = знаковый бит (0 = положит, 1 = отрицат)
                 = позиция неявной двоичной точки
               1 = целочисленный бит мантиссы:
                   записывается в long double
                   неявно (всегда 1) в float, double

               Смещенный порядок (нормализованные значения):
                   float:       127 (7FH)
                   double:      1023 (3FFH)
                   long double: 16,383 (3FFFH)

      Рис.1.1  Внутренние представления типов данных


 Выражения с константами

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

    выражение-с-константами:
        условное-выражение

      Выражения с константами не могут содержать приводимых ниже
 операций, если эти операции не содержатся в операнде операции sizeof:

 - присваивание
 - декремент
 - вызов функции
 - запятая
                               Описание операций

      Операциями    называются    лексемы,   вызывающие   некоторые
 вычисления  с  переменными  и  прочими  объектами,  указанными   в
 выражении.  Turbo  C++  имеет  особенно  богатый  набор  операций,
 включающий в  себя  помимо  обычных  арифметических  и  логических
 операций средства манипуляции с данными на битовом уровне, доступа
 к   компонентам   структур  и  объединений,  а  также  операции  с
 указателями (установка и обращение по ссылке).

      Расширения C++ предлагают дополнительные операции для доступа
 к компонентам класса и их объектам, а  также  механизм  перегрузки
 операций.   Перегрузка  позволяет  переопределять  действие  любых
 стандартных операций применительно к объектам заданного класса.  В
 данном  разделе  мы ограничимся рассмотрением стандартных операций
 Turbo  C++.  Перегрузка  рассматривается,  начиная  со  стр.   124
 оригинала.

      После определения стандартных операций мы обсудим типы данных
 и  объявления, а также объясним, как они влияют на действие каждой
 операции. Затем мы перейдем к рассмотрению  синтаксиса  построения
 выражений с помощью операций, пунктуаторов и объектов.

      Операции в Turbo C++ определяются следующим образом:

    операция: одно из

       []        ()        .          ->          ++          --
       &         *         +          -         тильда        !
       sizeof    /         %          <<          >>          <
       >         <=        >=         ==          !=          ^
       |         &&        ||         ?:          =           *=
       /=        %=        +=         -=          <<=         >>=
       &=        ^=        |=         ,           #           ##

      Операции # и ## используются только препроцессором (см. стр.
 133 оригинала).

      Следующие операции являются специфичными для C++:

       ::        .*        ->*

      За  исключением  операций  [],  ()  и ?:, служащих для записи
 выражений в скобках, многосимвольные  операции  рассматриваются  в
 качестве  одной  лексемы.  Лексема  одной  и той же операции может
 иметь  несколько  интерпретаций,  в  зависимости   от   контекста.
 Например,

    A * B                    Умножение
    *ptr                     Обращение по ссылке

    A & B                    Поразрядное И
    &A                       Операция адресации
    int &                    Модификатор указателя (C++)

    label:                   Метка оператора
    a ? x : y                Условный оператор

    void func(int n);        Объявление функции
    a = (b+c)*d;             Выражение со скобками

    a, b, c;                 Выражение с запятой
    func(a, b, c);           Вызов функции

    a = -b;                  Поразрядное вычитание (дополнение до
                             единицы)
    -func() {delete a;}      Деструктор (C++)

Унарные операции

    &                        Операция адресации
    *                        Операция обращения по ссылке
    +                        Унарный плюс
    -                        Унарный минус
  тильда                     Поразрядное дополнение (дополнение до
                             единицы)
    !                        Логическое отрицание
    ++                       Префикс:  пред- инкремент;
                             Постфикс: пост- инкремент
    --                       Префикс:  пред- декремент;
                             Постфикс: пост- декремент
                               Бинарные операции

 Операции типа сложения    +     Бинарный плюс (сложение)
                           -     Бинарный минус (вычитание)

 Операции типа умножения   *     Умножение
                           /     Деление
                           %     Остаток от деления

 Операции сдвига           <<    Сдвиг влево
                           >>    Сдвиг вправо

 Поразрядные операции      &     Поразрядное И
                           ^     Поразрядное исключающее ИЛИ
                           |     Поразрядное включающее ИЛИ

 Логические операции       &&    Логическое И
                           ||    Логическое ИЛИ

 Операторы присваивания    =     Присваивание
                           *=    Присвоить произведение
                           /=    Присвоить частное
                           %=    Присвоить остаток
                           +=    Присвоить сумму
                           -=    Присвоить разность
                           <<=   Присвоить сдвиг влево
                           >>=   Присвоить сдвиг вправо
                           &=    Присвоить поразрядное И
                           ^=    Присвоить поразрядное исключающее
                                 ИЛИ
                           |=    Присвоить поразрядное ИЛИ

 Операции отношения        <     Меньше
                           >     Больше
                           <=    Меньше или равно
                           >=    Больше или равно

 Операции равенства        ==    Равно
                           !=    Не равно

 Операции выбора           .     Прямой селектор компонента
 компонента                ->    Косвенный селектор компонента

 Операции с компонентами   ::    Доступ/разрешение контекста
 класса                    .*    Обращение через указатель
                                 к компоненту класса
                           ->*   Обращение через указатель
                                 к компоненту класса

 Условные операции   a ? x : y   "Если a то x иначе y"

 Операция запятой          ,     Вычислить, например, a, b, c
                                 слева - направо

      Функции  этих  операций,  также как их синтаксис, приоритет и
 свойства  ассоциативности  рассматриваются,  начиная  со  стр.  73
 оригинала.
                                  Пунктуаторы

      В  Turbo  C++  пунктуаторы,  также  называемые разделителями,
 определяются следующим образом:

    пунктуатор: одно из
       [ ] ( ) { } , ; : ... * = #

 Квадратные скобки

      [] (открывающая и закрывающая квадратные скобки) указывают на
 индексы одно- и многомерных массивов:

    char ch, str[] = "Stan"
    int mat[3][4];           /* матрица 3 x 4 */
    ch = str[3];             /* 4-й элемент */
    ...

 Круглые скобки

      () (открывающая  и  закрывающая  круглые  скобки)  группируют
 выражения,  выделяют  условные  выражения  и  указывают  на вызовы
 функций и параметры функций:

    d = c * (a + b); /* переопределение нормального приоритета */
                     /* выполнения операций */
    if (d == z) ++x; /* важно при использовании условных операций */
    func();          /* вызов функции без аргументов */
    int (*fptr)();   /* объявление указателя функции */
    fptr = func;     /* отсутствие () означает указатель функции */
    void func2(int n);   /* объявление функции с аргументами */

      Рекомендуется     использовать     круглые      скобки      в
 макроопределениях,  что  позволит  избежать  возможных  проблем  с
 приоритетами операций во время расширения:

    #define CUBE(x) ((x) * (x) * (x))

      Использование  круглых  скобок   для   изменения   нормальных
 приоритетов  операций  и  правил  ассоциативности  см.  на  стр.76
 оригинала.

 Фигурные скобки

      {} (Открывающие и  закрывающие  фигурные  скобки)  обозначают
 начало и конец составного оператора:

    if (d == z)
    {
      ++x
      func();
    }

      Закрывающая  фигурная  скобка  служит терминатором составного
 оператора, поэтому (;) (точка с запятой) после } не требуется,  за
 исключением структур или объявлений классов. Часто точка с запятой
 недопустима, как например в случае

    if (оператор)
       {};          /* недопустимое использование точки с запятой */
    else

 Запятая

      Запятая (,) отделяет элементы списка аргументов функции:

    void func(int n, float f, char ch);

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

    func(i, j);          /* вызов функции с двумя аргументами */
    func((exp1, exp2), (exp3, exp4, exp5)); /* также вызов функции
                                            с двумя аргументами */

 Точка с запятой

      Точка с запятой  (;)  служит  терминатором  оператора.  Любое
 допустимое  выражение  С  (включая и пустое выражение), за которым
 следует (;), интерпретируется как оператор, называемый  оператором
 выражения.  Выражение  вычисляется,  а его значение отбрасывается.
 Если такое выражение не имеет  побочных  эффектов,  то  Turbo  C++
 может его проигнорировать.

    a + b;   /* a + b вычисляется, но полученное значение теряется */
    ++a;     /* имеется побочный эффект для a, но результат ++a */
             /* теряется */
    ;        /* пустое выражение = нулевой оператор */

      Точки  с  запятой  часто  используются  для  создания  пустых
 операторов:

    for (i = 0; i < t; i++)
    {
       ;
    }

 Двоеточие

      Двоеточие (:) служит для обозначения оператора с меткой:

    stsrt:    x=0;
    ...
    goto stsrt;
    ...
    switch (a)  {
       case 1: puts("Первый");
               break;
       case 2: puts("Второй");
               break;
    ...
    default:   puts("Ни тот, ни другой!");
               break;
    }

      Метки рассматриваются на стр.92 оригинала.

 Многоточие

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

    void func(int n, char ch,...);

      Данное объявление указывает, что func будет определена  таким
 образом, что вызовы ее должны содержать как минимум два аргумента,
 int  и  char,  но  также  могут иметь и любое число дополнительных
 аргументов.

      В С++ запятую, предшествующую многоточию, можно опустить.

 Звездочка (объявление указателя)

      Звездочка (*) в  объявлении  переменной  обозначает  создание
 указателя на тип:

    char *char_ptr;  /* объявление указателя на тип char */

      Можно объявить указатели с несколькими уровнями косвенности,
 что обозначается соответствующим количеством звездочек:

    int **int_ptr;       /* указатель на указатель на int */
    double ***double_ptr /* указатель на указатель на указатель
                            на тип double */

      Звездочка также используется в качестве операции обращения
 через указатель, либо операции умножения:

    i = *int_ptr;
    a = b * 3.14;

 Знак равенства (инициализатор)

      Знак равенства (=) разделяет объявления переменных от списков
 инициализации:

    char array[5] = { 1, 2, 3, 4, 5 };
    int x = 5;

      В  функциях  С  никакой  код  не может предшествовать никаким
 объявлениям  переменных.  В  С++  объявления  любого  типа   могут
 находиться (с некоторыми ограничениями) в любой точке внутри кода.

      В  списке  аргументов функции С++ знак равенства указывает на
 значение параметра по умолчанию:

    int f(int i = 0) { ... } /* параметр i имеет значение по умол-
                                чанию  ноль */

       Знак равенства используется также как операция присвоения в
 выражениях:

    a = b + c;
    ptr = farmalloc(sizeof(float)*100);

 Знак фунта (директива препроцессора)

      Знак фунта (#) означает  директиву  препроцессора,  если  она
 является   первым  не-пробельным  символом  в  строке.  Он  задает
 действие компилятора, не обязательно связанное с генерацией  кода.
 Более   подробно  директивы  препроцессора  описаны  на  стр.  133
 оригинала.

      # и ## (двойной знак фунта) также используются  как  операции
 замены и слияния лексем на фазе сканирования кода препроцессором.

Объявления

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

      Контекст,   видимость,  продолжительность  и  тип  компоновки
 определяют  части  программы,  из  которых  могут   быть   сделаны
 допустимые   ссылки   на   идентификатор   с   целью   доступа   к
 соответствующему   объекту.   Контекст   обсуждается   на   стр.29
 оригинала,    видимость    -    на    стр.30;    продолжительность
 рассматривается, начиная со стр. 31, а тип компоновки - на стр.32.
                                    Объекты

      Объектом называется идентифицируемая область памяти,  которая
 может содержать фиксированное значение переменной (или набор таких
 значений). (Используемое в данном случае слово "объект" не следует
 путать     с     более     общим    термином,    используемым    в
 объектно-ориентированных языках - см. главу 5, "Введение в  С++"  в
 документе  "Начало работы".) Каждая величина имеет связанное с ней
 имя и тип (который также называют типом данных). Имя  используется
 для доступа к объекту. Имя может являться простым идентификатором,
 либо  сложным  выражением,  уникальным  образом  "указывающим"  на
 данный объект.

 Тип используется для

 - для определения требуемого количества памяти при ее исходном
   распределении,
 - для интерпретации битовых коды, находимых в объектах при
   последующих к ним обращениях,
 - а также в многочисленных ситуациях контроля типа, требуемого для
   обнаружения возможных случаев недопустимого присваивания.

      Turbo C++ поддерживает многие стандартные (предопределенные),
 а  также   определяемые   пользователем   типы   данных,   включая
 целочисленные  типы разных размеров, со знаком и без него, числа с
 плавающей  точкой  различной  точности  представления,  структуры,
 объединения,  массивы  и  классы.  Кроме того, имеется возможность
 устанавливать   указатели   на   большинство   этих   объектов   и
 манипулировать ими со многими моделями памяти.

      Стандартные  библиотеки  Turbo  C++, а также ваши собственные
 программы   и   файлы    заголовков    обеспечивают    однозначные
 идентификаторы  (или  выводимые  из  них  выражения) и типы, таким
 образом, что Turbo C++  может  непротиворечиво  выполнять  доступ,
 интерпретировать  и  (возможно)  изменять  битовые  коды в памяти,
 соответствующей каждому активному объекту вашей программы.

      Объявления     устанавливают     необходимые      соотношения
 распределения  памяти  между  идентификаторами и объектами. Каждое
 объявление  связывает  идентификатор  с  некоторым  типом  данных.
 Большинство  объявлений,  известных  как  объявления  определения,
 также задает создание (т.е. где и когда)  объекта;  иначе  говоря,
 распределение  физической  памяти  и  ее  возможную инициализацию.
 Прочие объявления, называемые объявлениями ссылки,  просто  делают
 указанные  в них идентификаторы известными компилятору. Один и тот
 же идентификатор может иметь множество объявлений ссылки, особенно
 в многофайловых  программах,  однако  для  каждого  идентификатора
 допустимо только одно объявление определения.

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

      Именующее   выражение  представляет  собой  локатор  объекта,
 выражение,  которое   обозначает   объект.   Примером   именующего
 выражения  может  служить *P, где P это выражение, дающее непустой
 указатель.    Модифицируемое    именующее    выражение    -    это
 идентифицирующее  выражение,  относящееся  к  объекту,  к которому
 возможен доступ и допустимо  его  изменение  в  памяти.  Указатель
 константы  const,  например,  не является модифицируемым именующим
 выражением.  Указатель  на  константу  может   быть   изменен   (а
 подлежащее обращению по этому указателю значение - не может).

      Исторически  в  слове  Lvalues  буква L означает "левый"; это
 означает, что Lvalue допускается  в  левой  части  (а  принимающей
 части)  оператора  присваивания.  Здесь  в  левой  части оператора
 присваивания допустимы только модифицируемые именующие  выражения.
 Например, если a и b - это не являющиеся константами целочисленные
 идентификаторы  с  правильно  распределенными  для  них  областями
 памяти,   то   оба   они   являются   модифицируемыми   именующими
 выражениями,  и  присваивания  типа  a  =  1;  и b = a + b; вполне
 допустимы.

 Значения переменной (Rvalues)

      Выражение a + b не может  являться  именующим  выражением,  и
 выражение  типа a + b = a недопустимо, поскольку выражение в левой
 части не относится  к  объекту.  Такие  выражения  часто  называют
 значением переменной (значение правой части выражения).
                             Типы и классы памяти

      Для связи идентификаторов с объектами требуется, чтобы каждый
 идентификатор  имел  как минимум два атрибута: класс памяти и тип
 (иногда  его  называют  типом  данных).   Компилятор   Turbo   C++
 определяет  эти  атрибуты  по  явным  или  неявным  объявлениям в
 исходном коде программы.

      Класс  памяти  задает  размещение  объекта  (сегмент  данных,
 регистр,   куча   или   стек)   и  продолжительность  его  времени
 существования (все время работы программы, либо же при  выполнении
 некоторых   конкретных  блоков  кода).  Класс  памяти  может  быть
 установлен синтаксисом объявления, расположением в  исходном  коде
 или обоими этими факторами.

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

      Контекстом   идентификатора  называется  часть  программы,  в
 которой данный идентификатор может быть использован для доступа  к
 связанному  с  ним  объекту.  Существует пять категорий контекста:
 блок (или локальный), функция,  прототип  функции,  файл  и  класс
 (только  для  С++).  Контекст зависит от того, как и где объявлены
 идентификаторы.

 Контекст блока

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

 Контекст функции

      Единственными   идентификаторами,   имеющими   контекст  типа
 функции,  являются  метки  операторов.  Имена  меток  могут   быть
 использованы   в  операторах  goto  в  любой  точке  функции,  где
 объявлена  данная  метка.  Метки  объявляются  неявно;  для  этого
 записывается  имя_метки: и за ним оператор. Имена меток в пределах
 функции должны быть уникальными.

 Контекст прототипа функции

      Идентификаторы, объявленные в списке объявлений параметров  в
 прототипе функции (не являющиеся частью определения функции) имеют
 контекст  прототипа  функции.  Конец  этого  контекста совпадает с
 концом прототипа функции.

 Контекст файла

      Идентификаторы   с   контекстом   файла,   называемые   часто
 глобальными,  объявляются  вне  всех блоков и классов; их контекст
 лежит между точкой объявления и концом исходного файла.

 Контекст класса (С++)

      Классом можно считать именованный набор компонентов,  включая
 сюда  структуры  данных  и  действующие  с  ними функции. Контекст
 класса относится, за некоторыми исключениями, к именам компонентов
 конкретного  класса.  Классы  и   их   объекты   имеют   множество
 специальных правил доступа и определения контекста; см. стр. 102 -
 113 оригинала.

 Контекст и пространства имен

      Пространство   имен  -  это  контекст,  в  пределах  которого
 идентификатор  должен  быть  уникальным.  В  С  существует  четыре
 раздельных класса идентификаторов:

 1. Имена меток операторов goto. Эти имена должны быть уникальными
    в пределах функции, в которой они объявлены.

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

      В С++ структуры, классы и перечислимые данные относятся к од-
 ному и тому же пространству имен.

 3. Имена  компонентов  структур и объединений. Они должны быть
    уникальными в пределах  структуры  или  блока,  в  котором  они
    определены.  На  тип  или  смещение  с  одним и тем же именем в
    различных структурах ограничений не существует.

 4. Переменные,  определения  типа  и  компоненты  перечислимых
    данных.  Они  должны  быть  уникальными  в  контексте,  где они
    определены. Идентификаторы, объявленные внешними,  должны  быть
    уникальными среди переменных, объявленных вовне.
                                   Видимость

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

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

      Видимость не может выходить за пределы контекста; но контекст
 может превышать видимость.

    ...
    {
      int i; char ch;  // автоматическое распределение по умолчанию
      i = 3;           // int i и char ch в контексте и видимы
    ...
      {
         double i;
         i = 3.0e3;    // double i в контексте и видима
                       // int i в контексте, но скрыта
         ch = 'A';     // char ch в контексте и видима
      }
                       // double i вне контекста
      i += 1;          // int i видима и равна 4
    ...                // char ch все еще в контексте и видима
                       // и равна 'A'
    }
    ...                // int i и char ch вне контекста

      И снова, специальные правила действуют  в  отношение  скрытых
 имен  классов и имен компонентов классов: специальные операции С++
 позволяют  доступ  к  скрытым  идентификаторам  при   определенных
 условиях (см. стр.103 оригинала).
                               Продолжительность

      Продолжительность,   близко   связанная   с  классом  памяти,
 определяет   продолжительность   периода,   в   течение   которого
 объявленным  идентификаторам соответствуют распределенные в памяти
 реальные  физические  объекты.  Также  делается   различие   между
 объектами  времени  компиляции  и  времени  выполнения.  Например,
 переменным, в отличие от определяемых типов  (typedefs)  и  типов,
 память  непосредственно  во  время  выполнения  не распределяется.
 Существует три вида продолжительности:  статическая,  локальная  и
 динамическая.

 Статическая продолжительность (static)

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

      При    отсутствии   явного   инициализатора,   либо   в   С++
 конструктора,   объекты    со    статической    продолжительностью
 инициализируются в ноль (или пустое значение).

      Статическую  продолжительность  не  следует путать с файловым
 или  глобальным  контекстом.  Объект   может   иметь   статическую
 продолжительность и при этом локальный контекст.

 Локальная продолжительность (local)

      Объект  с локальной продолжительностью всегда имеет локальный
 контекст, поскольку он не существует вне своего объемлющего блока.
 Обратное  неверно:  объект  с  локальным  контекстом  может  иметь
 статическую продолжительность.

      Объекты   с   локальной   продолжительностью   жизни,   иначе
 называемые  динамическими  локальными  переменными, менее надежны.
 Они создаются в стеке (или в регистре) при входе в  объемлющий  их
 блок или функцию. При выходе программы из такого блока или функции
 они  уничтожаются.  Объекты  с локальной продолжительностью должны
 быть  инициализированы  явно;  в  противном  случае  их   исходное
 содержимое  непредсказуемо. Объекты с локальной продолжительностью
 всегда должны иметь локальный контекст или контекст  функции.  При
 объявлении    переменных    локальной    продолжительности   можно
 использовать спецификатор класса памяти auto, однако  он  является
 избыточным,  поскольку  auto  для переменных, объявленных в блоке,
 всегда является умолчанием.

      При  объявлении  переменных  (например,  int,  char,   float)
 спецификатор  класса  памяти  register  также  подразумевает auto,
 однако компилятору при этом передается запрос (или рекомендация) о
 том, что при возможности данный  объект  желательно  разместить  в
 регистре.  Turbo  C++  можно  установить  таким  образом, чтобы он
 распределял  регистр   локальной   интегральной   переменной   или
 переменной  типа указатель, если какой-либо регистр свободен. Если
 свободных регистров нет, то переменная  распределяется  как  auto,
 или  динамический  локальный объект, без выдачи предупреждения или
 генерации ошибки.

 Динамическая продолжительность (dynamic)

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

Единицы трансляции

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

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

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

      Слово external имеет в С несколько значений; в данном  случае
 оно относится к объявлениям, находящимся вне каких-либо функций, и
 которые, следовательно, имеют контекст файла. (Отдельным свойством
 является   внешний   тип   компоновки;   см.   следующий   раздел,
 "Компоновка".) Любое объявление, также  резервирующее  память  для
 объекта  или  функции,  называется  определением  (или объявлением
 определения). Более подробную информацию см.  в  разделе  "Внешние
 объявления и определения" на стр.36 оригинала.
                                  Компоновка

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

      Каждое   вхождение   конкретного   идентификатора   с   типом
 компоновки external представляет тот же самый объект  или  функцию
 во  всем  множестве  файлов  и  библиотек, составляющих программу.
 Каждое вхождение конкретного  идентификатора  с  типом  компоновки
 internal  представляет  тот  же  самый объект или функцию только в
 пределах  одного  файла.  Идентификаторы  с  типом  компоновки  no
 (отсутствие) представляет уникальные элементы программы.

      Ниже  приводятся  правила  внешней  (external)  и  внутренней
 (internal) компоновки:

 1. Любой идентификатор объекта или файла, имеющий файловый кон-
    текст, будет иметь внутренний тип компоновки, если его объявле-
    ние содержит спецификатор класса памяти static.

    Для С, если один и тот же идентификатор в пределах одного файла
    появляется и с внутренним, и с внешним типом компоновки, то
    идентификатор будет иметь внутренний тип компоновки.

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

 3. Если функция объявлена без спецификатора класса памяти, то
    ее тип компоновки определяется, как если бы был использован
    спецификатор класса памяти extern.

 4. Если идентификатор объекта с файловым контекстом объявлен без
    спецификатора класса памяти, то идентификатор имеет внешний тип
    компоновки.

      Следующие идентификаторы не имеют атрибута типа компоновки:

 1. Любой идентификатор, объявленный иначе, чем объект или функция
    (например, идентификатор typedef).

 2. Параметры функции.

 3. Идентификаторы с контекстом блока в случае объектов, объявленных
    без спецификатора класса памяти extern.

Синтаксис объявления

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

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

      Стандарт  ANSI  C  вводит  новую  концепцию:  предварительное
 определение.  Любое  объявление   внешних   данных,   не   имеющее
 спецификатора  класса памяти и инициализатора, рассматривается как
 предварительное  определение.   Если   объявленный   идентификатор
 появляется   в   последующем   определении,   то   предварительное
 определение  рассматривается,  как  если  бы  имелся  спецификатор
 класса   памяти   extern   .   Другими   словами,  предварительное
 определение становится простым объявлением ссылки.

      Если достигнут конец единицы трансляции, а для идентификатора
 так  и  не  было  встречено  определения  с  инициализатором,   то
 предварительное  определение становится полным определением, а для
 определенного     таким     образом     объекта      резервируется
 неинициализированная    (заполненная   нулями)   область   памяти.
 Например,

    int x;
    int x;         /* допустимо, резервируется одна копия x */

    int y;
    int y = 4;     /* допустимо, y инициализируется значением 4 */

    int z = 5;
    int z = 6;     /* недопустимо, т.к. оба определения
                      инициализированы */
                             Возможные объявления

      В число объектов, которые могут быть объявлены, входят:

 - переменные
 - функции
 - классы и компоненты классов (С++)
 - типы
 - теги структур, объединений и перечислимых данных
 - компоненты структур
 - компоненты объединений
 - массивы прочих типов
 - перечислимые константы
 - метки операторов
 - макросы препроцессора

      Полный синтаксис объявлений  показан  в  следующих  таблицах.
 Рекурсивная  природа  синтаксиса  описателей  позволяет  создавать
 сложные  описатели.  Для  улучшения   читаемости   мы   пошли   на
 использование определяемого типа (typedef).

 Синтаксис объявлений Turbo C++                       Таблица 1.10
 -----------------------------------------------------------------
 объявление:
    <спецификаторы-объявления> <список-деклараторов>
    объявление-asm
    объявление-функции
    спецификация-типа-компоновки

 спецификатор-объявления:
    спецификатор-класса-памяти
    спецификатор-типа
    спецификатор-функции
    friend (только для С++)
    typedef

 спецификаторы-объявления:
    <спецификаторы-объявления> спецификатор-объявления

 спецификатор-класса-памяти:
    auto
    register
    static
    extern

 спецификатор-функции: (только для С++)
    inline
    virtual

 спецификатор-типа:
    имя-простого-типа
    спецификатор-класса
    спецификатор-перечислимых-данных
    спецификатор-усовершенствованного-типа
    const
    volatile

 имя-простого-типа:
    имя-класса
    имя-typedef
    char
    short
    int
    long
    signed
    unsigned
    float
    double
    void

 спецификатор-усовершенствованного-типа:
    ключ-класса  идентификатор
    ключ-класса  имя-класса
    enum имя-перечислимых-данных

 ключ-класса: (только для С++)
    class
    struct
    union

 список-перечислимых-данных:
    нумератор
    список-нумераторов  нумератор

 нумератор:
    идентификатор
    идентификатор = выражение-типа-константы

 выражение-типа-константы:
    условное-выражение

 спецификация-типа-компоновки: (только для С++)
    extern строка { <список-объявления> }
    extern строка объявление

 список-объявления:
    объявление
    список-объявления  объявление
 -----------------------------------------------------------------

      Для  следующей  таблицы  отметим, что на количество и порядок
 модификаторов и квалификаторов наложены ограничения.  Кроме  того,
 перечисленные   модификаторы   являются  единственным  дополнением
 синтаксиса декларатора, не входящим  в  стандарт  ANSI  C  и  С++.
 Каждый модификатор подробно описан, начиная со стр.46 оригинала.

 Синтаксис декларатора Turbo C++                      Таблица 1.11
 -----------------------------------------------------------------
 список-декларатора:
    инициализатор-декларатор
    список-декларатора  инициализатор-декларатор

 инициализатор-декларатор:
    имя-декларатора
    список-модификаторов
    операция-указателя  декларатор
    декларатор (список-объявления-параметров)
               <список-со-квалификаторов>
    (список-со-квалификаторов - только для С++)
    декларатор [<выражение-типа-константы>]

 список-модификаторов:
    модификатор
    список-модификаторов  модификатор

 модификатор:
    cdecl
    pascal
    interrupt
    near
    far
    huge

 операция-указателя:
    * <список-со-квалификаторов>
    & <список-со-квалификаторов> (только для С++)
    имя-класса = *<список-со-квалификаторов> (только для C++)

 список-со-квалификаторов:
    со-квалификатор <список-со-квалификаторов>

 со-квалификатор:
    const
    volatile

 имя-декларатора:
    имя
    имя-класса (только для С++)
      имя-класса (только для С++)
    имя-typedef

 имя-типа:
    спецификатор-типа <абстрактный-декларатор>

 абстрактный-декларатор:
    операция-указателя <абстрактный-декларатор>
    <абстрактный-декларатор> (список-аргументов-объявления) <список-
         -со-квалификаторов>
    <абстрактный-декларатор> [<выражение-типа-константы>]

 список-объявления-аргументов:
    <список-объявления-арг>
    список-объявления-арг, ...
    <список-объявления-арг>...(только для С++)

 список-объявления-арг:
    объявление-аргументов
    список-объявления-арг  объявление-аргументов

 объявление-аргументов:
    спецификаторы-объявления  декларатор
    спецификаторы-объявления декларатор = выражение (только для С++)
    спецификаторы-объявления <абстрактный-декларатор>
    спецификаторы-объявления <абстрактный-декларатор> =выражение
                                                     (только для С++)

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

 тело-функции:
    составной оператор

 инициализатор:
    = выражение
    = (список-инициализаторов)
    (список-выражений) (только для С++)

 список-инициализаторов:
    выражение
    список-инициализаторов , выражение
    (список-инициализаторов<,>)
 -----------------------------------------------------------------
                       Внешние объявления и определения

      Спецификаторы  класса  памяти  auto  и  register  во  внешнем
 объявлении появиться не  могут  (см.  "Единицы  трансляции").  Для
 каждого идентификатора в единице трансляции, объявленной с внешним
 типом  компоновки,  может  существовать  не  более одного внешнего
 определения.

      Внешнее определение - это внешнее объявление,  которое  также
 определяет   объект   или   функцию;   таким  образом,  оно  также
 распределяет память. Если  идентификатор,  объявленный  с  внешним
 типом  компоновки,  используется в выражении (не являющемся частью
 операции sizeof), то во всей программе должно иметься  в  точности
 одно внешнее определение данного идентификатора.

      Turbo  C++ позволяет затем переобъявление внешних имен, таких
 как массивы, структуры и объединения, добавляя информацию к  ранее
 выполненным объявлениям. Например,

    int a[]                 // нет размера
    struct mystruct;        // только тег, без объявления компонентов
    ...
    int a[3] = [1, 2, 3];   // задание размера и инициализация
    struct mystruct {
         int i, j;
    };                      // добавление деклараторов компонентов

 Объявления классов Turbo C++ (только С++)            Таблица 1.12
 -----------------------------------------------------------------
 спецификатор-класса:
    заголовок-класса (<список-компонентов>)

 заголовок-класса:
    ключ-класса <идентификатор> <базовый-спецификатор>
    ключ-класса имя-класса <базовый-спецификатор>

 список-компонентов:
    объявление-компонента <список-компонентов>
    спецификатор-доступа : <список-компонентов>

 объявление-компонента:
    <спецификаторы-объявления><список-декларатора-компонентов>;
    определение-функции <;>
    квалифицированное-имя;

 список-декларатора-компонентов:
    декларатор-компонента
    список-декларатора-компонентов, декларатор-компонента

 декларатор-компонента:
    декларатор 
    <идентификатор> : выражение-типа-константы

 pure-спецификатор:
    =0

 базовый-спецификатор:
    :базовый-список

 базовый-список:
    базовый-спецификатор
    базовый-список, базовый-спецификатор

 базовый-спецификатор:
    имя-класса
    virtual <спецификатор-доступа> имя-класса
    спецификатор-доступа Юvirtual> имя-класса

 спецификатор-доступа:
    private
    protected
    public

 имя-функции-преобразования:
    operator имя-типа-преобразования

 имя-типа-преобразования:
    спецификация-типа <операция-указателя>

 инициализатор-конструктора:
    : список-инициализаторов-памяти

 список-инициализаторов-памяти:
    инициализатор-памяти
    инициализатор-памяти, список-инициализаторов-памяти

 инициализатор-памяти:
    имя-класса (<список-аргументов>)
    идентификатор (<список-аргументов>)

 имя-функции-операции:
    operator операция

 операция: одно из
    new delete sizeof

    +      -      *      /      %      ^
    &      |             !      =      <>
    +=     -=     *=     /=     %=     ^=
    &=     |=     <<     >>     >>=    <<=
    ==     !=     <=     >=     &&     ||
    ++     --     ,      ->*    ->     ()
    []     .*
 -----------------------------------------------------------------




Спецификаторы типа

      Спецификатор типа с одним или более опциональным модификатором
 используется для задания типа объявляемого идентификатора:

    int i;                   // объявление i как целого со знаком
    unsigned char ch1, ch2;  // объявление двух символьных без знака

      По устоявшейся традиции, если спецификатор типа опущен, то по
 умолчанию назначается тип signed int (или, что эквивалентно, int).
 Однако, в С++ встречаются ситуации, когда отсутствие спецификатора
 типа  ведет  к  синтаксической неоднозначности, поэтому в практике
 работы с С++ используется явный ввод всех спецификаторов типа int.
                                Таксономия типа

      Существует  четыре  базовые  категории  типа:  void,  scalar,
 function   и  aggregate.  Типы  scalar  и  aggregate  могут  далее
 подразделяться следующим образом:

 - Scalar: арифметический, перечислимый, указатель и в С++ ссылки

 - Aggregate: массив, структура, объединение и в С++ класс

      Типы   делятся   на   фундаментальные   и   производные.    К
 фундаментальным   относятся:  void,  char,  int,  float  и  double
 совместно с short, long, signed, а также некоторые варианты с ними
 unsigned. Производные типы включают в себя указатели и  ссылки  на
 другие  типы,  массивы  других  типов, типы функций, типы классов,
 структуры и объединения.

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

      Задав  не-пустой  тип type (с некоторыми предосторожностями),
 можно объявлять производные типы следующим образом:

 Объявление типов                                     Таблица 1.13
 -----------------------------------------------------------------
    type t;               Объект типа type

    type array[10];       Десять типов: array[0]-array[9]

    type *ptr;            ptr это указатель типа

    type &ref=t;          ref = ссылка на тип (C++)

    type func(void);      func возвращает значение типа type

    void func1(type t);   func1 принимает параметр типа type

    struct st (type t1; type t2);  структура st содержит два типа
 -----------------------------------------------------------------

      Ниже показано, как производные типы могут быть объявлены в
 пределах класса:

    class cl {     // класс cl содержит указатель ptr на тип, плюс
                   // функцию, принимающую параметр type (C++)
       type *ptr;
       public:
       void func(type*);

 Тип void

      Void это специальный спецификатор типа, указывающий на отсут-
 ствие каких-либо значений. Он задается в следующих ситуациях:

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

    int func(void);   // функция не принимает аргументов

      С++  обрабатывает   0   специальным   образом.   См.   раздел
 "Объявления  и  прототипы"  на  стр.60  и  примеры  кода на стр.61
 оригинала.

 - Когда объявленная функция не возвращает значений:

    void func(int n); // возвращаемое значение отсутствует

 - В качестве родового указателя. Указатель на void является родо-
   вым указателем на все что угодно:

    void *ptr;        // далее ptr может быть установлен на любой
                      // объект

 - Выражения с проверкой типа:

    extern int errfunc();    // возвращает код ошибки
    ...
    (void) errfunc();        // значение возврата теряется
                             Фундаментальные типы

      signed  и  unsigned  -  это   модификаторы,   применяемые   к
 интегральным типам.

      Фундаментальные  спецификаторы  типа  создаются  из следующих
 ключевых слов:

    char     int      signed
    double   long     unsigned
    float    short

      На базе этих ключевых слов вы можете  построить  интегральные
 типы  и типы с плавающей точкой, которые в совокупности называются
 арифметическими  типами.   Включаемый   файл   limits.h   содержит
 определения диапазонов значений для всех фундаментальных типов.

 Интегральные типы

      Типы  char,  short,  int и long, а также их варианты unsigned
 называются интегральными  типами.  Ниже  приводятся  спецификаторы
 интегральных типов с помещенными в той же строке синонимами.

 Интегральные типы                                    Таблица 1.14
 -----------------------------------------------------------------
    char,signed char   Если по умолчанию char установлен как signed,
                       то это синонимы
    unsigned char
    char,unsigned char Если по умолчанию char установлен как
                       unsigned, то это синонимы
    signed char
    int,signed int
    unsigned,unsigned int
    short,short int,signed short int
    unsigned short,unsigned short int
    long,long int,signed long int
    unsigned long,unsigned long int
 -----------------------------------------------------------------

      С  типами  char,  short, int или long можно использовать либо
 signed, либо unsigned. Если же использовать только  сами  ключевые
 слова signed или unsigned, то они означают signed int или unsigned
 int, соответственно.

      В  отсутствие  слова  unsigned обычно принимается тип signed.
 Исключение  возникает  в  случае   char.   Turbo   C++   позволяет
 устанавливать  для char умолчание signed или unsigned. (Если вы не
 устанавливали это умолчание сами, то  это  умолчание  будет  равно
 signed).  Если  умолчание  установлено  в  значение  unsigned,  то
 объявление char ch объявит ch как  unsigned.  Для  переопределение
 этого  умолчания нужно задать signed char ch. Аналогичным образом,
 если для char установлено умолчание signed, то для  объявления  ch
 как unsigned char следует явно указать unsigned char ch.

      С int можно использовать либо long, либо short. Если ключевые
 слова  long  или  short использованы сами по себе, то они означают
 long int или short int, соответственно.

      ANSI C не  устанавливает  размеры  внутреннего  представления
 этих  типов,  за исключением того, что размеры данных short, int и
 long образуют неубывающую  последовательность  "short  <=  int  <=
 long".  Все  три  типа могут быть одинаковыми. Это существенно для
 написания мобильных кодов, предназначенных для переноса на  другую
 аппаратную базу.

      В  Turbo C++ типы int и short эквивалентны, и имеют оба длину
 16 бит. long представляет 32-битовые объекты. Их  разновидность  с
 ключевым  словом  signed  хранятся  в  формате дополнения до двух,
 причем  в  качестве  знакового  бита  используется  MSB  (наиболее
 значащий  бит):  0 означает положительное число, 1 - отрицательное
 (что объясняет диапазоне, приведенные в таблице  1.9).  В  версиях
 unsigned  для  хранения  числа  используются  все  биты,  что дает
 диапазон 0-(2^n-1), где n = 8,16 или 32.

 Типы с плавающей точкой

      Представления и множества принимаемых значений  для  типов  с
 плавающей  точкой зависят от конкретной реализации; то есть каждая
 новая реализация компилятора С свободна определять  их  по-своему.
 Turbo  C++ использует форматы с плавающей точкой IEEE. (Приложение
 А,  "Стандарты  ANSI,  зависимые  от  реализации"  содержит  более
 подробную информацию по этому вопросу.)

      float  и  double  представляют  собой 32- и 64-разрядные типы
 данных с плавающей точкой, соответственно. long можно использовать
 с  double  для  получения   80-разрядной   точности   представления
 идентификатора   с   плавающей   точкой:  long  double  test_case,
 например.

      Распределяемая для типов с плавающей точкой память показана в
 таблице 1.9.

 Стандартные преобразования

      При использовании арифметических выражений,  таких  как  a+b,
 где  a  и b - это данные различных арифметических типов, Turbo C++
 выполняет    перед     вычислением     определенные     внутренние
 преобразования.  Эти  стандартные  преобразования  включают в себя
 перевод  "низших"  типов   в   "высшие"   в   интересах   точности
 представления и непротиворечивости данных.

      Ниже    приводятся    шаги,   выполняемые   Turbo   C++   для
 преобразования операндов в арифметических выражениях:

 1. Все малые интегральные типы преобразовываются согласно таблице
    1.15. После этого любые два значения, участвующие в операции,
    становятся либо Int (включая модификаторы long и unsigned),
    либо double, float или long double.

 2. Если один из операндов имеет тип long double, то второй операнд
    также будет преобразован к типу long double.

 3. Иначе, если один из операндов имеет тип double, то второй операнд
    также будет преобразован к типу double.

 4. Иначе, если один из операндов имеет тип float, то второй операнд
    также будет преобразован к типу float.

 5. Иначе, если один из операндов имеет тип unsigned long, то второй
    операнд также будет преобразован к типу unsigned long.

 6. Иначе, если один из операндов имеет тип long, то второй операнд
    также будет преобразован к типу long.

 7. Иначе, если один из операндов имеет тип unsigned, то второй
    операнд также будет преобразован к типу unsigned.

 8. В противном случае оба операнда имеют тип Int.

      Результат вычисления выражения будет того же типа, что и оба
 участвующих в нем операнда.

 Методы стандартных арифметических преобразований     Таблица 1.15
 -----------------------------------------------------------------
 Тип               Преобразование в     Метод
 -----------------------------------------------------------------
 char              int                  Расширение нулем или знаком
                                        (в зависимости от умолчания
                                        для типа char)

 unsigned char     int                  Заполнение старшего байта
                                        нулем (всегда)

 signed char       int                  Расширение знаком (всегда)

 short             int                  То же значение

 unsigned short    unsigned int         То же значение

 enum              int                  То же значение
 -----------------------------------------------------------------

 Специальные преобразования типов char, int и enum

      Обсуждаемые в данном разделе преобразования специфичны для
 Turbo C++.

      Присваивание объекта типа signed char (например,  переменной)
 интегральному  объекту  вызывает автоматическое расширение знаком.
 Объекты типа signed  char  использует  расширение  знаком  всегда;
 объекты  типа  unsigned  char  при  преобразовании  в  int  всегда
 устанавливают старший байт в ноль.

      Преобразование  более  длинных  интегральных  типов  к  более
 коротким  типам  ведет  к усечению старших битов, оставляя младшие
 без изменения. Преобразование более короткого интегрального типа к
 более длинному либо расширяет лишние биты нового значения  знаком,
 либо  заполняет их нулем, в зависимости от того, является ли более
 короткий тип signed или unsigned, соответственно.
                                 Инициализация

      Инициализаторы устанавливают исходное  значение,  хранимое  в
 объекте  (переменные,  массивы,  структуры,  и  т.д.).  Если вы не
 инициализируете объект, и он имеет  статическую  продолжительность
 существования, то он будет инициализирован по умолчанию, следующим
 образом:

 - нулем, если это объект арифметического типа
 - null, если что указатель

      В   случае   автоматического  распределения  памяти  исходное
 значение динамической локальной переменной непредсказуемо.

      Синтаксис инициализаторов следующий:
    инициализатор
       = выражение
       = {список-инициализаторов}<,>}
       (список выражений)

    список-инициализаторов
       выражение
       список-инициализаторов, выражение
       {список-инициализаторов}<,>}

      Ниже приводятся правила, управляющие инициализаторами:

 1. Число инициализаторов в списке инициализаторов не может пре-
    вышать число инициализируемых объектов.

 2. Инициализируемый элемент должен быть типа объекта или массивом
    неизвестной размерности.

 3. Все выражения должны являться константами, если они находятся
    в одном из следующих мест:

    а. в инициализаторе объекта, имеющего статическую длительность
       (в Turbo C++ не требуется)

    b. в списке инициализаторов для массива, структуры или объеди-
       нения (также допустимы выражения с использованием sizeof)

 4. Если объявление идентификатора имеет контекст блока, и иден-
    тификатор имеет внешнюю или внутреннюю компоновку, объявление
    не может иметь инициализатор для идентификатора.

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

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

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

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

 Массивы, структуры и объединения

      Вы  инициализируете  массивы  и структуры (если хотите, то во
 время объявления) при помощи заключенного в фигурные скобки списка
 инициализаторов для  компонентов  или  элементов  рассматриваемого
 объекта. Инициализаторы даются по возрастанию индекса массивов или
 номеров   компонентов.   Инициализация   объединений   выполняется
 заключенным  в  фигурные  скобки   инициализатором   для   первого
 компонента  объединения. Например, вы можете объявить массив days,
 предназначенный для подсчета того, сколько раз каждый день  недели
 был в том или ином месяце (предполагая, что каждый день недели был
 в месяце хотя бы один раз), следующим образом:

    int days[7] = { 1, 1, 1, 1, 1, 1, 1)

      Этими   правилами  можно  воспользоваться  для  инициализации
 символьных массивов и широких символьных массивов:

 1. Можно инициализировать массивы символьного типа с помощью строки
    литералов, опционально заключенной в фигурные скобки. Каждый
    символ строки, включая нулевой терминатор, инициализирует пос-
    ледовательно расположенные элементы массива. Например, вы могли
    объявить:

       char name[] = { "Unknown" };

    установив тем самым массив из восьми элементов, элементы кото-
    рого равны 'U'(для name[0]), 'n' (для name[1]), и т.д. (включая
    нулевой терминатор.)

 2. Вы можете инициализировать широкий символьный массив (то есть
    совместимый с wchar_t), используя широкий строковый литерал,
    опционально заключенный в фигурные скобки. Как и в случае сим-
    вольных массивов, коды широкого символьного литерала последова-
    тельно инициализируют элементы массива.

      Ниже приводится пример инициализации структуры:

    struct mystruct {
       int i;
       char str[21];
       double d;
    } s = { 20, "Borland", 3.14 };

      Сложные  компоненты  структуры,   такие   как   массивы   или
 структуры,    могут    быть    инициализированы   соответствующими
 выражениями во вложенных фигурных скобок.  Можно  убрать  фигурные
 скобки,  но  тогда  приходится выполнять дополнительные правила, и
 такая практика не рекомендуется.
                              Простые объявления

      Простые объявления идентификаторов переменных имеют следующий
 шаблон:

    тип-данных перем1 <=иниц1>, перем2 <=иниц2>,...;

 где перем1, перем2, ... это произвольная последовательность от-
 дельных идентификаторов с опциональными инициализаторами. Каждая
 из переменных объявляется с указанным типом-данных. Например,

    int x = 1, y = 2;

 создает две целочисленных переменных x и y (и инициализирует их
 значениями 1 и 2, соответственно).

      Это были  объявления  определения;  при  этом  распределялась
 память и выполнялась инициализация.

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

      В C++ инициализатор статического объекта может являться любым
 выражением,  включающим  в  себя  константы  и  ранее определенные
 переменные и функции.
                          Спецификаторы класса памяти

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

    auto     register     typedef
    extern   static

 Использование спецификатора класса памяти auto

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

 Использование спецификатора класса памяти extern

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

 Использование спецификатора класса памяти register

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

      Turbo  C++ позволяет вам выбрать опции размещения переменных в
 регистрах в диалоговом поле Options | Compiler | Optimization. При
 выборе опции Automatic  Turbo  C++  сделает  попытку  распределить
 регистры   даже  если  спецификаторы  класса  памяти  register  не
 задавались.

 Использование спецификатора класса памяти static

      Спецификатор класса  памяти  static  может  использоваться  в
 объявлениях  функций  и  переменных с контекстом файла и локальным
 контекстом для обозначения  внутреннего  типа  компоновки.  Static
 также   указывает,   что   переменная   должна  иметь  статическую
 продолжительность существования. При отсутствии конструкторов  или
 явных  инициализаторов  статические  переменные инициализируются 0
 или null.

      В С++ компоненты класса,  статические  данные,  имеет  то  же
 значение  для  всех  вхождений  класса.  Члены класса, статические
 функции, не зависят от других вхождений класса.

 Использование спецификатора класса памяти typedef

      Ключевое слово typedef означает, что вы не объявляете объект,
 а определяете спецификатор нового типа данных. typedef  включается
 в  качестве спецификатора класса памяти вследствие синтаксических,
 а не функциональных аналогий.

    static long int biggy;
    typedef long int BIGGY;

      Первое объявление создает 32-битовый объект типа long int, со
 статической  продолжительностью  существования  и  именем   biggy.
 Второе  объявление  устанавливает  идентификатор  BIGGY в качестве
 спецификатора  нового  типа,  не  создавая  при  этом  какого-либо
 объекта времени выполнения. BIGGY можно использовать в последующих
 объявлениях,   там,  где  допустимо  задавать  спецификатор  типа.
 Например,

    extern BIGGY salary;

 имеет тот же эффект, что и

    extern long int salary;

      Хотя  данный  простой  пример  может  быть   равным   образом
 реализован  при  помощи  #define  BIGGY  long int, в более сложных
 случаях typedef позволяет  добиться  большего,  нежели  с  помощью
 текстовых подстановок.

 Важное замечание!

      typedef  не  создает  новых  типов данных; это ключевое слово
 просто создает полезные мнемонические синонимы,  или  алиасы,  для
 существующих  типов.  Это  особенно  полезно для упрощения сложных
 объявлений:

    typedef double (*PFD)();
    PFD array-pfd[10];
    /* array_pfd это массив из 10 указателей на функции,
       возвращающие значения типа double  */

      Нельзя использовать идентификаторы typedef со спецификаторами
 других типов:

    unsigned BIGGY pay;   /* НЕДОПУСТИМО */
                                 Модификаторы

      Помимо ключевых слов спецификатора класса памяти,  объявление
 может  использовать  конкретные  модификаторы, предназначенные для
 изменения некоторых аспектов распределения памяти  идентификатора/
 объекта.  В  следующей  таблице  сведены модификаторы, имеющиеся в
 Turbo C++.

 Модификаторы Turbo C++  Таблица 1.16
 -----------------------------------------------------------------
 Модификатор   Используется с      Использование
 -----------------------------------------------------------------
 const         Только переменными  Предотвращает изменения объекта

 volatile      Только переменными  Предотвращает распределение реги-
                                   стров и некоторые виды оптимиза-
                                   ции. Предупреждает компилятор о
                                   том, что объект при вычислении
                                   может получить какие-либо измене-
                                   ния извне.

 В С++ const и volatile расширены и включают классы и функции.


 Расширения Turbo C++

 cdecl         Функции             Устанавливает соглашения С пере-
                                   дачи аргументов

 cdecl         Переменные          Устанавливает учет регистра иден-
                                   тификатора и ведущие знаки подчер-
                                   кивания

 pascal        Функции             Устанавливает соглашения пере-
                                   дачи аргументов Паскаля

 pascal        Переменные          отменяет учет регистра идентифи-
                                   катора и ведущие знаки подчерки-
                                   вания

 interrupt     Функции             Функция компилируется с дополни-
                                   тельным кодом управления реги-
                                   стром, необходимыми при написании
                                   обработчиков прерываний

 near,         Переменные          Переопределяет умолчание типа
 far,          указатели           указателя, задаваемое текущей
 huge                              моделью памяти

 _cs,          Переменные          Указатели сегмента;
 _ds,          указатели           см. стр.199 оригинала
 _es,
 _seg,
 _ss

 near,         Функции             Переопределяет умолчание типа
 far,                              функции, задаваемое текущей
 huge                              моделью памяти

 near,         Переменные          Определяет размещение объекта в
 far,                              памяти

 _export       Функции             Только OS/2. Turbo C++ это
                                   игнорирует

 _loadds       Функции             Устанавливает регистр DS на
                                   текущий сегмент данных

 _saveregs     Функции             Предохраняет все значения регис-
                                   тров (кроме значений возврата)
                                   во время выполнения функции
 -----------------------------------------------------------------

 Модификатор const

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

    const float  pi     = 3.1415926;
    const        maxint = 32767;
    char  *const str    = "Hello, world!"; // указатель константа
    char  const  *str2  = "Hello, world!"; // указатель на константу

      Использование одного только модификатора  const  эквивалентно
 const int.

      С учетом этого, следующие операторы являются недопустимыми:

    pi  = 3.0;           /* присвоение значения константе */
    i   = maxint++;      /* инкремент константы */
    str = "Hi, there!";  /* установка указателя str на что-то еще

      Однако,  отметим,  что вызов функции strcpy(str,"Hi, there!")
 является   допустимым,   поскольку   он   выполняет   посимвольное
 копирование  из  строкового  литерала "Hi, there!" в адрес памяти,
 указываемый str.

      В С++ const также "скрывает"  объект  const  и  предотвращает
 внешнюю  компоновку.  При  необходимости нужно использовать extern
 const. Указатель на const не  может  быть  присвоен  указателю  на
 неconst  (в  противном  случае  значению  const  могло  было  быть
 выполнено присвоение при помощи указателя на не-const.) Например,

    char *str3 = str2  /* запрещено */

 Модификатор функции прерывания interrupt

      Модификатор  interrupt  специфичен  для  Turbo  C++.  Функции
 прерывания   предназначены   для  работы  с  векторами  прерывания
 8086/8088.   Turbo   C++   компилирует   функцию    interrupt    с
 дополнительным   кодом   входа  и  выхода,  таким  образом,  чтобы
 сохранялись регистры AX, BX, CX, DX,  SI,  DI,  ES  и  DS.  Прочие
 регистры  (BP,  SP,  SS, CS и IP) сохраняются как часть вызывающей
 последовательности С или как часть самого обработчика  прерываний.
 Для  возврата  функция  использует  команду  Iret, поэтому функция
 может  служить  для  обслуживания   аппаратных   или   программных
 прерываний. Ниже показан пример типичного определения interrupt:

    void interrupt myhandler()
    {
      ...
    }

      Вы  должны объявлять функции прерывания с типом void. Функции
 прерывания могут быть объявлены с любой моделью памяти.  Для  всех
 моделей  памяти,  кроме huge, DS устанавливается на сегмент данных
 программы. В случае модели  huge  DS  устанавливается  на  сегмент
 данных модуля.

 Модификатор volatile

      В   C++  volatile  имеет  специальное  значение  для  функций
 компонентов класса. Если вы объявили объект  volatile,  вы  можете
 использовать для него только функции компонентов volatile.

      Модификатор  volatile указывает, что данный объект может быть
 модифицирован не только вами, но также и  извне  вашей  программы,
 например,   подпрограммой   прерывания  или  портом  ввода/вывода.
 Объявление объекта volatile предупреждает компилятор, что  тот  не
 должен  делать  допущений  относительно  значения объекта во время
 расчета  содержащих  его   выражений,   поскольку   его   значение
 (теоретически)  может  в любой момент измениться. Компилятор также
 не будет делать такую переменную регистровой переменной.

    volatile  int  ticks;
    interrupt  timer()
    {
       ticks++;
    }
    wait(int interval)
    {
       ticks = 0;
       while (ticks < interval);  // не делает ничего

      Эти подпрограммы (предполагается, что timer правильно связана
 с аппаратным прерыванием  часов)  реализуют  выдержку  по  времени
 между  "тиканьем"  часов,  заданную  аргументом  interval.  Высоко
 оптимизированный компилятор не может загружать  значение  ticks  в
 проверку  выхода из цикла while, так как цикл не изменяет значения
 ticks.

 Модификаторы cdecl и pascal
      Turbo  C++  позволяет   вашим   программам   легко   вызывать
 подпрограммы,   написанные  на  других  языках,  и  наоборот.  При
 смешанном программировании приходится иметь дело с  двумя  важными
 вопросами: идентификаторы и передача параметров.

      В Turbo C++ все глобальные идентификаторы сохраняются в своем
 исходном  виде  (строчные,  заглавные  буквы  и  их  комбинации) с
 присоединенным в начале идентификатора знаком  подчеркивания  (_),
 если  вы  не  выберете  опцию  -u-  (Generate  Underbars...Off)  в
 диалоговом поле Options | Compiler | Code Generation).

      На стр.32 оригинала рассказано, как использовать extern,  что
 позволяет ссылаться на имена С из программы на C++.

 pascal

      В  Паскале  глобальные  идентификаторы не сохраняются в своем
 исходном виде и не  имеют  первым  символом  знак  подчеркивания.
 Turbo C++ позволяют объявлять любые идентификаторы как имеющие тип
 pascal;  тогда  такой  идентификатор  преобразовывается к верхнему
 регистру,  и  ему  не  предшествует  знак   подчеркивания.   (Если
 идентификатор   является   функцией,   то   что  также  влияет  на
 используемую последовательность передачи  параметров;  подробности
 см. на стр. 51 оригинала, "Модификаторы типа функции".)

      Опция   компилятора   -p   (Calling   Convention...Pascal   в
 диалоговом поле Options | Compiler  |  Code  Generation)  вызывает
 обработку  функций  (и  указателей на эти функции) как если бы они
 имели тип pascal.

      Модификатор pascal специфичен для Turbo C++; он  предназначен
 для     функций     (и     указателей    функций),    использующих
 последовательность  передачи  параметров  Паскаля.   Кроме   того,
 функции, объявленные с типом pascal, могут тем не менее вызываться
 из  подпрограмм  С,  если  последним  известно, что данная функция
 имеет тип pascal.

    pascal putnums(int i, int j, int k)
    {
       printf("And the answers are: %d, %d, and %j\n",i,j,k);
    }

      Функции типа  pascal  не  могут  принимать  переменное  число
 аргументов,  в  отличие  от  таких  функций, как printf. Поэтому в
 определении функции  типа  pascal  использовать  многоточие  (...)
 нельзя.

 cdecl

      Программа  main  должна  быть  объявлена как cdecl, поскольку
 загрузочный код С всегда пытается вызвать главную процедуру (main)
 по соглашениям С.

      После  компиляции   программы   с   опцией   -p   вам   может
 понадобиться,  чтобы некоторые идентификаторы сохранили регистр, в
 котором  они  были   первоначально   набраны,   и   ведущий   знак
 подчеркивания,  особенно  если  это  идентификаторы  С  из другого
 файла. Это можно сделать, объявив эти  идентификаторы  как  cdecl.
 (Это также влияет на передачу параметров функциям).

      Как  и pascal, модификатор cdecl специфичен для Turbo C++. Он
 используется с функциями и указателями функций.  Этот  модификатор
 переопределяет директиву компилятора -p и позволяет вызывать такую
 функцию  как правильную функцию С. Например, если вы компилируете
 предыдущую  программу  с  установленной  опцией  -p,  но   желаете
 использовать printf, то нужно сделать следующее:

    extern cdecl printf();
    putnums(int i, int j, int k);
    cdecl main()
    {
       putnums(1,4,9);
    }
    putnums(int i, int j, int k)
    {
       printf("And the answers are: %d, %d, and %j\n",i,j,k);
    }

      При  компиляции  такой  программы  с опцией -p все функции из
 библиотеки исполняющей системы должны иметь объявление cdecl. Если
 вы посмотрите файлы заголовка (такие как stdio.h), вы увидите, что
 с учетом этого каждая функция определена там как cdecl.

 Модификаторы указателей

      Turbo C++ имеет восемь модификаторов,  влияющих  на  операцию
 обращения  по  ссылке, то есть на модификацию указателей в данные.
 Эти модификаторы: near, far, huge, _cs, _ds, _es, _seg и _ss.

      С позволяет выполнять компиляцию с  использованием  одной  из
 нескольких  моделей  памяти.  Используемая  вами модель определяет
 (помимо всего прочего) внутренний формат указателей. Например, при
 использовании  малой  данных  small  (tiny,  small,  medium)   все
 указатели  данных  содержат  16-битовое смещение регистра сегмента
 данных (DS). При использовании  большой  модели  данных  (compact,
 large,  huge)  все указатели данных имеют длину 32 бита и содержат
 как адрес сегмента, так и смещение.

      Иногда,  используя один  размер  модели  данных,   вам   может
 понадобиться   объявить  указатель,  размер  или  формат  которого
 отличен  от  текущего  по  умолчанию.  Это  делается   с   помощью
 модификаторов указателей.

      Углубленное  рассмотрение  указателей near, far и huge см. на
 стр.  192  оригинала  в  главе  4,  а   описание   нормализованных
 указателей  см. на стр. 193 оригинала. Кроме того, см. на стр. 199
 оригинала обсуждение _cs, _ds, _es, _seg и _ss.

 Модификаторы типа функции

      Модификаторы near, far и huge могут также использоваться  как
 модификаторы  типа функции; т.е., они могут модифицировать, помимо
 указателей данных, функции и указатели функций.  Кроме  того,  для
 модификации  функций могут служить модификаторы _export, _loadds и
 _saveregs.

      Модификаторы функций near, far и huge могут комбинироваться с
 модификаторами cdecl или pascal, но не с interrupt.

      Функции типа huge полезны для интерфейса с  кодами  на  языке
 ассемблера,   не   использующими   такое  же,  как  в  Turbo  С++,
 распределение памяти.

      Функции,  не  имеющие  модификатора  interrupt,  могут   быть
 объявлены  как  near,  far  или  huge  с тем, чтобы переопределить
 установки текущей модели памяти по умолчанию.

      Функция near использует ближние (near)  вызовы;  функция  far
 или huge использует дальние (far) команды вызова.

      В  случае  моделей  памяти tiny, small и compact функция, где
 это не было задано явно, имеет по умолчанию тип  near.  В  моделях
 medium и large по умолчанию функция имеет тип far. В модели памяти
 huge по умолчанию используется тип huge.


      Функция huge аналогична функции far, за исключением того, что
 при  входе  в  функцию  huge  регистр  DS устанавливается на адрес
 сегмента данных исходного модуля,  но  для  функции  far  остается
 неустановленным.

      Модификатор     _export    лексически    анализируется,    но
 игнорируется. Он обеспечивает совместимость с исходными  модулями,
 написанными  для  OS/2.  Для  программ  в  DOS модификатор _export
 никакого значения не имеет.

      Модификатор   _loadds   указывает,   что    функция    должна
 устанавливать  регистр  DS аналогично тому, как это делает функция
 huge, но не подразумевает вызовов near  или  far.  Таким  образом,
 _loadds far эквивалентно объявлению huge.

      Модификатор   _saveregs   заставляет  функцию  сохранить  все
 значения регистров и затем восстановить  их  перед  возвратом  (за
 исключением   явных   значений   возврата,  передаваемых  в  таких
 регистрах AX или DX.)

      Модификаторы  _loadds  и  _saveregs  полезны  при   написании
 подпрограмм  интерфейса нижнего уровня, как например, подпрограммы
 поддержки мыши.
                       Сложные объявления и деклараторы

      Синтаксис декларатора см. на  стр.35  оригинала.  Определение
 включает в себя деклараторы идентификаторов и функций.

      Простые   объявления  содержат  список  разделенных  запятыми
 идентификаторов, за которыми  следуют  опциональные  спецификаторы
 класса памяти, спецификаторы типа и прочие модификаторы.

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

      Формат декларатора указывает на то, каким образом объявленное
 имя-декларатора  должно  интерпретироваться  при  использовании  в
 выражениях.  Если type это любой тип, а спецификатор-класса-памяти
 это любой спецификатор класса памяти, то объявление

    спецификатор-класса-памяти  type  D1, D2;

 указывает, что каждое вхождение D1 или D2 в  выражение  будет
 рассматриваться    как   объект   типа   "type"   и   с   заданным
 "классом-памяти". Тип имени-декларатора, входящего  в  декларатор,
 должно  быть  некоторой  фразой, содержащей type, например "type",
 "pointer to type", "array of type", "function returning type"  или
 "pointer to function returning type", и т.д.

      Например, в объявлениях

    int n, nao[], naf[3], *pn, *apr[], (*pan)[], &nr=n
      int f(void), *frp(void), (*pfn)(void);

 каждый из деклараторов мог бы быть использован в качестве значе-
 ния переменной (правой части) (или, возможно, в некоторых случаях
 в качестве именующего выражения (левой части) ) в выражениях, где
 допустим один объект int. Типы встроенных идентификаторов берутся
 из их деклараторов следующим образом:

 Сложные объявления                                   Таблица 1.17
 -----------------------------------------------------------------
 Синтаксис        Подразумеваемый тип имени         Пример
 -----------------------------------------------------------------
  type имя;       type                              int count;
  type имя[]      (открытый) массив array of type   int count[1];
  type имя[3];    Фиксированный массив из трех      int count[3];
                  элементов типа type
                  (name[0],name[1],name[3])
  type *имя;      Указатель на type                 int *count;
  type *имя[];    (открытый) массив указателей      int *count[];
  type *(имя[])   То же самое                       int *(count[]);
  type (*имя)[];  Указатель на (открытый) массив    int (*count)[];
                  типа type
  type &имя;      Ссылка на тип type (только С++)   int &count;
  type имя();     Функция, возвращающая тип type    int count();
  type *имя();    Функция, возвращающая указатель   int *count();
                  на тип type
  type *(имя());  То же самое                        int *(count());
  type (*имя)();  Указатель на функцию, возвращающую int (*count)();
                  тип type
 -----------------------------------------------------------------

      Отметим  необходимость  круглых скобок в (*имя)[] и (*имя)(),
 поскольку приоритет декларатора массива [] и  декларатора  функции
 ()  выше,  чем  декларатора указателя *. Круглые скобки в *(имя[])
 опциональны.

                                   Указатели

      Обсуждение   создания   ссылок   и   обращения   по   ссылкам
 (разыменования) см. на стр.80 оригинала.

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

      Два  эти  класса  указателей  имеют  отличные  друг  от друга
 свойства, назначения и правила манипулирования, хотя и те и другие
 разделяют между собой  определенные  операции  Turbo  C++.  Вообще
 говоря,  указатели  функций  используются для доступа к функциям и
 для  передачи  одних  функций  другим   в   качестве   аргументов;
 выполнение   арифметических  операций  с  указателями  функций  не
 допускается.  И  напротив,  указатели  объектов  при  сканировании
 массивов    или    более   сложных   структур   памяти   регулярно
 инкрементируются и декрементируются.

      Хотя указатели содержат числа  с  большинством  характеристик
 типа   unsigned   int,   они  имеют  свои  собственные  правила  и
 ограничения на присвоения,  преобразования  и  выполнение  с  ними
 арифметических  действий.  Примеры в следующих нескольких разделах
 иллюстрируют эти правила и ограничения.


Указатели объектов

      "Указатель на объект  типа  type"  содержит  адрес  (то  есть
 указывает)  объекта  с типом type. Поскольку указатель сам по себе
 является объектом, то вы можете установить указатель на  указатель
 (и   т.д.).   В   число   прочих   объектов,   на  которые  обычно
 устанавливается указатель, входят массивы, структуры,  объединения
 и классы.

      Размер  указателей  объектов зависит обычно от модели памяти,
 размера и расположения сегментов данных,  а  также,  возможно,  от
 опциональных модификаторов указателей (этот вопрос рассматривается
 на стр.51 оригинала).




                               Указатели функций

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

      Указатель  функции имеет тип "указатель функции, возвращающей
 тип type", где type есть тип возвращаемых функцией данных.

      В С++, где контроль типов  данных  более  строгий,  указатель
 функции  имеет  тип  "указатель функции принимающей агрументы типа
 type и возвращающей тип type". Действительно, в С функция, опреде-
 ленная с типами аргументов, также будет иметь данный, более  узкий
 тип. Например,

    void (*func)();

      В С это будет указатель функции, не возвращающей никаких зна-
 чений.  В  С++ это указатель функции, не принимающей никаких аргу-
 ментов и не возвращающей никаких значений. В примере

    void(*func)(int);

 *func это указатель функции, принимающей аргумент int и не возвра-
 щающей никаких значений.
                             Объявления указателей

      Подробное описание типа void см. на стр.39 оригинала.

      Объявление указателя всегда должно устанавливать его на неко-
 торый конкретный тип, даже если этот тип void (что фактически  оз-
 начает  указатель на любой тип). Однако, уже после объявления ука-
 затель обычно может быть  переназначен  на  объект  другого  типа.
 Turbo C++ позволяет переназначать указатели без приведения в соот-
 ветствие  типа, но компилятор выдаст при этом предупреждение, если
 только первоначально указатель не был объявлен с типом void.  В  С
 (но не в С++) вы можете назначить указатель void* на указатель, не
 имеющий тип void*.

      Если  type есть любой предопределенный или определенный поль-
 зователем тип, включая void, то объявление

    type *ptr;  /* Опасно - неинициализированный указатель */

 объявит ptr как "указатель на тип type". К объявленному таким  об-
 разом  объекту  ptr применимы все правила, связанные с контекстом,
 продолжительностью и видимостью.

      Указатель со значением null это адрес, гарантированно  отлич-
 ный  от  любого  допустимого указателя, используемого в программе.
 Присвоение указателю целой константы 0 присваивает указателю  зна-
 чение null.

      Указатель  типа "указатель на void" не следует путать с нуле-
 вым (null) указателем. Объявление

    void *vptr;

 объявляет, что vptr - это родовой указатель, которому  может  быть
 присвоено любое значение "указатель на тип type" без выдачи компи-
 лятором  сообщений.  Присвоения  без  правильного приведения типов
 между "указателем на тип type1" и "указателем на тип  type2",  где
 type1 и type2 это различные типы, может вызвать предупреждение или
 ошибку  компилятора. Если type1 это функция, а type2 нет (или нао-
 борот), присваивания указателей недопустимы. Если type1 это указа-
 тель на void, приведения типов не требуется. Если type2 это указа-
 тель на тип void, то в С приведение не нужно.

      Ограничения присвоения также существуют относительно указате-
 лей разных размеров (near, far и huge).  Можно  присвоить  меньший
 указатель большему, не вызвав ошибки, но нельзя выполнить обратную
 операцию, не выполнив явную операцию приведения. Например,

    char near *ncp;
    char far  *fcp;
    char huge *hcp;
    fcp = ncp;              // допустимо
    hcp = fcp;              // допустимо
    fcp = hcp;              // недопустимо
    scp = fcp;              // недопустимо
    scp = (char nesr*)fcp;  // теперь допустимо
                             Указатели и константы

      Указатели  или указываемые ими объекты могут быть объявлены с
 модификатором const. Присвоение объекту, объявленному  как  const,
 не  допускается.  Также не допускается создание указателя, который
 может нарушить запрещение на модификацию объекта  типа  константы.
 Рассмотрим следующие примеры:

    int i;                    // i это целое;
    int * pi;                 // pi это указатель на i
                              // (неинициализированный)
    int * const cp = &i;      // cp это указатель-константа на int
                              // const
    int ci = 7;               // ci это константа int const
    int * pci;                // pci это указатель  на  константу ci
    const int * const cpc = &ci; // cpc это указатель-константа
                              // на константу int

      Следующие присвоения допустимы:

    i = ci;                   // Присвоить const int переменной int
    *cp = ci;                 // Присвоение const int объекту, на
                              // который указывает
                              // указатель-константа
    ++pci;                    // Инкремент указателя на константу
    pci = cpc;                // Присвоение константы-указателя-на
                              // константу указателю-на-константу

      Следующие присвоения недопустимы:

    ci = 0;                  // Присвоение значений константе
                             // const int недопустимо
    ci--;                    // Изменение константы недопустимо
    *pci = 3;                // Присвоение объекту, на который
                             // указывает указатель-на-константу
                             // недопустимо
    cp = &ci;                // Присвоение константе-указателю,
                             // даже если ее значение не будет
                             // изменено, недопустимо
    cpc++;                   // Изменять указатель-константу
                             // недопустимо
    pi = pci;                // Если бы такое присвоение было
                             // разрешено, вы могли бы присваивать
                             // *pci (константе), присваивая *pi
                             // что недопустимо

      Аналогичные правила относятся и к модификатору volatile.  От-
 метим,  что const и volatile могут появляться в качестве модифика-
 торов одного и того же идентификатора.
                     Арифметические операции с указателями

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

      Арифметические операции с указателями  ограничены  сложением,
 вычитанием и сравнением. Арифметические операции с указателями об-
 ъектов типа "указатель на тип type" автоматически учитывают размер
 этого  типа, то есть число байт, необходимое для хранения в памяти
 объекта данного типа.

      При выполнении арифметических операций с указателями  предпо-
 лагается,  что указатель указывает на массив объектов. Таким обра-
 зом, если указатель объявлен как указатель на type, то прибавление
 к нему целочисленного значения перемещает указатель на  соответст-
 вующее количество объектов type. Если type имеет размер 10 байтов,
 то  прибавление  целого  числа 5 к указателю этого типа перемещает
 указатель в памяти на 50 байт. Разность представляет  собой  число
 элементов  массива, разделяющих два значения указателей. Например,
 если ptr1 указывает на третий элемент массива, а ptr2 на  десятый,
 то результатом выполнения вычитания ptr2 - ptr1 будет 7э

      Когда  с "указателем на тип type" выполняется операция сложе-
 ния или вычитание целого числа, то результат также будет "указате-
 лем на тип type". Ецли type неявляется массивом, то операнд указа-
 теля будет рассматриваться как указатель на первый элемент "масси-
 ва типа type" длиной sizeof(type).

      Конечно, такого элемента, как "указатель на следующий за пос-
 ледним элемент", однако указатель может  принимать  это  значение.
 Если P указывает на последний элемент массива, то значение P+1 до-
 пустимо,  но P+2 неопределено. Если P указывает на элемент за пос-
 ледним элементом массива, то допустимо значение P-1, когда  указа-
 тель  установлен  на  последний  элемент массива. Однако установка
 указателя на элемент за последним элементом массива ведет  к  неп-
 редсказуемым результатам работы программы.

      Для  информации:  P+n  можно представить себе как перемещение
 указателя на (n*sizeof(type)) байт вперед, пока указатель остается
 в допустимых границах (не далее первого за концом массива  элемен-
 та).

      Вычитание  между  двумя указателями на элементы одного и того
 же массива дает интегральное значение типа ptrdiff_t, определенное
 в stddef.h (signed long для указателей huge и far; signed int  для
 всех  прочих).  Данное  значение представляет собой разность между
 индексами двух указанных элементов, при условии вхождения в диапа-
 зоне ptrdiff_t. В выражении P1P2, где P1 и P2 это указатели на тип
 type (или указатели на квалифицированный тип), P1 и P2 должны ука-
 зывать на существующие элементы или на следующий за последним эле-
 мент. если P1 указывает на i-й элемент, а P2 указывает на j-й эле-
 мент, то P1-P2 имеет значение (i-j).

Преобразования указателей

      Указатели одного типа могут быть  преобразованы  в  указатели
 другого типа при помощи следующего механизма приведения типов:

    char *str
    int *ip
    str = (char*)ip;

      В  более общем виде, приведение (type*) преобразует указатель
 в тип "указатель на тип type".

Объявления ссылок в С++

      Ссылочные типы С++ тесно связаны с типами указателей. Ссылоч-
 ные типы создают алиасы объектов и позволяют  передавать  функциям
 аргументы  по ссылке. Традиционно передача аргументов в С выполня-
 ется только по значению. В С++ передавать аргументы можно  как  по
 значению,  так и по ссылке. Полную информацию см. в разделе "Ссыл-
 ки" на стр.98 оригинала.
                                    Массивы

      Данный раздел начинает часть главы, посвященную  рассмотрению
 грамматики структуры фраз языка; описание различий между граммати-
 ческими правилами лексики и структуры фраз языка см. на стр.4.

      Объявление

    type декларатор [<выражение-типа-константы>]

 объявляет масив, состоящий из элементов типа type. Массив в С сос-
 тоит  из непрерывной области памяти, по размеру позволяющей в точ-
 ности разместить все его элементы.

      Если в деклараторе массива задано выражение, то при  его  вы-
 числении  должна получаться положительная целочисленная константа.
 Получившееся значение представляет собой число элементов  массива.
 Каждый  из  элементов  массива  нумеруется от 0 до числа элементов
 массива, минус единица.

      Многомерные массивы создаются путем  объявления  массивов  из
 элементов  типа  массив.  Таким  образом, двумерный массив из пяти
 строк и семи столбцов с именем alpha объявляется  следующим  обра-
 зом:

    type alpha [5] [7];

      В  определенном  контексте  первый декларатор массива из нес-
 кольких может не иметь выражения в квадратных скобках. Такой  мас-
 сив  имеет  неопределенный размер. Контекстом, где допустимо такое
 положение, является тот случай, когда  для  резервирования  памяти
 размер массива не требуется. Например, для объявление объекта типа
 массива extern точный размер массива не требуется; не требуется он
 и  при передаче функции параметра типа массива. Будучи специальным
 расширением ANSI C, Turbo C также позволяет объявлять  в  качестве
 последнего  элемента структуры массив неопределенного размера. Та-
 кой массив не увеличивает размера структуры,  а  для  того,  чтобы
 обеспечить  правильное выравнивание структуры, ее можно специально
 дополнить символами-заполнителями. Такие структуры обычно  исполь-
 зуются при динамическом распределении памяти, когда для правильно-
 го  резервирования области памяти к размеру структуры следует явно
 прибавить фактический размер необходимого массива.

      За исключением использования массива в качестве операнда опе-
 рации sizeof или &, выражение с типом массива преобразуется в кон-
 станту-указатель на первый элемент массива.

Функции

      Функции представляют собой центральный вопрос  программирова-
 ния на Turbo C++. Такие языки программирования, как Паскаль, дела-
 ют различие между процедурами и функциями. В Turbo C++ функции иг-
 рают обе роли.
                           Объявления и определения

      Каждая  программа должна иметь одну внешнюю функцию main, со-
 держащую точку входа в программу. Обычно функции  объявляются  как
 прототипы в стандартных или создаваемых пользователем файлах заго-
 ловка,  либо  в  файлах  программы. По умолчанию функции имеют тип
 extern, и доступ к ним возможен из любого файла программы. Функция
 может быть ограничена спецификатором  класса  памяти  static  (см.
 стр. 32 оригинала).

      Функции  объявляются в исходных файлах, либо делаются доступ-
 ными при компоновке с откомпилированными библиотеками.

      В С++ вы должны всегда пользоваться прототипами  функции.  Мы
 рекомендуем также всегда использовать их и в С.

      Данная  функция  может  быть  объявлена в программе несколько
 раз, при условии, что эти  объявления  совместимы.  Неопределяющие
 объявления  функции, использующие формат прототипа функции предос-
 тавляют Turbo C++ детальную информацию о параметрах, что позволяет
 лучшее управление числом аргументов, контролем их типа и  преобра-
 зованиями типов.

      За  исключением перегрузки функции в С++, допустимым является
 только одно объявление данной функции в программе. Объявления, ес-
 ли они имеются, должны соответствовать определению  функции.  (Су-
 щественным различием между определением и объявлением является то,
 что определение содержит собственно тело функции.)
                            Объявления и прототипы

      В  оригинальном  стиле  объявлений  Кернигэна и Ритчи функция
 могла быть либо объявлена неявно, по ее вызову, либо явно:

     func()

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

      Эта задача упрощается благодаря введению прототипа функции со
 следующим синтаксисом объявления:

     func(список-деклараторов-параметров)

      При  помощи  IDE или опции компилятора командной строки можно
 разрешить выдачу следующего предупреждения: "Function called with-
 out a prototype" ("Функция вызывается без прототипа").

      Деклараторы определяют типы каждого параметра функции. Компи-
 лятор использует эту информацию для контроля достоверности  вызова
 функции.  Компилятор также может приводить аргументы к нужному ти-
 пу. Предположим, что у вас имеется следующий фрагмент кода:

    long lmax(long v1, long v2);  /* прототип */
    main()
    {
       int limit = 32;
       char ch = 'A';
       long mval;
       mval = lmax(limit,ch):    /* вызов функции */

      Поскольку данная программа имеет прототип функции  для  lmax,
 данная  программа  преобразовывает limit и ch к типу long по стан-
 дартным правилам присвоения, прежде чем поместить их  в  стек  для
 вызова  lmax.  Без прототипа функции limit и ch были бы помещены в
 стек как целое и символьное значения, соответственно; в этом  слу-
 чае  стек,  переданный lmax, не соответствовал бы по размеру и со-
 держимому тому, что ожидает на входе lmax, что привело бы  к  воз-
 никновению проблем. Классический стиль объявлений не позволяет вы-
 полнять  контроль  типа  и числа параметров, поэтому использование
 прототипов функций существенно упрощает  отслеживание  программных
 ошибок.

      Прототипы функций также упрощают документирование кодов прог-
 раммы.  Например, функция strcpy принимает два параметра: исходную
 строку и строку назначения. Вопрос, где  какая  из  них?  Прототип
 функции

    char *strcpy(char *dest, char *source);

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

      Декларатор функции в круглых скобках, содержащий единственное
 слово  void,  указывает на функцию, вообще не принимающую аргумен-
 тов:

    func(void);

      В С++ func() также означает функцию, не принимающую  аргумен-
 тов.

      stdarg.h содержит макросы, которые можно использовать в функ-
 циях, определяемых пользователем, с переменным числом параметров.

      Прототип функции обычно объявляет функцию, принимающую фикси-
 рованное  число  параметров. Для функции С, принимающей переменное
 число параметров (например, printf) прототип функции может  закан-
 чиваться многоточием (...), например:

    f(int *const, long total, ...)

      В  случае данной формы прототипа фиксированные параметры про-
 веряются во время компиляции, а  переменные  параметры  передаются
 без контроля типа.

      Ниже приводятся примеры деклараторов функций и прототипы:

    int  f(); /* В С это функция, возвращащая int, без информации о
                                параметрах.    Это    "классический
                                стиль" Кернигэна и Ритчи */

    int f(); /* В С++ это функция, не принимающая аргументов */

    int  f(void); /* Функция, возвращающая int и не принимающая па-
                                раметров */

    int p(int,long) /* Функция с типом  возврата  int,  принимающая
                                два  параметра,  первый типа int, и
                                второй типа long */

    int pascal q(void); /* функция типа pascal, возвращающая int  и
                                не принимающая параметров */

    char  far  *s(char *source, int kind); /* Функция, возвращающая
                                дальний указатель на char и  прини-
                                мающая   два  параметра:  превый  -
                                дальний указатель на char, а второй
                                int */

    int printf(char *format,...); /* Функция,  возвращающая  int  и
                                принимающая  фиксированный параметр
                                типа указатель на char и любое чис-
                                ло дополнительных параметров  неиз-
                                вестного типа */

    int  (*fp)(int);  /*  Указатель  на функцию, возвращающую int и
                                   принимающую один параметр int */
                                  Объявления

      Общий синтаксис для определений внешних функций приводится  в
 следующей таблице:

 Определения внешних функций                          Таблица 1.18
 -----------------------------------------------------------------
    файл:
        внешнее-определение
        файл   внешнее-определение

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

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

      В  целом,  определение  функции состоит из следующих разделов
 (грамматика позволяет создание и более сложных конструкций):

 1. Опциональные спецификаторы класса памяти:  extern  или  static.
    Умолчанием является extern.

 2. Тип возврата, возможно void. Умолчанием является int.

      Элементы из пунктов 1 и 2 можно взаимно комбинировать.

 3. Опциональные модификаторы: pascal, cdecl, interrupt, near, far,
    huge.  Умолчание зависит от модели памяти и установленных опций
    компилятора.

 4. Имя функции.

 5. Список объявления параметров, который может быть пустым, заклю-
    ченный в круглые скобки. В с предпочтительно обозначать отсутс-
    твие параметров записью func(void). В С  допускается  и  старый
    стиль  записи func(), но это может приводить к неоднозначностям
    и возможным ошибкам. В С++ выдается соответствующее  предупреж-
    дение.

 6. Тело функции, представляющее собой коды, выполняемые при вызове
    функции.
                       Объявления формальных параметров

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

        int func(void) {             // аргументы отсутствуют

 С++    int func(T! t1, T2 t2, T3 t3=1)  {
                                     // три простых параметра,
                                     // один из которых с аргументо
                                     // по умолчанию

 C++    int func(T1* ptr1, T2& tref)   {
                                     // аргументы указатель и ссылк

        int func(register int i) {   // запрос регистра для аргумен

        int func(char *str,...) { /* один строковый аргумент и  пе-
                 ременное число прочих аргументов, либо фиксирован-
                 ное число аргументов с переменными типами */

      В  С++ вы можете задавать, как показано, аргументы по умолча-
 нию. Параметры со значениями по умолчанию должны являться  послед-
 ними  параметрами в списке. Типы аргументов могут быть скалярными,
 структурами,объединениями,  перечислимого  типа,  указателями  или
 ссылками  на структуры или объединения, или указателями на функции
 или классы.

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

      Все  объявленные параметры автоматически получают контекст, а
 также длительность данной функции. Единственным допустимым для них
 классом спецификатора является register.

      В деклараторах формальных параметров могут использоваться мо-
 дификаторы const и volatile.
                  Вызовы функций и преобразования аргументов

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

      Ниже приводится краткое изложение правил,  управляющих  обра-
 боткой в Turbo C++ модификаторов языка и формальных параметров при
 вызове  функций, как при наличии прототипа, так и при его отсутст-
 вии:

 1. Модификаторы языка для определения функции должны  соответство-
    вать модификаторам, используемым в объявлении функции, при всех
    вызовах функции.

 2. Функция может модифицировать значения своих формальных парамет-
    ров,  но  это  не  влияет на фактические аргументы в вызывающей
    программе, за исключением аргументов типа ссылка в C++.

      Если ранее не был объявлен прототип функции, Turbo C++ преоб-
 разует интегральные аргументы при вызове функции в соответствии  с
 правилами интегрального расширения, описанными в разделе "Стандар-
 тные  преобразования" на стр.41 оригинала. При наличии в контексте
 прототипа функции Turbo C++ преобразует данные аргументы к  объяв-
 ленным типам параметров, как при операции присвоения.

      Если  прототип  функции  включает в себя многоточие (...), то
 Turbo C++ преобразует все данные аргументы функции, как и в  любом
 другом  прототипе  (использующем многоточие). Компилятор расширяет
 любые аргументы,  заданные  помимо  фиксированных  параметров,  по
 обычным правилам для аргументов функции без прототипов.

      При  наличии прототипа число аргументов в прототипе и функции
 должно совпадать (при условии, что в прототипе не задано  многото-
 чие).  Типы аргументов должны являться совместимы в такой степени,
 чтобы операция присвоения выполнялась правильно. Вы всегда  можете
 использовать  явные приведения, чтобы преобразовать аргумент к ти-
 пу, приемлемому для прототипа функции.

 Важное замечание

      Если прототип функции не соответствует фактическому определе-
 нию функции, Turbo C++ обнаружит это в том и том случае, когда оп-
 ределение содержится в той же единице компиляции, что и  прототип.
 При создании библиотеки подпрограмм с соответствующим файлом заго-
 ловка  прототипов  не  забывайте включать этот файл при компиляции
 библиотеки, с тем, чтобы любые расхождения между прототипом и фак-
 тическими определениями функции были обнаружены. С++  обеспечивает
 при  компоновке контроль типов, поэтому все различия между ожидае-
 мыми и действительно заданными параметрами будут компоновщиком об-
 наружены.


                                   Структуры

      Инициализация структуры описана на стр.42 оригинала.

      Структура - это производный тип данных, обычно представляющий
 собой определяемый пользователем  набор  именованных  компонентов.
 Эти компоненты могут быть любого типа, как фундаментального, так и
 производного  (с  некоторыми  описываемыми далее ограничениями), и
 располагаться в любой последовательности.  Кроме  того,  компонент
 структуры  может иметь тип битового поля, более нигде не разрешае-
 мого. Тип струтуры в  Turbo  C++  позволяет  обрабатывать  сложные
 структуры данных так же легко, как и простые переменные.

      В С++ тип структуры рассматривается как тип класса (с опреде-
 ленными  различиями: доступ по умолчанию устанавливается public, а
 умолчание для базового класса также public). Это позволяет органи-
 зовывать более сложное управление компонентами структуры при помо-
 щи спецификаторов доступа С++: public (это умолчание),  private  и
 protected.  Помимо данного опционального механизма управления дос-
 тупом и упомянутых исключений, далее рассматриваемые  синтаксис  и
 применение структур относятся равно к структурам С и С++.

      Объявление  структур  выполняется  при помощи ключевого слова
 struct. Например,

    struct mystruct { ... }; // mystruct - это тег структуры
    ...
    struct mystruct s, *ps, arrs[10];
    /* s имеет тип структуры mystruct; ps это указатель на тип
       struct mystruct */
               Структуры без тегов и определения типов (typedef)

      Структуры без компонентов и компоненты объединений при иници-
 ализации игнорируются.

      Если тег структуры опущен, то получается структура без  тега.
 Такую  структуру можно использовать для объявления идентификаторов
 в разделяемом запятыми списке-идентификаторов-структуры как  имею-
 щих данный тип структуры (или являющихся производными от него), но
 объявлять объекты этого типа дополнительно где-либо еще нельзя:

    struct { ...} s, *ps, arrs[10];  //структура без тега

      При  объявлении структуры, как с тегом, так и без него, можно
 создать typedef:

    typedef struct mystruct { ... } MYSTRUCT;
    MYSTRUCT s, *ps, arrs[10];       // то же, что и
                                     // struct mystruct s и т.д.
    typedef struct { ... } YRSTRUCT; // тег отсутствует YRSTRUCT y,
    *yp, arry[20];

      Обычно и тег, и typedef одновременно не нужны; в  объявлениях
 структуры может быть использован любой из них.
                       Объявления компонентов структуры

      Список-объявления-компонентов  в  фигурных  скобках объявляет
 типы и имена компонентов структуры при помощи синтаксиса  деклара-
 тора, показанного в таблице 1.11 на стр.36 оригинала.

      Компоненты структуры могут быть любого типа, за двумя
 исключениями

 1.  Тип компонента не может быть тот же, что и объявляемая в теку-
    щий момент структура:

       struct mystruct { mystruct s } s1, s2;   // недопустимо

    Компонент структуры может являться указателем на объявляемую
    структуру, как в следующем примере:

       struct mystruct { mystruct *ps } s1, s2; // так можно

    Кроме  того,  структура  может содержать ранее объявленные типы
    структур, объявляя вхождения объявленных ранее структур.

      В С++ ключевое слово struct может быть опущено.

 2. Кроме С++, компонент структуры нигде не может иметь тип  "функ-
    ция, возвращающая ...", но тип "указатель на функцию, возвраща-
    ющую
    ..." допустим. В С++ struct может иметь компоненты-функции.

Структуры и функции

      Функция  может иметь возвращаемое значение типа структуры или
 указателя структуры.

    mystruct func1(void); // func1() возвращает структуру  mystruct
    *func29void); // func2() возвращает указатель структуры

      Структура  может  быть передана функции в качестве аргумента,
 следующим образом:

    void func1 (mystruct s);       // непосредственно
    void func2 (mystruct *sptr);   // через указатель
    void func3 (mystruct &sref);   // по ссылке (только С++)
                         Доступ к компоненту структуры

      Доступ к компонентам структур и объединений выполняется  опе-
 раторами выбора . и ->. Предположим, что объект имеет тип структу-
 ры  S,  а sptr это указатель на S. Тогда, если m это идентификатор
 типа M, объявленного в S, то выражения s.m и sptr->m имеют тип M и
 представляют объект m - компонент структуры s.  Выражение  s->sptr
 является удобным синонимом (*sptr).m.

      Операция . называется прямым селектором компонента структуры;
 операция -> называется косвенным селектором компонента (или указа-
 телем) структуры; например,

    struct mystruct {
       int i;
       char str[21];
       double d;
    } s, *sptr=&s;
    ...
    s.i  =  3; // присвоению члему i структуры mystruct s sptr->d =
    1.23; // присвоение компоненту d структуры mystruct s

      Выражение s.m является именуемым значением (lvalue),  если  s
 это  не  именуемое  значение  и  s не имеет тип массива. Выражение
 sptr->m является именуемым выражением, если m не имеет тип  масси-
 ва.
      Если  структура  B содержит поле, тип которого есть структура
 A, то доступ к компонентам A выполняется  через  два  одновременно
 задаваемых селектора компонента структуры:

    struct A {
       int j;
       double x;
    }

    struct B {
       int i;
       struct A a;
       double d;
    } s, *sptr;
    ...
 s.i  =  3;  //  присвоение  компоненту i структуры B s.a.j = 2; //
 присвоение компоненту j структуры A sptr->d = 1.23; //  присвоение
 компоненту d структуры B (sptr->).x = 3.14 // присвоение компонен-
 ту x структуры A

      Каждое  объявление структуры вводит уникальный тип структуры,
 поэтому в

    struct A {
       int i,j;
       double d;
    } a, a1;

    struct B {
       int i,j;
       double d;
    } b;

 объекты a и a1 оба имеют тип struct A, но объекты a и b имеют раз-
 личные типы структуры. Структурам может  выполняться  присваивание
 только в том случае, если и исходная структура, и структура назна-
 чения имеют один и тот же тип:

    a = a1;     // так можно; тип один и тот же, поэтому может быть
                // выполнено покомпонентное присвоение структур
    a = b;      // так нельзя; разные компоненты
    a.1 = b.1; a.j = b.j; a.d = b.d;  // однако присвоение можно
                // выполнять на уровне компонентов структуры
                         Выравнивание по границе слова

      Память распределяется структуре покомпонентно, слева-направо,
 от младшего к старшему адресу памяти. В следующем примере

    struct mystruct {
       int i;
       char str[2];

       double d;
    } s;

 объект  s занимает достаточное количество памяти для размещения 2-
 байтового целочисленного значения, 21-байтовой строки и 8-байтово-
 го значения типа double. Формат данного объекта в памяти определя-
 ется опцией Turbo C++ выравнивания по границе слова. Когда эта оп-
 ция выключена (по умолчанию), s будет занимать 31 байт непрерывно.
 Если же включить выравнивание по границе слова опцией -a  компиля-
 тора (или в диалоговом поле Options | Compiler | Code Generation),
 то Turbo C++ заполняет структуры байтами таким образом, что струк-
 тура была выравнена по следующим правилам:

 1. Структура должна начинаться по границе слова (четный адрес).

 2.  Любой не-символьный элемент будет иметь четное смещение в бай-
    тах относительно начала структуры.

 3. В конец структуры при необходимости добавляется конечный  байт,
    таким  образом, чтобы вся структура в целом занимала четное ко-
    личество байтов.

      Если  опция  выравнивания  включена,  то  приведенный  пример
 структуры  имел  бы добавленный перед double байт, и весь объект в
 целом занимал бы 32 байта.

                          Пространство имен структур

      Имена тегов структур разделяют общее пространство имен с  те-
 гами  объединений и перечислимых данных (однако в С++ имена входя-
 щих в структуру перечислимых данных находятся  в  другом  адресном
 пространстве). Это означает, что в пределах одного контекста такие
 теги должны иметь уникальные имена. Однако, имена тегов не обязаны
 отличаться  от идентификаторов, находящихся в трех других адресных
 пространствах: пространстве имен меток, пространстве  (пространст-
 вах) имен компонентов и едином адресном пространстве (которое сос-
 тоит из имен переменных, функций, имен typedef и нкмераторов).

      Имена компонентов в пределах данной структуры или объединения
 лбязаны быть уникальными, но среди разных структур или объединений
 они могут совпадать. Например,

    goto s;
    ...
    struct s { // так можно; теги и имена меток находятся в разных
               // адресных пространствах
       int s;  // так можно; теги, имена меток и имена компонентов
               // дятся в разных адресных пространствах
       float s;// так нельзя: повторение имени компонентов структур
    } s;       // так можно; пространства имен переменных различны
               // В С++ это допустимо только если s не имеет
               // конструктора.

    union  s  { // так нельзя: повторение имен в пространстве тегов
       int s; // так можно: новое пространство компонентов float f;
    } f;       // так можно: пространство имен переменных

    struct t {
       int s;  // так можно: следующее пространство имен компоненто
       ...
    } s;       // так нельзя: повторение имен переменных

Неполные объявления

      Указатель структуры  типа  А  допустим  в  объявлении  другой
 структуры В до объявления структуры А:

    struct  A;  //  неполное  объявление struct B { struct A *pa };
    struct A { struct B *pb };

      Первое объявление А называется  неполным,  поскольку  в  этой
 точке отсутствует определение А. В данной ситуации неполное объяв-
 ление допустимо, поскольку в объявлении В размер А необязателен.
                                 Битовые поля

      Структура  может  содержать  любые комбинации битовых полей с
 данными других типов.

      Целочисленные компоненты типа signed или unsigned можно объя-
 вить битовыми полями шириной от 1 до 16 бит. Ширина битового  поля
 и его опциональный идентификатор задаются следующим образом:

      спецификатор-типа <идентификатор-битового поля>:ширина;

 где  спецификатор-типа  это  char, unsigned char, int или unsigned
 int. Битовые поля располагаются с нижнего и  кончая  саршим  битом
 слова. Выражение "ширина" должно быть задано и должно давать цело-
 численную константу со значением в диапазоне от 0 до 16.

      Если  идентификатор битового поля опущен, то число битов, за-
 данное выражением "ширина", распределяется в памяти, но  поле  при
 этом остается недоступным программе. Это позволяет создавать бито-
 вые  шаблоны для, например, аппаратных регистров компьютера, в ко-
 торых некоторые биты не используются. Пример:

    struct mystruct {
       int       i:2;
       unsigned  j:5;
       int        :4;
       int       k:1;
       unsigned  m:4;
    } a, b, c;

 создает следующее распределение памяти.

 -----------------------------------------------------------------
 | 15| 14| 13| 12| 11| 10| 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
 -----------------------------------------------------------------
 | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x |
 -----------------------------------------------------------------
 |<------------->|<->|<------------->|<----------------->|<----->|
 -----------------------------------------------------------------
 |       m       | k |не используется|         j         |   i   |
 -----------------------------------------------------------------

      Целочисленные поля хранятся в виде дополнения до двух, причем
 крайний левый бит побещается в MSB (наиболее  значащий  бит).  Для
 битового поля типа Int (например, signed) MSB интерпретируется как
 знаковый  бит. Битовое поле шириной 2, содержащее двоичное 11, бу-
 дет, следовательно, в случае типа unsigned интерпретироваться  как
 3,  а в случае Int как -1. В предыдущем примере допустимое выраже-
 ние a.i = 6 поместит в a.i двоичное 10 = -2, не выдавая каких-либо
 предупреждений. Поле k типа signed int шириной 1  может  содержать
 только значения -1 и 0, так как битовый шаблон 1 будет интерпрети-
 рован как -1.

 Примечание

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

      Выражение  &mystruct.x недопустимо, так как x это идентифика-
 тор битового поля, а никакой гарантии, что mystruct.x имеет  адрес
 на границе байта, нет.
                                  Объединения

      Объединения  соответствуют  типам  вариантных  записей языков
 Pascal и Modula-2.

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

    union myunion {   /* тег объединения = myunion  */
       int i;
       double d;
       char ch;
    } mu, *muptr=μ

 идентификатор mu типа union myunion может служить для хранения
 2-байтового значения int, 8-байтового значения double или 1-байто-
 вого char, но одновременно - только одного из этих значений.

      Обе  операции  sizeof(union myunion) и sizeof (mu) возвращают
 значение 8, но когда mu содержит объект типа int, то 6 байт  оста-
 ются  неиспользованными  (туда  помещаются символы-заполнители), а
 когда mu сщдержит объект типа char - то 7 байт. Доступ к компонен-
 там  объединения  выполняется  при  помощи  селекторов  компонента
 структуры (. и ->), но требуется соблюдать осторожность:

    mu.d = 4.016;
    printf("mu.d  = %f\n",mu.d);// порядок: на дисплее mu.d = 4.016
    printf("mu.i = %f\n",mu.i);// забавный результат ! mu.ср = 'A';
    printf("mu.ch = %c\n",mu.ch); // порядок: на дисплее mu.ch =  A
    printf("mu.d  = %f\n",mu.d); // забавный результат ! muptr->i =
    3; printf("mu.i = %d\n",mu.i); // порядок: на дисплее mu.i = 3

      Второй оператор printf допустим, поскольку mu.i целочисленно-
 го типа. Однако, битовая комбинация в mu.i соответствует части ра-
 нее присвоенного значения типа double и не даст как правило полез-
 ной целочисленной интерпретации.

      При правильных преобразованиях  указатель  объединения  может
 указывать на любые его компоненты, и наоборот.
                            Объявления объединений

      Общий  синтаксис  объявления объединений во многом напоминает
 синтаксис объявления структур. Различия состоят в следующем:

 1. Объединения могут содержать битовые поля,  но  активным  бывает
    только одно изних. Все они начинаются в начале объединения.

 2.  С++  : В отличие от структур С++, объединения С++ не могут ис-
    пользовать спецификаторы  класса  доступа:  public,  private  и
    protected. Все поля объединения имеют доступ private.

 3. Объединения инициализируются через компонент, объявленный первы

       union local87  {
          int i;
          double d;
       } a = { 20};

 4.  С++  : Объединение не может участвовать в иерархии класса. Оно
    не может являться производным от какого-либо  класса  или  быть
    базовым классом. Объединение может иметь конструктор.

 5. С++ : Анонимные объединения не могут иметь компоненты-функции.
                              Перечислимые данные

      Тип  перечислимых данных служит для обеспечения мнемонических
 идентификаторов набора целочисленных значений. Например, следующее
 объявление:

    enum days { sun, mon, tues, wed, thur, fri, sat }  anyday;

 устанавливает уникальный интегральный тип, enum  days,  переменную
 anyday этого типа и набор нумераторов (sun,mon,...), которым соот-
 ветствуют целочисленные константы.

      Turbo  C++  может  хранить нумераторы в одном байте, если это
 позволяет диапазон значений нумераторов, когда выключена опция  -b
 (по  умолчанию  она  включена;  это означает, что данные типа enum
 всегда int), но при использовании их в выражениях выполняется этих
 данных преобразования к типу int. Идентификаторы,  используемые  в
 списке  нумераторов,  неявно получают тип unsigned char или int, в
 зависимости от значений нумераторов. Если все значения могут  быть
 представлены типом unsigned char, то это и будет типом каждого ну-
 мератора.

 C++  В  С переменной перечислимого типа может быть присвоено любое
 значение типа int - кроме этого, никакого контроля типа не  выпол-
 няется.  В  С++  переменной перечислимого типа может присваиваться
 только значение одного из ее нумераторов. Таким образом,

    anyday = mon;    // так можно
    anyday = 1;      // так нельзя, даже хотя mon == 1

      Идентификатор days является опциональным тегом  перечислимого
 типа,  который  можно использовать в последующих объявлениях пере-
 менных перечислимого типа enum days:

    enum days payday, holiday; // объявление двух переменных

 С++ В С++ ключевое слово enum можно опустить, если в пределах дан-
 ного контекста имя days не дублируется.

      Как и в случае объявлений struct и union, если далее перемен-
 ные данного типа enum не требуются, тег может быть опущен:

    enum { sun, mon, tues, wed, thur, fri, sat } anyday;
    /* анонимный тип enum */

      Подробное описание констант перечислимого типа см. на стр.
 17 оригинала.

      Нумераторы, перечисленные внутри фигурных скобок,  называются
 перечислимыми константами. Каждой из них назначается фиксированное
 целочисленное значение. При отсутствии явно заданных инициализато-
 ров первый нумератор (sun) устанавливается в ноль, а каждый после-
 дующий  нумератор имеет значение на единицу больше, чем предыдущий
 (mon = 1, tue = 2 и т.д.).

      При наличии явных интегральных инициализаторов вы можете  ус-
 тановить  один  или более нумераторов в конкретные значения. Любые
 последующие имена без инициализаторов будут получать приращение  в
 единицу. Например, в следующем объявлении

    /* выражение инициализатора может включать в себя нумераторы,
       объявленные ранее */
    enum coins { penny = 1, tuppence, nickel = penny + 4, dime =10,
                 quarter = nickel * nickel } smallchange;

 tuppence  примет значение 2, nickel - значение 5, а quarter - зна-
 чение 25.

      Инициализатор может быть любым выражением, дающим положитель-
 ное или отрицательное целочисленное значение (после, возможно, не-
 которых целочисленных преобразований.) Обычно такие значения быва-
 ют уникальными, но дублирование их также не запрещено.

      Тип enum может участвовать во всех конструкциях,  допускающих
 использование типов int.

    enum  days { sun, mon, tues, wed, thur, fri, sat } anyday;
    enum days payday;
    typedef enum days DAYS;
    DAYS  *daysptr;
    int  i  = tues;
    anyday = mon; // так можно
    *daysptr = anyday;   // так можно
    mon = tues;          // неверно: mon - это константа

      Теги  перечислимых типов разделяют пространство имен с тегами
 структур и объединений. Нумераторы разделяют пространство  имен  с
 обычными идентификаторами переменных:

    int mon = 11;
    {
       enum days { sun, mon, tues, wed, thur, fri, sat } anyday;
       /* нумератор mon скрывает внешнее объявление int mon */
       struct  days  { int i, j;); // неверно: дублируется тег days
       double sat; // неверно: переопределение sat
    }
    mon = 12;                     // снова в контексте int mon

 C++ В С++ нумераторы, объявленные в пределах  класса,  имеют  кон-
 текст этого класса.
                                   Выражения

      В  таблице 1.19 показано, каким образом комбинируются иденти-
 фикаторы и операции для составления грамматически верных "фраз".

      Выражением называется последовательность операций,  операндов
 и  пунктуаторов, задающих определенное вычисление. Формальный син-
 таксис, показанный в таблице 1.19, обозначает, что выражения опре-
 деляются рекурсивно: под-выражения могут быть вложены без формаль-
 ных ограничений. (Однако, если компилятор не сможет обработать ка-
 кое-либо слишком сложное выражение, то будет выдано  сообщение  об
 ошибке).

 Выражения Turbo C++                                  Таблица 1.19
 -----------------------------------------------------------------
 первичное-выражение:
    литерал
    псевдо-переменная
    (выражение)
    this (только С++)
    :: идентификатор (только С++)
    :: имя-функции-операции (только С++)
    имя

 литерал:
    целочисленная-константа
    символьная-константа
    константа-с-плавающей-точкой
    строка

 имя:
    идентификатор:
    имя-функции-операции (только С++)
    имя-функции-преобразования (только С++)
    квалифицированное-имя (только С++)

 квалифицированное-имя: (только С++)
    имя-класса :: идентификатор
    имя-класса :: имя-функции-операции
    имя-класса :: имя-функции-преобразования
    имя-класса :: имя-класса
    имя-класса :: - имя-класса

 постфиксное-выражение:
    первичное-выражение
    постфиксное-выражение[выражение]
    постфиксное-выражение (<список-выражений>)
    постфиксное-выражение  (<список-выражений>)  (только С++) пост-
    фиксное-выражение . имя постфиксное-выражение -> имя  постфикс-
    ное-выражение ++ постфиксное-выражение --

 список-выражений:
    выражение-присваивания
    список-выражений , выражение-присваивания

 унарное-выражение:
    постфиксное-выражение
    ++ унарное-выражение
    -- унарное-выражение
    унарная-операция  выражение-приведения
    sizeof  унарное-выражение
    sizeof  (имя-типа)
    выражение-распределения (только С++)
    выражение-отмены-распределения (только С++)

 унарная-операция: одно из
    & * + - тильда !

 выражение-распределения: (только С++)
    <::> new <местоположение> имя-ограниченного-типа <инициализатор
    <::> new <местоположение> имя-типа <инициализатор>

 местоположение: (только С++)
    (список-выражений)

 имя-ограниченного-типа: (только С++)
    спецификатор-типа <декларатор-ограничения>

 декларатор-ограничения: (только С++)
    операция-указателя <декларатор ограничения>
    декларатор-ограничения [<выражение>]

 выражение-отмены-распределения: (только С++)
    <::> delete выражение-приведения
    <::> delete [выражение] выражение-приведения

 выражение-приведения:
    унарное-выражение
    (имя-типа) выражение-приведения

 выражение-типа-ссылки:
    выражение-приведения
    выражение-типа-ссылки  .* выражение-приведения (только С++) вы-
    ражение-типа-ссылки -> выражение-приведения (только С++)

 выражение-типа-умножения:
    выражение-типа-ссылки
    выражение-типа-умножения * выражение-типа-ссылки
    выражение-типа-умножения / выражение-типа-ссылки
    выражение-типа-умножения % выражение-типа-ссылки

 выражение-типа-сложения:
    выражение-типа-умножения
    выражение-типа-сложения + выражение-типа-умножения
    выражение-типа-сложения - выражение-типа-умножения

 выражение-типа-сдвига:
    выражение-типа-сложения
    выражение-типа-сдвига <<  выражение-типа-сложения
    выражение-типа-сдвига >>  выражение-типа-сложения

 выражение-отношения:
    выражение-типа-сдвига
    выражение-отношения < выражение-типа-сдвига
    выражение-отношения > выражение-типа-сдвига
    выражение-отношения <= выражение-типа-сдвига
    выражение-отношения >= выражение-типа-сдвига

 выражение-типа-равенства:
    выражение-отношения
    выражение-типа-равенства = выражение-отношения
    выражение-типа-равенства != выражение-отношения

 выражение-И:
    выражение-типа-равенства
    выражение-И & выражение-типа-равенства

 выражение-исключающее-ИЛИ:
    выражение-И
    выражение-исключающее-ИЛИ выражение-логическое-И

 выражение-включающее-ИЛИ:
    выражение-исключающее-ИЛИ
    выражение-включающее-ИЛИ  |  выражение-исключающее-ИЛИ

 выражение-логическое-И:
    выражение-включающее-ИЛИ
    выражение-логическое-И && выражение-включающее-ИЛИ

 выражение-логическое-ИЛИ:
    выражение-логическое-И
    выражение-логическое-ИЛИ !! выражение-логическое-И

 условное-выражение:
    выражение-логическое-ИЛИ
    выражение-логическое-ИЛИ ? выражение : условное-выражение

 выражение-присвоения:
    условное-выражение
    унарное-выражение операция-присвоения выражение-присвоения

 операция-присвоения: одно из
    =     *=     /=     %=     +=     -=
    <<=   ??=    &=     ^=     |=

 выражение:
    выражение-присвоения
    выражение, выражение-присвоения

 выражение-типа-константы:
    условное-выражение
 -----------------------------------------------------------------

      Стандартные преобразования подробно рассматриваются на стр.42
 оригинала, в таблице 1.15.

      Вычисление выражений  выполняется  по  определенным  правилам
 преобразования, группировки, ассоциативности и приоритета, которые
 зависят  от  используемых  в  выражениях операций, наличию круглых
 скобок и типов данных операндов. Способ  группировки  операндов  и
 подвыражений  не обязательно определяет фактический порядок вычис-
 ления выражений в Turbo C++ (см.  "Последовательность  вычислений"
 на стр. 76 оригинала.)

      Выражения  могут  давать  в  результате  именующие  выражения
 (lvalue), значения переменных (rvalue), либо не давать никаких вы-
 ражений вообще. Не зависимо от того, является ли результатом выра-
 жения некоторое значение, выражение может иметь побочный эффект.

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

 Ассоциативность и приоритеты операций Turbo C++      Tаблица 1.20
 -----------------------------------------------------------------
    Операции Ассоциативность
 -----------------------------------------------------------------
    () [] -> :: . Слева-направо
    ! тильда - ++ -- & *                         Справа-налево
    sizeof new delete .* ->* / %            Слева-направо
    + -                                     Слева-направо
    << >>                                   Слева-направо
    < <= > >=                               Слева-направо
    &                                       Слева-направо
    ^                                       Слева-направо
    |                                       Слева-направо
    &&                                      Слева-направо
    ||                                      Слева-направо
    ?:условное выражение                    Справа-налево
    = += /= %= += -=                        Справа-налево
    &= ^= |= ,                              Слева-направо
 -----------------------------------------------------------------

      Приоритеты  обозначаются  последовательностью  расположения в
 данной таблице. Первый элемент таблицы имеет наивысший приоритет.

Выражения и Turbo C++

      С++ позволяет перегрузку некоторых  стандартных  операций  С,
 как описано начиная со стр.125 оригинала. Перегруженной называется
 такая операция, которая применительно к выражениям типа класса ве-
 дет себя некоторым специальным образом. Например, оператор отноше-
 ния == может быть определен в классе complex для проверки равенст-
 ва  двух  комплексных  чисел, причем действие его для типов данных
 других классов остается прежним. Перегруженный оператор реализует-
 ся как функция; эта функция определяет тип операнда, именующее вы-
 ражение (lvalue) и последовательность вычислений,  устанавливаемая
 при  использовании перегруженного оператора. Однако, перегрузка не
 может изменять приоритеты операций. Аналогичным образом, С++  поз-
 воляет  выполнять  определяемые пользователем преобразования между
 объектами класса и фундаментальными типами. Учтите, что  некоторые
 правила относительно операций и преобразований, обсуждаемые в дан-
 ном разделе, неприменимы к выражениям в С++.
                         Последовательность вычислений

      Последовательность  вычисления  операндов  в выражениях Turbo
 C++ не определена, если иное явно не задано операцией.  Компилятор
 пытается  реорганизовать  выражение  таким образом, чтобы улучшить
 качество генерируемого кода.  Следовательно,  необходима  осторож-
 ность  при работе с выражениями, в которых значение модифицируется
 более одного раза. В целом,следует  избегать  создания  выражений,
 которые  одновременно и модифицируют, и используют значение одного
 и того же объекта. Рассмотрим выражение

    i = v[i++];  // i неопределено

      Значение i зависит от того, выполняется ли  инкрементирование
 до или после присвоения. Аналогичным образом,

    int total = 0;
    sum = [total = 3] + (++total);      // sum = 4 или sum = 7 ??

 имеет неоднозначность идентификаторов sum и total. Решение состоит
 в том, чтобы упростить выражение при помощи временной переменной:

    int temp, temp = 0;
    temp = ++total;
    sum = (total = 3) + temp;

      Когда  синтаксис  принудительно  устанавливает последователь-
 ность вычисления операндов, то множественные  вычисления  в  одной
 конструкции не содержат опасности неоднозначности:

    sum = (i = 3, i++, i++);  // так можно: sum = 4, i = 5

      Каждое  под-выражение  или  выражение  с запятыми вычисляется
 слева-направо, и все выражение в целом вычисляется по  направлению
 к самому правому значению.

      Turbo  C++ перегруппирует выражения, реорганизовывая ассоциа-
 тивные и коммутативные операции независимо от наличия круглых ско-
 бок, с тем, чтобы получить эффективно компилируемое выражение; ре-
 организация выражения ни в коем случае не влияет на результат  вы-
 числения выражения.

      Круглые  скобки  можно  использовать для того, чтобы принуди-
 тельно задать порядок вычислений в выражении. Например, если  име-
 ются  переменные a, b, c и f, то выражение f=a+(b+c) вызывает сна-
 чала вычисление (b+c), а затем уже сложение результата с a.

Ошибки и переполнения

      Во время вычисления выражения Turbo C++ может встретить  мно-
 гие  проблематичные ситуации, как то деление на ноль или получение
 значений с плавающей точкой, выходящих за пределы допустимого диа-
 пазона. Переполнение целочисленных значений  игнорируется  (С  ис-
 пользу-
                                       n
 ет  арифметические  действия по модулю 2 в n-разрядных регистрах),
 однако ошибки, обнаруживаемые математическими библиотечными  функ-
 циями, могут обрабатываться стандартными или определяемыми пользо-
 вателем подпрограммами. См. matherr и signal в Справочнике по Биб-
 лиотеке.

Семантика операций

      Описанные  здесь операции Turbo C++ являются операциями стан-
 дарта ANSI C.

      Если операции не перегружены, то следующая информация  дейст-
 вительна как для С, так и для С++. В С++ вы можете перегрузить все
 эти  операции,  за исключением операции . (операция задания компо-
 нента) и ?: (условная операция) (также не могут быть перегружены
 операции С++ :: и .*).

      Если операция перегружена, то приводимые здесь  сведения  для
 нее  недействительны.  Таблица  1.19  на стр.74 оригинала приводит
 синтаксис для всех операций и выражений с операциями.
                       Постфиксные и префиксные операции

      Шесть постфиксных операций [] () . -> ++  и  --  используются
 для построения постфиксных выражений, показанных в таблице синтак-
 сиса  выражений  (таблица  1.19). Операции инкремента и декремента
 (++ и --) также являются префиксными и  унарными  операциями;  они
 обсуждаются, начиная со стр.79 оригинала.

 Операция индексации массива []   ---------------------------------

      В выражении

    постфиксное-выражение [выражение]

 в С, но не обязательно в С++, выражение выраж1[выраж2] определяет-
 ся как

    *((выраж1) + (выраж2))

 где  либо  выраж1 это указатель, а выраж2 это целочисленное значе-
 ние, либо выраж1 это это целочисленное значение, а выраж1 это ука-
 затель. (Каждый из пунктуаторов [], * и + может быть перегружен  в
 С++).

 Операция вызова функции ()

      Выражение

    постфиксное-выражение(<список-аргументов-выражения>)

 представляет собой вызов функции, задаваемой постфиксным выражени-
 ем.  Список-аргументов-выражения  - это разделяемый запятой список
 выражения любого типа, задающий фактические  (или  действительные)
 аргументы  функции. Значение самого выражения вызова функции, если
 оно вообще имеет место, определяется оператором возврата в опреде-
 лении функции. См. "Вызовы функций и преобразования аргументов" на
 стр.63 оригинала, где приводится более поробное  изложение  вызова
 функций.

 Операция задания компонента структуры/объединения . (точка)

           В выражении

    постфиксное-выражение . идетификатор

 постфиксное-выражение  должно иметь тип структуры или объединения;
 идентификатор должен являться именем компонента  данной  структуры
 или объединения. Выражение обозначает объект - компонент структуры
 или объединения. Значением данного выражения будет являться значе-
 ние выбранного таким образом компонента; оно будет являться имену-
 ющим выражением (lvalue) в том и только том случае, если именующим
 выражением является само постфиксное выражение. Подробное описание
 использования операций . и -> дается на стр.66 оригинала.

      Именующие выражения определяются на стр.28 оригинала.

 Операция указателя структуры/объединения ->

      В выражении

    постфиксное-выражение -> идентификатор

 должно  иметь  тип  указателя структуры или указателя объединения;
 идентификатор же должен быть именем компонента этой структуры  или
 объединения. Выражение обозначает объект - компонент структуры или
 объединения.  Значением  данного выражения будет являться значение
 выбранного таким образом компонента; оно будет являться  именующим
 выражением  (lvalue) в том и только том случае, если именующим вы-
 ражением является само постфиксное выражение.

 Операция постфиксного инкремента ++

      В выражении

    постфиксное-выражение++
 операндом является постфиксное выражение:
 оно должно быть скалярного типа (арифметического или типа указате-
 ля) и должно являться модифицируемым именующим  выражением  (более
 подробная  информация об именующих выражениях приводится на стр.28
 оригинала.) Постфикс ++ также называют  операцией  постинкремента.
 Значением всего выражения является значение постфиксного выражения
 до  выполнения инкремента. После вычисления постфиксного выражения
 операнд инкрементируется на 1.

      Величина инкремента зависит от типа операнда.  Значения  типа
 указателя вычисляются по правилам арифметических действий с указа-
 телями.

 Постфиксная операция декремента --

      Постфиксная операция декремента, также известная как постдек-
 ремент,  подчиняется  тем же правилам, что и операция постфиксного
 инкремента, за исключением того, что единица после вычисления  вы-
 ражения вычитается.
                       Операции инкремента и декремента

      Первыми  двумя унарными операциями являются операции ++ и --.
 Эти операции бывают как постфиксными, так и префиксными, и поэтому
 обсуждаются в данногм  разделе.  Затем  рассматриваются  остальные
 шесть префиксных операций.

 Операция префиксного инкремента ++

      В выражении

    ++ унарное-выражение

 операндом является унарное выражение:
 оно должно быть скалярного типа (арифметического или типа указате-
 ля)  и должно являться модифицируемым именующим выражением. Опера-
 цию префиксного инкремента также называют операцией преинкремента.
 Операнд инкрементируется на 1 до вычисления  выражения;  значением
 всего выражения является инкрементированное значение операнда. Ве-
 личина инкремента зависит от типа операнда. Значения типа указате-
 ля вычисляются по правилам арифметических действий с указателями.

 Префиксная операция декремента --

      Префиксная операция декремента, также известная как предекре-
 мент, имеет следующий синтаксис:

    --унарное-выражение

 и  подчиняется  тем же правилам, что и операция префиксного инкре-
 мента, за исключением того, что единица перед вычислением  выраже-
 ния вычитается.
                               Унарные операции

      Существует шесть унарных операций (кроме ++ и --):
 .  *  +  -  тильда и  !.  Их синтаксис:

    унарная-операция  выражение-приведения

    выражение-приведения:
       унарное-выражение
       (имя-типа) выражение-приведения

 Операция адресации &

      Символ & также используется в С++ для задания ссылочных типов
 данных. См. стр.98 оригинала.

      Операции  &  и * (операция * описана в следующем разделе) ис-
 пользуются совместно в качестве операций установки ссылки и  обра-
 жения по ссылке (разыменования). В выражении

    & выражение-приведения

 операнд  выражение-приведения  должен  являться  либо обозначением
 функции, либо именующим выражением, обозначающим объект, не являю-
 щийся битовым  полем  и  не  имеющий  спецификатор  класса  памяти
 register.  Если  операнд  имеет некоторый тип type, то результатом
 операции будет указатель на type.

      Отметим, что некоторые  не  являющиеся  именующим  выражением
 идентификаторы,  такие как имена функций и имена массивов, автома-
 тически преобразовываются в определенном контексте к типу  "указа-
 тель  на  X". Операцию & использовать с такими объектами можно, но
 такая операция будет являться избыточной и будет отменена компиля-
 тором.

      Рассмотрим следующий фрагмент:

    type t1 =1, t2 = 2;
      type *ptr = &t1;   // инициализированный указатель
      *ptr = t2;         // тот же эффект, что и t1 = t2

      Отметим, что   type *ptr = &t1  обрабатывается как

    T *ptr;
    ptr = &t1;

 так что присваивается не *ptr, а ptr. После инициализации ptr  ад-
 ресом  &t1  его можно использовать для обращения по ссылке и полу-
 чить именующее выражение *ptr.

 Операция обращения по ссылке *

      В выражении

    * выражение-приведения

 операнд выражение-приведения должен иметь тип "указатель на type",
 где type это любой тип. Результатом обращения по ссылке имеет  тип
 type.  Если  операнд имеет тип "указатель функции", то результатом
 будет являться обозначение функции; если операндом является указа-
 тель на объект, то результатом будет именующее выражение,  обозна-
 чающее данный объект. В следующих ситуациях результат обращения по
 ссылке неопределен:

 1. Выражение-приведения это пустой (null) указатель.
 2.  Выражение-приведения это адрес динамической локальной перемен-
    ной, а выполнение объемлющего блока уже завершено.

 Унарная операция плюс +

      В выражении

    +выражение-приведения

 операнд выражение-приведения должен быть арифметического типа. Ре-
 зультатом является значение операнда после любых требуемых  интег-
 ральных действий.

 Унврная операция минус -

      В выражении

    -выражение-приведения

 операнд выражение-приведения должен быть арифметического типа. Ре-
 зультатом  является  отрицательное  значение  операнда после любых
 требуемых интегральных действий.

 Операция поразрядного дополнения (тильда)

      В выражении

    <тильда>выражение-приведения

 операнд выражение-приведения должен быть интегрального  типа.  Ре-
 зультатом  является  поразрядное  дополнение  операнда после любых
 требуемых интегральных действий. Каждый бит со значением  0  уста-
 навливается в 1, а каждый единичный бит операнда устанавливается в
 0.

 Операция логического отрицания !

      В выражении

    !выражение-приведения

 операнд  выражение-приведения должен быть скалярного типа. Резуль-
 тат иммет тип int и представляет собой логическое  отрицание  опе-
 ранда:  0  при  ненулевом операнде и 1 в случае нулевого операнда.
 Выражение !E эквивалентно выражению (0 == Е).
                                Операция sizeof

      Существует  два  раздельных  способа  использования  операции
 sizeof:

    sizeof унарное-выражение
    sizeof (имя-типа)

      Размер выделяемой для каждого типа памяти зависит от конкрет-
 ной машины.

      В  обоих  сучаях  результат  представляет собой целочисленную
 константу, выражающую размер в байтах области  памяти,  занимаемой
 операндом  (определяемый  за некоторыми исключениями типом операн-
 да). В первом случае тип выражения операнда определяется без  рас-
 чета выражения (и следовательно, без побочных эффектов). Если опе-
 ранд имеет тип char (signed или unsigned), то операция sizeof дает
 в  результате  1.  Если операнд не является параметром и имеет тип
 масива, то результат представляет собой общее количество байтов  в
 массиве  (другими словами, имя массива не преобразовавается к типу
 указателя). Число элементов  массива  равно  sizeof  массив/sizeof
 массив[0].
      Если  операнд является параметром, объявленным как массив или
 функция, sizeof дает размер указателя. Применительно к  структурам
 и  объединениям  sizeof  дает общее число байтов, включающее любые
 символы-заполнители.

      Целочисленный  тип  результата  операции  sizeof   называется
 size_t, определенный как unsigned int в stddef.h.

      Можно  использовать  sizeof  в  директивах препроцессора; это
 особенность Turbo C++.

 C++: В С++ sizeof(тип класса), где тип класса является производным
 от какого-либо базового класса, возвращает размер базового класса.
                            Операции типа умножения

      Существует три операции типа умножения: * /  и  %.  Синтаксис
 этих операций следующий:

    выражение-типа-умножения:
       выражение-приведения
       выражение-типа-умножения * выражение-приведения
       выражение-типа-умножения / выражение-приведения
       выражение-типа-умножения % выражение-приведения

       Операнды  операций  *  (умножения) и / (деления) должны быть
 арифметического типа. Операнды операции % (деление по модулю,  или
 остаток)  должны быть интегрального типа. С операндами выполняются
 обычные арифметические преобразования (см. стр.41 оригинала).
      Результатом выполнения операции (операнд1 * операнд2) являет-
 ся произведение двух операндов. Результатами операций (операнд1  /
 операнд2)  и  (операнд1  % операнд2) являются частное и остаток от
 деления, соответственно, где операнд1 делится на операнд2, при ус-
 ловии, что операнд2 не равен нулю. Использование операций / и %  с
 нулевым делителем дает ошибку.

      Если  операнд1  и операнд2 имеют целочисленный тип, а частное
 не является целым, то результаты операции следующие:

 1. Если операнд1 и операнд2 имеют одинаковый  знак,  то  операнд1/
    операнд2 есть наибольшее целое, меньшее чем истинное частное, а
    операнд1 % операнд2 имеет тот же знак, что и операнд1.

 2.  Если операнд1 и операнд2 имеют разные знаки, то операнд1/ опе-
    ранд2 есть наименьшее целое, большее чем  истинное  частное,  а
    операнд1 % операнд2 имеет тот же знак, что и операнд1.

      Округление всегда выполняется к нулю.
                            Операции типа сложения

      Существует  две операции типа сложения: + и -. Синтаксис этих
 операций следующий:

    выражение-типа-сложения:
       выражение-типа-умножения
       выражение-типа-сложения + выражение-типа-умножения
       выражение-типа-сложения - выражение-типа-умножения

 Операция сложения +

      Допустимыми являются следующие типы операндов выражения  опе-
 ранд1 + операнд2:

 1. Операнд1 и операнд2 оба арифметического типа.

 2.  Операнд1 интегрального типа, а операнд2 является указателем на
    объект.

 3. Операнд2 интегрального типа, а операнд1 является указателем  на
    объект.

      В первом случае выполняются стандартные арифметические преоб-
 разования операндов, а результатом является их арифметическая сум-
 ма.  В случаях 2 и 3 применяются правила арифметических действий с
 указателями. (Арифметические действия с указателями рассматривают-
 ся на стр.57 оригинала).

 Операция вычитания -

      Допустимыми являются следующие типы операндов выражения  опе-
 ранд1 - операнд2:

 1. Операнд1 и операнд2 оба арифметического типа.

 2. Оба операнда являются указателями на совместимые типы объектов.
 (Примечание: неквалифицированный тип type рассматривается как сов-
 местимый  с  квалифицированными типами const type, volatile type и
 const volatile type.)

 3. Операнд2 интегрального типа, а операнд1 является указателем  на
    объект.

      В первом случае выполняются стандартные арифметические преоб-
 разования операндов, а результатом является их арифметическая раз-
 ность. В случаях 2 и 3 применяются правила арифметических действий
 с указателями.
                         Операции поразрядного сдвига

      Существует две операции поразрядного сдвига: << и >>. Синтак-
 сис этих операций следующий:

    выражение-типа-сдвига:
       выражение-типа-сложения
       выражение-типа-сдвига << выражение типа сдвига
       выражение-типа-сдвига >> выражение типа сдвига




 Операция поразрядного сдвига влево <<

      В выражении E1 << E2 операнды Е1 и Е2 должны иметь тип int. С
 Е1  и Е2 выполняются обычные целочисленные действия, а тип резуль-
 тата определяется операндом Е1. Если Е2 отрицателен, либо по числу
 разрядов больше или равен Е1, то операция неопределена.

      Результатом операции E1 << E2 является значение E1, сдвинутое
 влево на Е2 разрядов и при необходимости заполненное справа  нуля-
 ми.  Сдвиг влево unsigned long E1 эквивалентно умножению Е1 на 2 в
 степени Е2 и редуцированию  по  модулю  ULONG_MAX+1;  сдвиг  влево
 unsigned  int эквивалентно умножению на 2 в степени Е2 и редуциро-
 ванию по модулю UINT_MAX+1. Если Е1 это signed int,  то  результат
 следует  интерпретировать  с осторожностью, поскольку знаковый бит
 изменился.

      Константы ULONG_MAX и UINT_MAX определяются в .h-файле.

 Операция поразрядного сдвига вправо  >>

      В выражении E1 >> E2 операнды Е1 и Е2 должны иметь тип int. С
 Е1 и Е2 выполняются обычные целочисленные действия, а тип  резуль-
 тата определяется операндом Е1. Если Е2 отрицателен, либо по числу
 разрядов больше или равен Е1, то операция неопределена.

      Результатом операции E1 >> E2 является значение E1, сдвинутое
 вправо  на Е2 разрядов. Если Е1 имеет тип unsigned, то при необхо-
 димости происходит его заполнение нулями слева. Если же  Е1  имеет
 тип  signed, то заполнение слева выполняется знаком (0 для положи-
 тельных и 1 для отрицательных значений). Такое расширение знаково-
 го бита гарантирует, что знак у Е1 >> E2 будет таким же, как  и  у
 E1. За исключением типов со знаком, значение E1 >> E2 представляет
 собой целую часть частного.
                              Операции отношения

      Существует  четыре операции отношения: < > <= и >=. Синтаксис
 этих операций следующий:

    выражение-отношения:
       выражение-типа-сдвига
       выражение-отношения  <  выражение-типа-сдвига
       выражение-отношения  >  выражение-типа-сдвига
       выражение-отношения  <=  выражение-типа-сдвига
       выражение-отношения  >=  выражение-типа-сдвига

 Операция меньше чем <

      В выражении E1 < E2 операнды должны удовлетворять  одному  из
 следующего набора условий:

 1. Оба значения Е1 и Е2 - арифметического типа.

 2. Оба значения Е1 и Е2 являются указателями квалифицированных или
    неквалифицированных версий совместимых типов объектов.

      Определение квалифицированных имен дается на стр.108 оригина-
 ла.

 3. Оба значения Е1 и Е2 являются указателями квалифицированных или
    неквалифицированных версий совместимых неполных типов.

      В случае 1 выполняются обычные арифметические преобразования.
 Результат  E1 < E2 имеет тип int. Если значение E1 меньше значения
 E2, то результат равен 1 (истина); в  противном  случае  результат
 равен 0 (ложь).

      В случаях 2 и 3, где Е1 и Е2 являются указателями совместимых
 типов,  результат операции E1 

      Выражение E1 > E2 дает 1 (истина), если  значение  Е1  больше
 значения  Е2;  в противном случае результат равен 0 (ложь), причем
 используются те же способы интерпретации арифметических  сравнений
 и  сравнений указателей, что определены для операции "больше чем".
 К операндам применимы те же правила и ограничения.

 Операция меньше или равно <=

      Аналогичным образом, выражение E1 <= E2 дает 1 (истина), если
 значение Е1 меньше или равно значению Е2. В противном  случае  ре-
 зультат  равен 0 (ложь), причем используются те же способы интерп-
 ретации арифметических сравнений и сравнений указателей, что опре-
 делены для операции "меньше чем". К операндам применимы те же пра-
 вила и ограничения.

 Операция больше или равно >=

      И наконец, выражение E1 >= E2 дает 1 (истина), если  значение
 Е1  больше или равно значению Е2. В противном случае результат ра-
 вен 0 (ложь), причем  используются  те  же  способы  интерпретации
 арифметических  сравнений  и  сравнений указателей, что определены
 для операции "меньше чем". К операндам применимы те же  правила  и
 ограничения.
                            Операции типа равенства

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

    выражение-типа-равенства:
       выражение-отношения
       выражение-типа-равенства  ==  выражение-отношения
       выражение-типа-равенства  !=  выражение-отношения

 Операция проверки равенства ==

      В выражении E1 == E2 операнды должны удовлетворять одному  из
 следующего набора условий:

 1. Оба значения Е1 и Е2 - арифметического типа.

 2. Оба значения Е1 и Е2 являются указателями квалифицированных или
    неквалифицированных версий совместимых типов.

 3. Одно из значений, Е1 или Е2, является указателем объекта непол-
    ного  типа, а второй - указателем на квалифицированную или нек-
    валифицированную версию void.

 4. Одно из значений, Е1 или Е2, является указателем,  а  второе  -
    константой типа пустого указателя.

      Если  Е1 и Е2 имеют тип, являющийся допустимым типом для опе-
 раций отношения, то применимы правила, подробно описанные для опе-
 раций отношения типа Е1 < E2, E1 <= T2, и т.д.

      В случае 1, например, выполняются обычные арифметические пре-
 образования, а результат операции Е1 == Е2  имеет  тип  int.  Если
 значение  Е1  равно  значению Е2, то результат равен 1 (истина); в
 противном случае результат равен нулю (ложь).

      В случае 2 Е1 == Е2 дает 1 (истина), если Е1 и  Е2  указывают
 на  один  и  тот же объект, либо оба указывают на "следующий после
 последнеего" элемент одного и того же объекта  типа  массив,  либо
 оба являются пустыми указателями.

      Если Е1 и Е2 являются указателями на объекты типа функции, то
 Е1  ==  Е2 дает значение 1 (истина), если оба они пустые, либо оба
 указывают на одну и ту же функцию. И наоборот, если Е1 == Е2  дает
 1  (истина),  то  и Е1, и Е2 указывают на одну и ту же функцию или
 являются пустыми.

      В случае 4 указатель объекта или неполного типа преобразуется
 к типу другого операнда (указателю квалифицированной или  неквали-
 фицированной версии void).

 Оператор проверки неравенства !=

      Выражение Е1 != Е2 подчиняется тем же правилам, что и ля Е1
 == Е2, за исключением того, что результат равен 1 (истина), если
 операнды неравны, и 0 (ложь) в случае равенства операндов.

Операция поразрядного И  &

      Синтаксис данной операции следующий:

    выражение-И:
       выражение-типа-равенства
       выражение-И  &  выражение-равенства

      В  выражении  E1  & E2 оба операнда должны быть интегрального
 типа. Выполняются обычные арифметические преобразования Е1 и Е2, а
 результатом является поразрядное И для Е1 и Е2. Каждый бит резуль-
 тата определяется в соответствии с таблицей 1.21.

 Таблица истинности для поразрядных операций          Таблица 1.21
 -----------------------------------------------------------------
 Битовое значение Битовое значение Е1 & E2 E1 ^ E2 E1 | E2
      в Е1                в Е2
 -----------------------------------------------------------------
        0                   0             0         0         0
        1                   0             0         1         1
        0                   1             0         1         1
        1                   1             1         0         1
 -----------------------------------------------------------------

Операция поразрядного исключающего ИЛИ  ^

      Синтаксис этой операции следующий:

    выражение-исключающее-ИЛИ:
       выражение-И
       выражение-исключающее-ИЛИ  ^  выражение-И

      В выражении E1 ^ E2 оба операнда  должны  быть  интегрального
 типа,  причем выполняются обычные арифметические преобразования Е1
 и Е2, а результатом операции является поразрядное исключающее  ИЛИ
 для Е1 и Е2. Каждый бит результата определяется таблицей 1.21.

Операция поразрядного включающего ИЛИ  |

      Синтаксис этой операции следующий:

    выражение-включающее-ИЛИ:
       выражение-исключающее-ИЛИ
       выражение-включающее-ИЛИ  |  выражение-исключающее-ИЛИ

      В  выражении  E1  | E2 оба операнда должны быть интегрального
 типа, причем выполняются обычные арифметические преобразования  Е1
 и  Е2,  а результатом операции является поразрядное включающее ИЛИ
 для Е1 и Е2. Каждый бит результата определяется таблицей 1.21.

Операция логического И  &&

       Синтаксис этой операции следующий:

    выражение-логическое-И:
       выражение-включающее-ИЛИ
       выражение-логическое-И &&  выражение-включающее-ИЛИ

      В выражении E1 && E2 оба операнда должны быть скалярного  ти-
 па.  Результат операции имеет тип int и равен 1 (истина), если оба
 значения, Е1 и Е2 ненулевые; в противном случае результат равен  0
 (ложь).

      В  отличие от поразрядной операции &, операция && гарантирует
 расчет выражения в последовательности слева-направо: первым вычис-
 ляется Е1; если Е1 равен 0, то Е1 && E2 дает 0 (ложь), и Е2 не вы-
 числяется вообще.

Операция логического ИЛИ  ||

       Синтаксис этой операции следующий:

    выражение-логическое-ИЛИ:
       выражение-логическое-И
       выражение-логическое-ИЛИ  ||  выражение-логическое-И

      В выражении E1 || E2 оба операнда должны быть скалярного  ти-
 па. Результат операции имеет тип int и равен 1 (истина), если одно
 из значений, Е1 или Е2 ненулевое; в противном случае результат ра-
 вен 0 (ложь).

      В  отличие от поразрядной операции |, операция || гарантирует
 расчет выражения в последовательности слева-направо: первым вычис-
 ляется Е1; если Е1 не равен 0, то Е1 || E2 дает 1 (истина),  и  Е2
 не вычисляется вообще.
                             Условная операция ?:

      Синтаксис этой операции следующий:

    условное-выражение
       выражение-логическое-ИЛИ
       выражение-логическое-ИЛИ  ?  выражение : условное-выражение

      В  выражении  Е1  ? Е2 : Е3 операнд Е1 должен быть скалярного
 типа. Операнды Е2 и Е3 должны удовлетворять одному  из  приводимых
 ниже правил:

 1. Оба операнда - арифметического типа.

 2. Оба операнда имеют совместимые типы структуры или объединения.

 3. Оба операнда - типа void.

 4.  Оба операнда имеют тип указателя на квалифицированные или нек-
    валифицированные версии совместимых типов.

 5. Один операнд имеет тип указателя, а второй является  константой
    типа пустого указателя.

 6.  Один операнд имеет тип указателя на объект или неполный тип, а
    второй - тип указателя на квалифицированную или  неквалифициро-
    ванную версию типа void.

      Прежде всего вычисляется Е1; если он имеет ненулевое значение
 (истина),  то Е2 вычисляется, а Е3 игнорируется. Если Е1 дает ноль
 (ложь), то Е3 вычисляется, а Е2 игнорируется.  Результат  операции
 Е1 ? Е2 : Е3 зависит от того, который из операндов, Е2 или Е3, бу-
 дет вычисляться.

      В  случае 1 оба операнда, Е2 и Е3, подвергаются обычным ариф-
 метическим преобразованиям, а типом результата  будет  общий  тип,
 получаемый в результате преобразований.

      В случае 2 типом результата будет являться общий тип структу-
 ры или объединения Е2 или Е3.

      В случае 3 результат будет иметь тип void.

      В  случаях  4  и  5 типом результата является указатель типа,
 квалифицированного всеми квалификаторами типов, на которые  указы-
 вают оба операнда.

      В случае 6 типом результата будет тип операнда, не являющего-
 ся указателем на void.
                              Операции присвоения

      Существует  одиннадцать операций присвоения. самым простым из
 них является операция =; остальные называются составными операция-
 ми присвоения.

      Синтаксис операций присвоения следующий:

    выражение-присвоения:
       условное-выражение
       унарное-выражение операция присвоения выражение-присвоения

    операция-присвоения: одно из
       =     *=    /=    %=    +=    -=
       <<=   >>=   &=    ^=    |=

 Простая операция присвоения =

      В выражении Е1 = Е2 Е1 должен быть  модифицируемым  именующим
 выражением.  Значение Е2 после преобразования к типу Е1 помещается
 в объект, задаваемый Е1 (замещая предыдущее значение Е1). Значение
 выражнения присвоения это значение Е1 после  присвоения.  Само  по
 себе выражение присвоения не является именующим значением.

      Операнды  Е1  и  Е2 должны удовлетворять одному из следующего
 набора правил:

 1. Е1 имеет квалифицированный или неквалифицированный арифметичес-
    кий тип, а Е2 имеет арифметический тип.

 2. Е1 имеет квалифицированную или неквалифицированную версию  типа
    структуры или объединения, совместимого с типом Е2.

 3.  Е1  и Е2 это указатели на квалифицированную или неквалифициро-
    ванную версии совместимых типов , а тип, на  который  указывает
    левый  операнд, имеет все квалификаторы типа, на который указы-
    вает правый операнд.

 4. Один из операндов, Е1 или Е2, является указателем  объекта  или
    неполного  типа, а другой - указвтелем на квалифицированную или
    неквалифицированную версию void. Тип, на который указывает  ле-
    вый операнд, имеет все квалификаторы типа, на который указывает
    правый операнд.

 5. Е1 является указателем, а Е2 - константой типа пустого указате-
    ля.

 Составные операции присвоения

      Составные  операции вида операция=, где "операция" - это один
 из десяти символов операции * / % + - << >> & ^ |, интерпретируют-
 ся следующим образом:

      Е1 операция= Е2

 имеет тот же эффект, что и

      Е1 = Е1 операция Е2

 за исключением того, что именующее значение Е1 вычисляется  только
 один раз. Например, Е1 += Е2 это то же самое, что Е1 = Е1 + Е2.

      Правила для составных операций присвоения, следовательно, та-
 кие же, как и описанные в предыдущем разделе (для простой операции
 присвоения =).
                              Операция с запятой

      Синтаксис этой операции следующий:

    выражение:
       выражение-присвоения
       выражение , выражение-присвоения

      В выражении с запятой

    Е1,Е2

 левый операнд Е1 вычисляется как выражение void, затем Е2 вычисля-
 ется  таким образом, что дает результат и тип выражения с запятой.
 Рекурсивно, выражение

    Е1,Е2,...,Уn

 дает в результате вычисляемые слева-направо Ei, а значение  и  тип
 En  определяет  результат всего выражения в целом. Для того, чтобы
 избежать неоднозначности интерпретации запятых с учетом  существо-
 вания запятых при задании аргументов функции и в списках инициали-
 зации, следует использовать круглые скобки. Например,

    func(i, (j = 1, j +4), k);

 вызывает func с тремя аргументами, а не с четырьмя. Эти аргументы:
 i, 5 и k.
                                   Операторы

      Операции  определяют  поток управления выполнением программы.
 При отсутствии заданных операторов перехода и выбора операторы вы-
 полняются последовательно, в порядке их следования в исходном коде
 программы. В следующей таблице показан синтаксис операторов:

 Операторы Turbo C++                                  Таблица 1.22
 -----------------------------------------------------------------
 оператор:
    оператор-с-меткой
    составной-оператор
    оператор-выражение
    оператор-выбора
    оператор-итерации
    оператор-перехода
    asm-оператор
    объявление (только С++)

 asm-оператор:
    asm лексемы новая-строка
    asm лексемы;
    asm {лексемы; <лексемы;>=
        <лексемы;>
        }

 оператор-с-меткой
    идентификатор : операция
    case выражение-типа-константы : оператор
    default : оператор

 составной-оператор:
    { <список-объявления> <список-операторов> }

 список-объявления:
    объявление
    список-объявления  объявление

 список-операторов:
    <выражение>;

 оператор-выбора:
    if (выражение) оператор
    if (выражение) оператор else оператор
    switch (выражение) выражение

 выражение-итерации:
    while (выражение) оператор
    do оператор while (выражение);
    for (оператор-нач-условия <выражение> ; <выражение>)оператор

 оператор-нач-условия:
    оператор-выражение
    объявление (только С++)

 оператор-перехода:
    goto идентификатор;
    continue ;
    break ;
    return <выражение>;
 -----------------------------------------------------------------

Блоки

      Составной оператор, или блок, представляет собой список (воз-
 можно, пустой) операторов, заключенных  в  фигурные  скобки  ({}).
 Синтаксически блок можно рассматривать в качестве единого операто-
 ра,  но он играет также роль в определении контекста идентификато-
 ров. Идентификатор, объявленный в пределах блока, имеет  контекст,
 начиная с точки объявления и кончая заключающей скобкой. Блоки мо-
 гут иметь любую глубину вложенности.

Операторы-с-метками

      Оператору можно присвоить метку следующим образом:

 1. идентификатор-метки : оператор

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

 2. case выражение-типа-константы : оператор
    default : оператор

      Операторы  с метками case и default используются только в со-
 четании с операторами выбора.

Операторы-выражения

      Любое выражение, за которым следует двоеточие, образует  опе-
 ратор-выражение:

      <выражение>

      Turbo  C++ выполняет операторы-выражения, вычисляя выражения.
 Все побочные эффекты от этого вычисления завершаются до начала вы-
 полнения следующего  оператора.  Большинство  операторов-выражений
 представляют собой операторы присвоения или вызовы функций.

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

      Операторы выбора, или операторы управления потоком выполнения
 программы, выполняют выбор одной из альтернативных ветвей програм-
 мы, проверяя для этого определенные значения. Сущесвует  два  типа
 операторов выбора: if...else и switch.

 Операторы if

      Базовый оператор if имеет следующий шаблон:

    if  (условное-выражение)  оператор-если-"истина"   опера-
                            тор-если-"ложь"

      Заключение условного-выражения в круглые скобки является важ-
 ным моментом синтаксиса этого оператора.

      Условное-выражение должно быть скалярного типа. Это выражение
 вычисляется. Если оно является нулевым (или пустым в  случае  типа
 указателя),  мы говорим, что условное-выражение ложно; в противном
 случае оно истинно.

      Если предложение else отсутствует, а условное-выражение  дает
 значение  "истина",  то выполняется оператор-если-"истина"; в про-
 тивном случае он игнорируется.

      Если  задано  опциональное  предложение   else   оператор-ес-
 ли-"ложь",  а условное-выражение дает значение "истина", то выпол-
 няется оператор-если-"истина"; в противном случае выполняется опе-
 ратор-если"ложь".

 Примечание

      В отличие от, например, Паскаля, Turbo C++ не имеет специаль-
 ного булевого типа данных. В условных проверках роль  такого  типа
 может играть целочисленная переменная или указатель. Выражение от-
 ношения  (a > b) (если оно допустимо) дает int 1 (истина), если (a
 > b), и int 0 (ложь), если (a < b). Преобразования указателей  вы-
 полняются  таким образом, что значение указателя всегда может быть
 корректно сравнено с выражением типа константы,  дающим  0.  Таким
 образом, сравнение для пустых указателей может быть сделано в виде
 if (lptr)... или if (ptr == 0)....

      Оператор-если-"ложь"  и оператор-если-"истина" сами могут яв-
 ляться операторами if, что позволяет организовывать любую  глубину
 вложенности  условных проверок. При использовании вложенных конст-
 рукций if...else следует быть внимательным и обеспечивать правиль-
 ный выбор выполняемых операторов. Оператор endif здесь  отсутству-
 ет; любая неоднозначность конструкции "else" разрешается сопостав-
 лением  else  с последним найденным на уровне данного блока if без
 else. Например,

    if (x == 1)
       if (y == 1) puts("x=1 и y=1");
    else puts("x != 1");

 дает неверное решение! else, независимо от ваших намерений, сопос-
 тавляется второму оператору if.  Правильное  решение  это:  x=1  и
 y!=1. Отметим действие фигурных скобок:

    if (x == 1)
    {
       if (y == 1) puts("x = и y=1");
    }
    else puts("x != 1");  // правильное решение

 Операторы switch

      Оператор switch использует следующий базовый формат:

    switch (переключающее-выражение) оператор-варианта

      Оператор  switch  позволяет  передавать  управление одному из
 нескольких операторов с меткой варианта в зависимости от  значения
 переключающего выражения. Последнее должно быть интегрального типа
 (в  С++  оно может быть типа класса, при условии, что возможно его
 однозначное преобразование к интегральному типу.) Любой оператор в
 операторе-варианта (включая пустой оператор)  может  быть  помечен
 одной или более меткой варианта:

    case выражение-типа-константы-i : оператор-варианта-i

 где  каждое выражение-типа-константы-i должно иметь уникальное це-
 лочисленное значение (преобразуемое к типу управляющего выражения)
 в пределах объемлющего оператора switch.

      Допускается иметь в одном операторе switch повторяющиеся кон-
 станты варианта.

      Оператор может иметь также не более одной метки default:

    default : оператор-умолчания

      После вычисления переключающего-выражения выполняется  сопос-
 тавление  результата  с  одним из выражений-типа-константы-i. Если
 найдено соответствие, то управление  передается  оператору-вариан-
 та-i с меткой, для которой найдено соответствие.

      Если  соответствия не найдено и имеется метка default, то уп-
 равление передается оператору-умолчания. Если соответствия не най-
 дено и метка default отсутствует, то никакие операторы не выполня-
 ются. Когда программа встречает метки case и default, это не  про-
 изводит  на  нее  никакого  действия. Управление просто передается
 дальше через метки следующему оператору или переключателю. Для то-
 го, чтобы остановить выполнение группы операторов для  конкретного
 варианта, следует использовать оператор break.
                              Операторы итерации

      Операторы  итерации  позволяют организовывать циклическое вы-
 полнение набора операторов программы. Turbo C++  имеет  три  формы
 операторов итерации : циклы while, do и for.

 Операторы while

      Общий формат данного оператора следующий:

    while (условное-выражение) оператор-пока-"истина"

      Оператор тела цикла, оператор-пока-"истина", будет циклически
 повторяться  до  тех  пор,  пока вычисление условного выражения не
 даст значения ноль (ложь).

      Условное выражение вычисляется и проверяется первым (как опи-
 сано на стр.93 оригинала). Если это значение  ненулевое  (истина),
 то  выполняется  оператор-пока-"истина"; если не встречен оператор
 перехода, выполняющий выход из цикла, то условное-выражение вычис-
 ляется снова. цикл повторяется до тех пор, пока условное-выражение
 не даст значения0.

      Как и в случае оператора if, выражения типа  указателя  могут
 сравниваться  с пустым указателем, так что while (ptr) ... эквива-
 лентно

    while (ptr != NULL)...

      Цикл while представляет  собой  удобный  способ  сканирования
 строк и других заканчивающихся пустым символом структур данных:

    char str[10]="Borland";
    char *ptr=&str[0];
    int count=0;
    //...
    while (*ptr++)  // цикл до конца строки
       count++;

      При  отсутствии  операторов  перехода  оператор-пока-"истина"
 должен некоторым способом воздействовать на значение условного-вы-
 ражения, либо последнее должно изменяться во время его вычисления,
 с тем, чтобы предотвратить зацикливание.

 Операторы do while

      Общий формат этих операторов следующий:

    do выполняемый-оператор while (условное-выражение)

      Выполняемый-оператор циклически повторяется до тех пор,  пока
 вычисление  условного-выражения  не даст 0 (ложь). Главное отличие
 этого оператора от оператора while состоит в том, что условное-вы-
 ражение здесь проверяется не до, а после первого  выполнения  тела
 цикла.  Гарантировано  как минимум одно его выполнение. На тип ус-
 ловного-выражения накладывается то же самое ограничение (оно долж-
 но быть скалярным).

 Операторы for

      Формат оператора for в С следующий:

    for(<выражение-инициализации>;<выражение-проверки>;
        <выражение-инкремента>) оператор

      В С++ <выражение-начального-значения> может быть как  выраже-
 нием, так и объявлением.

      Последовательность  действий при выполнении данного оператора
 следующая:

 1. Выполняется вычисление  выражения-инициализации,  если  таковое
    задано.  Как следует из его названия, это выражение обычно ини-
    циализирует один или несколько счетчиков цикла, но его  синтак-
    сис в действительности позволяет любую степень сложности (вклю-
    чая  в  случае С++ объявления). Отсюда следует вывод, что любая
    программа в С может быть записана в  виде  единственного  цикла
    for. (Однако не пытайтесь писать в таком стиле без должной под-
    готовки. Такое доступно лишь высоким профессионалам).

 2. Выражение-проверки вычисляется по правилам, приводимым для цик-
    лов  while. Если выражение-проверки ненулевое (истина), то опе-
    ратор тела цикла выполняется.  Пустое  выражение  трактуется  в
    данном случае как while(1), то есть как если бы условие провер-
    ки  выполнялось  всегда.Если  выражение-проверки  дает значение
    ноль (ложь), то цикл for прекращается.

 3. Выражение-инремента выполняет приращения одного или  нескольких
    цикловых счетчиков.

 4. Выражение "оператор" (возможно, пустое) вычисляется, после чего
    управление возвращается на шаг 2.

      Если  какие-либо из опциональных элементов опущены, то вместо
 них должны быть заданы соответствующие точки с запятой:

    for (;;) {   // то же, что и for(;1;)
       // вечный цикл
    }

 C++: Правила С для операторов for применимы и в С++. Однако, выра-
 жение-инициализации в С++ может также включать в себя  и  объявле-
 ние.  Контекст  объявленного  идентификатора продолжается до конца
 данного оператора, не далее. Напрмер,

    for (int i = 1; i < j; ++i)
    {
       if (i ...) ...   // здесь обращаться к значению i можно
    }
    if (i ...)          // а здесь нельзя : i вне контекста
                              Операторы перехода

      При выполнении оператор перехода происходит безусловная пере-
 дача управления в некоторую точку программы. Существует четыре та-
 ких оператора: break, continue, goto и return.

 Операторы break

      Синтаксис следующий:

    break;

      Оператор break можно использовать  только  внутри  операторов
 итерации  (while,  do  и  for - циклы) или с оператором switch. Он
 прекращает выполнение итерации  или  оператора  switch.  Поскольку
 операторы итерации и оператор switch могут комбинироваться и иметь
 любую  глубину  вложенности,  следует обращать особое внимание на
 то, чтобы оператр break выполнял выход именно из нужного цикла или
 оператора switch. Правило состоит в том, что оператор break закан-
 чивает выполнение ближайшего к нему объемлющего цикла итерации или
 оператора switch.

 Операторы continue

      Синтаксис следующий:

    continue;

      Оператор continue может быть использован только внутри опера-
 тора итерации; он передает управление на проверку  условия  циклов
 while и do, либо на выражение инкремента цикла for.

      При  вложенности  циклов итерации оператор continue считается
 принадлежащим ближайшей объемлющей итерации.

 Операторы goto

      Синтаксис следующий:

    goto метка;

      Оператор goto передает управление оператору, имеющему указан-
 ную "метку" (См. "Операторы с метками" на стр.92 оригинала), кото-
 рый должна находиться в пределах той же функции.

 С++: В С++ допустимо обойти объявление с явным или неявным инициа-
 лизатором, если это объявление не находится во  внутреннем  блоке,
 который также обходится.

 Операторы return

      Если тип возврата функции не равен void, то тело функции дол-
 жно содержать как минимум один оператор return следующего формата:

    return выражение-возврата;

 где  выражение-возвратадолжно быть типа type или типа, преобразуе-
 мого к типу, заданному type, при присвоении.  Значение  выражения-
 возврата и есть значение, возвращаемое данной функцией. Выражение,
 вызывающее  функцию,  вида  func(список-действительных-аргументов)
 является значением rvalue типа type, а не именующим (lvalue)  зна-
 чением.

    t = func(arg);     // так можно
    func(arg)  =  t; /* в С так нельзя; в С++ так можно, если типом
                          возврата func является ссылка */
    (func(arg))++; /* в С так нельзя; в С++ так можно,  если  типом
                          возврата func является ссылка */

      Выполнение  вызова  функции  заканчивается,  когда встретился
 оператор return; если оператор return отсутствует,  то  выполнение
 "проваливается"  к последней закрыващей фигурной скобке тела функ-
 ции.

      Если тип возврата void, то  оператор  return  можно  записать
 как:

    {
       ...
       return;
    }

 без  выражения-возврата,  либо  оператор  return вообще может быть
 опущен.

С++

      В целом, С++ является над-множеством языка С.  Это  означает,
 что,  вообще  говоря, можно компилировать программы С в среде С++,
 однако компилировать программы С++ в среде С при наличии в них ка-
 ких-либо специфических для С++ конструкций нельзя. Некоторые ситу-
 ации требуют специального внимания. Одна и  та  же  функция  func,
 дважды объявленная в С с различными значениями аргументов, вызовет
 ошибку  повторения  имен.  Однако, в С++ func интерпретируется как
 перегруженная функция - а то, допустимо это или  нет,  зависит  от
 других  обстоятельств. Общие вопросы программирования на С++ см. в
 главе 5, "Основы С++" документа "Начало работы". Глава 6, "Краткий
 справочник по С++" в том же документе поможет вам  быстро  понять,
 как именно работают конструкции языка.

      Хотя  С++ вводит новые ключевые слова и операции для работы с
 классами, некоторые средства С++ применимы вне контекста  классов.
 Сначала  мы рассмотрим тиенно эти, используемые незвисимо от клас-
 сов средства, а затем займемся спецификой работы с классами и свя-
 занными с ними механизмами.
                                    Ссылки

      Установка ссылок при помощи указателей и обращение по ссылкам
 рассматриваются на стр.80 оригинала.

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

 Простые ссылки

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

    int i = 0;
    int &ir = i;   // ir является алиасом i
    ir = 2;        // то же, что i = 2

      В  данном примере создается именующее значение ir, являющееся
 алиасом i, при условии, что инициализатор имеет тот же тип, что  и
 ссылка. Выполнение операций с ir имеет тот же результат, что и вы-
 полнение их с i. Напрмер, ir = 2 присваивает 2 переменной i, а &ir
 возвращает адрес i.

 Аргументы типа ссылки

      Декларатор ссылки может также быть использован для объявления
 в функции параметров типа ссылки:

    void func1 (int i);
    void func2 (int &ir);   // ir имеет тип "ссылка на int"
      ...
    int sum=3;
    func1(sum); // sum передается по значению func2(sum) // sum пе-
    редается по ссылке

      Переданный  по ссылке аргумент sum может быть изменен прямо в
 func2. Напротив, func1 получает только копию аргумента sum  (пере-
 данного  по  значению), поэтому сама переменная sum функцией func1
 изменена быть не может.

      При передаче фактического аргумента x по значению  соответст-
 вующий  формальный аргумент в функции принимает копию x. Любые из-
 менения этой копии в теле функции не отражаются на самом  значении
 x.  Разумеется,  функция  может возвратить значение, которое затем
 может быть использовано для изменения x, но  самостоятельно  изме-
 нить  напрямую параметр, переданный ей по значению, функция не мо-
 жет.

      Традиционный метод С для изменения x состоит в  использовании
 в качестве фактического аргумента &x, то есть адреса x, а не само-
 го  значения  x.  Хотя &x передается по значению, функция получает
 доступ к x благодаря тому, что ей доступна  полученная  копия  &x.
 Даже  если  функции не требуется изменять значения x, тем не менее
 полезно (хотя это чревато возможностью нежелательных побочных  эф-
 фектов)  передавать &x, особенно если x представляет собой большую
 по размерам структуру данных. Передача x непосредственно по значе-
 нию ведет к  бесполезным  затратам  памяти  на  копирование  такой
 структуры данных.

      Сравним три различных реализации функции treble:

 Реализация 1

    int treble_1(n)
    {
       return 3*n;
    }
    ...
    int x, i = 4;
    x = treble_1(i);       // теперь x = 12, i = 4
    ...

 Реализация 2

    void treble_2(int* np)
    {
       *np = (*np)*3;
    }
    ...
    treble_2(int &i);      // теперь i = 12


 Реализация 3

    void treble_3(int& n)  // n имеет тип ссылки
    {
       n = 3*n;
    }
    ...
    treble_3(i);           // теперь i = 36

      Объявление формального аргумента type& t (или, что эквивален-
 тно,  type  &t)  устанавливает  t  как  имеющую тип "ссылки на тип
 type". Поэтому при вызове treble_3 с действительным аргументом  i,
 i  используется  для инициализации формального аргумента ссылки n.
 Следовательно, n играет роль алиаса i, и n = 3*n также присваивает
 i значение 3*i.

      Если инициализатор представляет собой  константу  или  объект
 нессылочного  типа, то Turbo C++ создаст временный объект, для ко-
 торого ссылка действует как алиас:

    int& ir = 6; /* создается временный объект int, с именем алиаса
                       ir, который получает значение 6 */
    float f;
    int& ir2 = f; /* создается временный объект int, с именем алиа-
                       са ir2, f перед присвоением преобразовывает-
                       ся */
    ir2 = 2.0       // теперь ir2 = 2, но f остается без изменений

      Автоматическое создание временных объектов  позволяет  выпол-
 нять преобразования ссылочных типов, если формальные и фактические
 аргументы имеют различные (но совместимые для привоения) типы. При
 передаче  по значению, разумеется, проблем с преобразованием типов
 меньше, поскольку перед присвоением  формальному  аргументу  копия
 фактического аргумента может быть физически изменена.

Операция доступа к контексту

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

    int i;                  // глобальная переменная i
    ...
    void func(void);
       {
          int i=0; // локальная i скрывает глобальную i i =  3;  //
          эта i - локальная
          ::i = 4;          // эта i - глобальная
          printf ("%d",i);  // будет напечатано значение 3
       }

      Приведенный  код  работает также, если i является статической
 переменной уровня файла.

      При использовании с типами классов операция ::  имеет  другой
 смысл; это рассматривается далее в данной главе.
                             Операции new и delete

      Операции  new и delete выполняют динамическое распределение и
 отмену распределения памяти, аналогично, но с более высоким  прио-
 ритетом, нежели стандартныке библиотечные функции семейства malloc
 и free (См. справочник по библиотеке).

      Упрощенный синтаксис:

    указатель-имени = new имя <инициализатор-имени>;
    delete указатель-имени;


      Имя может быть любого типа, кроме "функция, возвращающая ..."
 (однако, указатели функций здесь допустимы).

      new пытается создать объект с типом "имени", распределив (при
 возможности)  sizeof(имя) байт в свободной области памяти (которую
 также называют "кучей"). Продолжительность существования в  памяти
 данного  объекта - от точки его создания и до тех пор, пока опера-
 ция delete не отменит распределенную для него память, либо до кон-
 ца работы программы.

      В случае успешного завершения new возвращает указатель нового
 объекта. Пустой указатель означает неудачное  завершение  операции
 (например, недостаточный объем или слишком большая фрагментирован-
 ность кучи). Как и в случае malloc, прежде чем пытаться обращаться
 к  новому объекту, следует проверить укеазатель на наличие пустого
 значения. Однако, в отличие от malloc, new сама  вычисляет  размер
 "имени", и явно указывать операцию sizeof нет необходимости. Далее
 возвращаемый указатель будет иметь правильный тип, "указатель име-
 ни", без необходимости явного приведения типов.

    name *nameptr    // name может иметь любой тип, кроме функции
       ...
    if (!(nameptr = new name)) {
       errmsg("Недостаточно памяти для name");
       exit (1);
    }
    // использование *nameptr для инициализации объекта new name
    ...
    delete nameptr;  // удаление name и отмена распределения
                     // sizeof(name) байтов памяти

      new, будучи ключевым словом, не нуждается в прототипе.



 Операция new с массивами

      Если "имя" это массив, то возвращаемый new указатель указыва-
 ет на первый элемент массива. При создании с помощью new многомер-
 ных массивов следует указывать все размерности массива:

    mat_ptr = new int[3][10][12];     // так можно
    mat_ptr = new int[3][][12];       // нельзя
    mat_ptr = new int[][10][12];      // нельзя

 ::operator new

      При  использовании  с объектами, не являющимися классами, new
 вызывает стандартную библиотечную подпрограмму, global  ::operator
 new.  Для объектов классов типа "имя" может быть определена специ-
 альная операция имя::operator new. new, применительно  к  объектам
 класса  "имя",  запускает  соответствующую  операцию имя::operator
 new;  в  противном  случае   используется   стандартная   операция
 ::operator new.

 Инициализаторы с операцией new

      Другим преимуществом операции new по сравнению с malloc явля-
 ется опциональный инициализатор (хотя calloc очищает его распреде-
 ление к нулю). При отсутствии явных инициализаторов объект, созда-
 ваемый  new,  содержит  непредсказуемые данные ("мусор"). Объекты,
 распределяемые new, за исключением массивов,  могут  инициализиро-
 ваться соответствующим выражением в круглых скобках:

    int_ptr = new int(3);

      Массивы  классов с конструкторами инициализируются при помощи
 конструктора-умолчания (см. стр.115 оригинала). Определяемая поль-
 зователем операция new с задаваемой  пользователем  инициализацией
 играет ключевую роль в конструкторах С++ для объектов типа класса.
                                    Классы

      Классы  С++  предусматривают создание расширений системы пре-
 допределенных типов. Каждый тип класса представляет собой уникаль-
 ное множество объектов и операций (правил), а  также  преобразова-
 ний,  используемых для создания, манипулирования и уничтожения та-
 ких объектов. Могут быть объявлены производные классы, наследующие
 компоненты одного или более базовых (порождающих) классов.

      В С++ структуры и объединения рассматриваются  как  классы  с
 определенными умолчаниями правил доступа.

      Упрощенный,  в  "первом  приближении",  синтаксис  объявления
 класса следующий:

    ключ-класса  имя-класса <:базовый-список>[<список-компонентов>]

      Ключ-класса - это class, struct или union.

      Опциональный базовый-список  перечисляет  базовый  класс  или
 классы,  из  которого  имя-класса  берет (или наследует) объекты и
 правила.  Если  объявлены  некоторые  базовые  классы,  то   класс
 "имя-класса" называется производным классом (см.стр.110, "Доступ к
 базовым  и производным классам"). Базовый список содержит специфи-
 каторы доступа по умолчания и опциональные их переопределения, ко-
 торые могут модифицировать права  доступа  производного  класса  к
 компонентам  базовых  классов  (см. стр.108 оригинала, "Управление
 доступом к компонентам").

      Опциональный список-компонентов объявляет  компоненты  класса
 (данные  и функции) для имени-класса с умолчаниями и переопределе-
 ниями спецификаторов доступа, которые могут влиять  на  то,  какие
 функции к каким компонентам класса могут иметь доступ.

 Имена классов

      Имя-класса  это  любой  идентификатор,  уникальный в пределах
 своего контекста. В классах структур и в  объединениях  имя-класса
 может  быть опущено (см. "Структуры и типы, определяемые пользова-
 телем (typedef) без тегов" на стр.65 оригинала).

 Типы классов

      Объявление создает уникальный тип, тип  класса  "имя-класса".
 Это  позволяет вам объявлять последующие объекты класса (или вхож-
 дения класса) данного типа, а также объекты, являющиеся  производ-
 ными  от этого типа (такие как указатели, ссылки, массивы объектов
 "имя-класса" и т.д.):

    class X { ... };
    X x, &xr, *xptr, xarray[10];
    /* четыре объекта: типа X, ссылка на X, указатель на X и масив
       элементов типа X */

    struct Y { ... };
    Y y, &yr, *yptr, yarray[10];
    // С позволяет иметь
    // struct Y y, &yr, *yptr, yarray[10];

    union Z { ... };
    Z z, &zr, *zptr, zarray[10];
    // С позволяет иметь
    // union Z z, &zr, *zptr, zarray[10];

      Отметим различие между объявлением структур и объединений в С
 и С++: в С ключевые слова struct и union обязательны, но в С++ они
 нужны только в том случае, когда имена классов, Y и Z, скрыты (см.
 следующий раздел).

 Контекст имени класса

      Контекст имени класса является локальным, с  некоторыми  осо-
 бенностями,  характерными для классов. Контекст имени класса начи-
 нается с точки его объявления и заканчивается вместе с  объемлющим
 блоком.  Имя  класса  скрывает  любой класс, объект, нумератор или
 функцию с тем же именем в объемлющем контексте.  Если  имя  класса
 объявлено  в контексте, содержащем объявление объекта, функции или
 нумератора с тем же именем, обращение к классу возможно только при
 помощи уточненного спецификатора типа. Это означает, что с  именем
 класса  нужно  использовать  ключ класса, class, struct или union.
 Например,

    struct S { ... };
    int S(struct S *Sptr);
    void func(void)
    {
       S t;          // недопустимое объявление: нет ключа класса
                     // и функции S в контексте
       struct S s; //  так  можно:  есть  уточнение  ключем  класса
       S(&s); // так можно: это вызов функции
    }

      С++ позволяет также неполное объявление класса:

    class X;    // еще нет компонентов !
    struct Y;
    union Z;

      Неполные объявления позволяют некоторые ссылки к именам клас-
 сов  X,  Y  или Z (обычно ссылки на указатели объектов классов) до
 того, как классы будут полностью определены (см. "Объявление ком-
 понентов  структуры" на стр.65 оригинала). Разумеется, прежде чем
 вы сможете объявить и использовать объекты классов, вы должны вы-
 полнить полное объявление классов со всеми их компонентами.

 Объекты классов

      Объекты  классов могут быть присвоены (если не было запрещено
 копирование), переданы как аргументы функции, возвращены фунукцией
 (за некоторыми исключениями) и т.д. Прочие операции с объектами  и
 компонентами  классов  могут  быть различными способами определены
 пользователем, включая функции-компоненты и "друзья", а также  пе-
 реопределение стандартных функций и операций при работе с объекта-
 ми  конкретного класса. Переопределение функций и операций называ-
 ется перегрузкой. Операции и функции, ограниченные объектами конк-
 ретного класса (или  взаимосвязанной  группы  классов)  называются
 функциям-компонентами данного класса. С++ имеет механизм, позволя-
 ющий  вызвать то же имя функции или операции для выполнения другой
 задачи, в зависимости от типа или числа аргументов или операндов.

 Список компонентов класса

      Опциональный список-компонентов представляет собой последова-
 тельность объявлений данных (любого типа, включая нумераторы,  би-
 товые  поля  и  другие классы) и объявлений и определений функций,
 каждое из которых может иметь  опциональные  спецификаторы  класса
 памяти  и модификаторы доступа. Определенные таким образом объекты
 называются компонентами класса. Спецификаторы класса памяти  auto,
 extern  и  register в данном случвае недопустимы. Компоненты могут
 быть объявлены со спецификаторами класса памяти static.

 Функции-компоненты

      Функция, объявленная  без  спецификатора  friend,  называется
 функцией-компонентом  класса. Функции, объявленные с модификатором
 friend, называется "функцией-другом".

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

 Ключевое слово this

      Не-статические  функции компонентов работают с объектами типа
 класса, с которыми они вызываются. Например,  если  x  это  объект
 класса X, а f это функция-компонент X, то вызов функци x.f() рабо-
 тает с x. Аналогичным образом, если xptr есть указатель объекта X,
 то  вызов функции xptr->() работает с *xptr. Откуда f может знать,
 с каким x работать? С++ передает  f  указатель  на  x,  называемый
 this. this передается как скрытый аргумент при вызове не-статичес-
 ких функций-компонентов.

      Ключевое  слово this представляет собой локальную переменную,
 доступную в теле не-статической функции-компонента. this не требу-
 ет объявлений, и на него редко встречаются явные ссылки в  опреде-
 лении функции. Однако, оно неявно используется в функции для ссыл-
 ки  к  компонентам.  Если, например, вызывается x.f(y), где y есть
 компонент X, то this устанавливается на &x, а y устанавливается на
 this->y, что эквивалентно x.y.

 Встраиваемые функции (inline)

      Функция-компонента может быть  объявлена  в  пределах  своего
 класса,  но  определена где-либо в другом месте. И наоборот, функ-
 ция-компонента может быть и объявлена, и определена в своем  клас-
 се,  и тогда она называется встраиваемой функцией. (Некоторые при-
 меры таких функций показаны в главе 5 документа "Начало работы").

      В некоторых случаях Turbo C++ может уменьшить затраты времени
 на вызов функции, подстанавливая вместо вызова функции непосредст-
 венно компилированный код тела функции. Этот  процесс,  называемый
 встраиваемым расширением тела функции, не влияет на контекст имени
 функции  или  ее аргументов. Встраиваемое расширение не всегда по-
 лезно и желательно. Спецификатор inline представляет собой  запрос
 (или  требование) компилятору, в котором вы сообщаете, что встраи-
 ваемые расширения желательны. Как и в случае спецификатора  класса
 памяти  register, компилятор может либо удовлетворить, либо проиг-
 норировать ваше пожелание.

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

    int i;                          // global int
    class X {
       public:
       char*  func(void) { return i; } // inline по умолчанию char*
       i;
    };

 эквивалентно

    inline char* X::func(void) {return i; }

      func определяется "вне" класса с явным спецификатором inline.
 Переменная i, возвращаемая func, есть char* i класса X - см.  раз-
 дел, посвященный контексту компонентов на стр.107 оригинала.


 Статические компоненты

      Спецификатор класса памяти static может  быть  использован  в
 объявлениях компонентов данных и функций-компонентов класса. Такие
 компоненты  называются  статическими и имеют свойства, отличные от
 свойств  не-статических  компонентов.  В   случае   не-статических
 компонентов  для  каждого  объекта  класса  "существует" отдельная
 копия; в случае же статических компонентов существует только  одна
 копия,  а  доступ  к  ней  выполняется  без  ссылки  на какой-либо
 конкретный объект класса. Если х это статический компонент  класса
 Х, то к нему можно обратиться как Х::х (даже если объекты класса Х
 еще  не  созданы). Однако, можно выполнить доступ к х и при помощи
 обычных операций доступа к компонентам. Например,  в  виде  y.x  и
 yptr->x,  где  y это объект класса X, а yptr это указатель объекта
 класса X, хотя выражения y и yptr еще не вычислены.  В  частности,
 статическая   функция-компонент   может   быть   вызвана   как   с
 использованием специального синтаксиса вызова функций компонентов,
 так и без него.

    class X {
       int member_int;
    public:
       static void func(int i, X* ptr);
    };
    void g(void);
    {
       X obj;
       func(1, &obj);     // ошибка, если где-нибудь еще
                          // не определена глобальная func()
       X::func(1, &obj);  // вызов static func() в X
                          // допустим только для статических функций
       obj.func(1, &obj); // то же самое (допустимо как для стати-
                          // ческих, так и не-статических функций)
    }

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

    void X%%func(int i, X* ptr)
    {
       member_int = i;       // на какой объект ссылается
                             // member_int? Ошибка !
       ptr->member_int = 1;  // так можно: теперь мы знаем!
    }

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

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

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

    class X {
       ...
       static int x;
       ...
    }; int X::x = 1;

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

 - уменьшения числа видимых глобальных имен

 - того, чтобы сделать очевидным, какие именно статические объекты
   какому классу принадлежат

 - разрешения управления доступам к их именам.

 Контекст компонента

      Выражение   X::func()   в  примере,  приведенном  на  стр.106
 оригинала, использует  имя  класса  X  с  модификатором  контекста
 доступа, обозначающим, что func, хотя и определена "вне" класса, в
 действительности  является  функцией-компонентом  Х и существует в
 контексте Х. Влияние Х:: распространяется на тело определения этой
 функции. Это объясняет, почему возвращаемое  функцией  значение  i
 относится  к  X::i, char* i из Х, а не к глобальной переменной int
 i. Без модификатора Х:: функция func представляла бы  обычную,  не
 относящуюся  к  классу функцию, возвращающую глобальную переменную
 int i.

      Следовательно, все функции-компоненты находятся  в  контексте
 своего класса, даже если они определены вне этого класса.

      К  компонентам  данных  класса  Х можно обращаться при помощи
 операций выбора . и -> (как и в структурах  С).  Функции-компоненты
 можно  вызывать  также  при  помощи операций выбора (см. "Ключевое
 слово this на стр.105 оригинала). Например,

    class X {
    public:
       int i;
       char name[20];
       X *ptr1;
       X *ptr2;
       void Xfunc(char *data, X* left, X* right); // определение
                                           // находится не здесь
    };
    void f(void);
    {
       X x1, x2, *xptr=&x1;
       x1.i = 0;
       x2.i = x1.i;
       xptr->i = 1;
       x1.Xfunc("stan", &x2, xptr);
    }

      Если m является компонентом или базовым компонентом класса Х,
 то выражение Х::m называется квалифицированным именем;  оно  имеет
 тот  же тип, что и m, и является именующим выражением только в том
 случае, если именующим  выражением  является  m.  Ключевой  момент
 здесь  состоит  в  том,  что  даже если имя класса Х скрыто другим
 именем, квалифицированное имя Х::m тем не менее обеспечит доступ к
 нужному имени класса, m.

      Компоненты класса не могут быть добавлены к нему в другом  разделе
 вашей  программы.  Класс Х не может содержать объекты класса Х, но
 может содержать указатели или ссылки на объекты класса Х  (отметим
 аналогию с типами структур и объединений С).

 Управление доступом к компонентам

      Компоненты   класса   получают   атрибуты  доступа  либо  по
 умолчанию  (в  зависимости  от  ключа  класса   и   местоположения
 объявления),  либо при использовании какого-либо из спецификаторов
 доступа: public, private или protected. Значение  этих  атрибутов
 следующие:

    public     Компонент может быть использован любой функцией.

    private    Компонент может быть использован только функциями-
               компонентами и "друзьями" класса, в котором он
               объявлен.

    protected  То  же  самое,  что  для  private,  но кроме того,
               компонент   может   быть    использован    функциями
               компонентов   и  друзьями  классов,  производных  от
               объявленного   класса,   но   только   в    объектах
               производного      типа.      (Производные     классы
               рассматриваются в следующем разделе).

      Спецификаторы доступа не влияют на  объявления  функций  типа
 friend. (См. раздел "Друзья" классов" на стр.112 оригинала).

      Компоненты   класса  по  умолчанию  имеют  атрибут  private,
 поэтому  для  переопределения  данного  объявления   спецификаторы
 доступа public или protected должны задаваться явно.

      Компоненты  struct  по умолчанию имеют атрибут public, но вы
 можете переопределить  это  умолчание  при  помощи  спецификаторов
 доступа public или protected.

      Компоненты   union   по   умолчанию  имеют  атрибут  public;
 переопределить его нельзя. Все три спецификатора доступа  задавать
 для компонентов объединения недопустимо.

      Модификатор   доступа   по   умолчанию   или   заданный   для
 переопределения атрибута доступа остается действительным для всех
 последующих объявлений  компонентов,  пока  не  встретится  другой
 модификатор доступа. Например,

    class X {
       int i;      // X::i по умолчанию privte
       char ch;    // так же, как и X::ch
    public:
       int j;      // следующие два компонента - public
       int k;
    protected:
       int l;      // X::l - protected
    };

    struct Y {
       int i;      // Y::i по умолчанию public
    private:
       int j;      // Y::j - private
    public:
       int k;      // Y::k - public
    };

    union Z {
       int i;      // public по умолчанию, других вариантов нет
       double d;
    };

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

 Доступ к базовым и производным классам

      При  объявлении производного класса D вы перечисляете базовые
 классы В1, В2 ... в разделяемом запятой базовом-списке:

    ключ-класса D:базовый-список {<список-компонентов>}

      Поскольку  сам  базовый  класс  может  являться   производным
 классом,  то  вопрос  об атрибуте доступа решается рекурсивно: вы
 отслеживаете его до  тех  пор,  пока  не  доберетесь  до  "самого"
 базового класса, породившего все остальные.

      D     наследует    всем    компонентам    базовых    классов.
 (Переопределенные компоненты базовых классов  наследуются,  и  при
 необходимости  доступ  к  ним  возможен при помощи переопределений
 контекста). D может использовать только компоненты базовых классов
 с атрибутами public и protected. Однако, что  будут  представлять
 собой  атрибуты доступа унаследованных компонентов с точки зрения
 D? D может понадобиться  использовать  public  компонент  базового
 класса,  но  при  этом  сделать  его  для внешних функций private.
 Решение здесь состоит  в  том,  чтобы  использовать  спецификаторы
 доступа в базовом-списке.

      При объявлении D вы можете задать спецификатор доступа public
 или private перед классами в базовом-списке:

    class D : public B1, private B2, ... {
    ...
    }

      Задать  в  базовом-списке  protected  нельзя.  Объединения не
 могут содержать базовых классов и не могут сами быть  использованы
 в качестве базовых классов.

      Эти  модификаторы  не  изменяют  атрибутов  доступа  базовых
 членов с точки зрения  базового  класса,  хотя  и  могут  изменить
 атрибуты  доступа  базовых компонентов с точки зрения производных
 классов.

      Умолчанием является private, если D  есть  объявление  класса
 class, и public, если D есть объявление структуры struct.

      Производный класс наследует атрибуты доступа базового класса
 следующим образом:

 базовый   класс  public:  компоненты  public
                       базового класса  становятся  членами  public
                       производного  класса. Компоненты pro- tected
                       базового  класса   становятся   компонентами
                       protected  производного  класса.  Компоненты
                       private   базового   класса   остаются   для
                       базового класса private.

 базовый  класс  private: и public, и protected компоненты базового
                       класса   становятся   private   компонентами
                       производного   класса.   Компоненты  private
                       базового класса остаются для базового класса
                       private.

      В обоих случаях  отметим,  что  компоненты  private  базового
 класса   были  и  остаются  недоступными  для  функций-компонентов
 производного класса, пока в описании доступа  базового  класса  не
 будут явно заданы объявления friend. Например,

    class X : A {     // умолчание для класса - private A
    ...
    }
    /* класс X является производным от класса А */

    class Y : B, public C {   // переопределяет умолчание для С
    ...
    }
    /* класс Y является производным (множественное наследование)
       от B и C.  По умолчанию - private B */

    struct S : D {    // умолчание для struct - public D
    ...               /* struct S - производная от D */
    }
    struct T : private D, E {  // переопределяет умолчание для D
                               // E по умолчанию public
    ...
    }
    /* struct T является производной (множественное наследование)
       от D и T.  По умолчанию - public E  */

      Действие   спецификаторов  доступа  в  базовом  списке  можно
 скорректировать при помощи квалифицированного-имени в  объявлениях
 public или protected для производного класса. Например,

    class B {
       int a;         // по умолчанию private
    public:
       int b, c;
       int Bfunc(void);
    };

    class X : private B {   // теперь в Х a, b, c и Bfunc - private
       int d;               // по умолчанию private. Примечание: f
                            // в Х недоступна
    public:
       B::c;                // c была private; теперь она  public
       int e;
       int Xfunc(void);
    };

    int Efunc(X& x);       // внешняя по отношению к В и Х

      Функция  Tfunc  может  использовать только имена с атрибутом
 public, например c, e и Xfunc.

      Функция Xfunc в X является производной от private B,  поэтому
 она имеет доступ к:

 - "скорректированной-к-типу-public" c

 - "private-относительно-Х" компонентам В:b и Bfunc

 - собственным private и public компонентам: d, e и Xfunc.

      Однако, Xfunc не имеет доступа к "private-относительно-B"
 компоненту a.
                          Виртуальные базовые классы

      При  множественном  наследовании  базовый класс не может быть
 задан в производном классе более одного раза:

    class B { ... };
    class D : B, B { ... }:  // недопустимо

      Однако, базовый класс может быть передан производному  классу
 более одного раза косвенно:

    class X : public B { ... }
    class Y : public B { ... }

    class Z : public X, public Y { ... }  // допустимо

      В  данном  случае  каждый  объект  класса  Z  будет иметь два
 подобъекта класса В. Если это вызывает проблемы,  к  спецификатору
 базового  класса  может  быть  добавлено  ключевое  слово virtual.
 Например,

    class X : virtual public B { ... }
    class Y : virtual public B { ... }
    class Z : public X, public Y { ... }

      Теперь В является виртуальным  базовым  классом,  а  класс  Z
 имеет только один под-объект класса В.
                           "Друзья" классов (friend)

      Друг  F  класса  X это функция или класс, которые, не являясь
 функцией-компонентом Х, имеют тем не менее полные права доступа  к
 компонентам Х private и protected. Во всех прочих отношениях F это
 обычная   с  точки  зрения  контекста,  объявлений  и  определений
 функция.

      Поскольку F  не  является  компонентом  Х,  она  не  лежит  в
 контексте  Х и поэтому не может вызываться операциями выбора x.F и
 xptr->F (где x это объект Х, а xptr это указатель объекта Х.

      Если в объявлении или определении функции в пределах класса Х
 используется спецификатор  friend,  то  такая  функция  становится
 "другом" класса Х.

      Функция-"друг",  определенная  в пределах класса, подчиняется
 тем же правилам встраивания, что и функции-компоненты класса  (см.
 "Встраиваемые  функции" на стр.105 оригинала). Функции-"друзья" не
 зависят  от  их  позиции  в  классе  или  спецификаторов  доступа.
 Например,

    class X {
       int i;                            // private относительно Х
       friend void friend_func(X*, int);
    /* friend_func не является private, хотя она и объявлена
       в разделе private */
    public:
       void member_func(int);
    };
    /*объявления;для обеих функций отметим доступ к private int i*/
    void friend_func(X* xptr, int a) { xptr->i = a; }
    void X::member_func(int a) { i = a; }

    X xobj;

    /* отметим различие в вызовах функций */
    friend_func(&xobj, 6);
    xobj.member_func(6);

      Вы  можете  сделать  все функции класса Y друзьями класса Х в
 одном объявлении:

    class Y;                   // неполное объявление
    class X {
       friend Y;
       int i;
       void member_funcX();
    };

    class Y; {
       void friend_X1(X&);
       void friend_X2(X*);
       ...
    };

      Функции, объявленные в Y, являются друзьями Х, хотя они и  не
 имеют  спецификаторов  friend.  Они  имеют  доступ к компонентам Х
 private, таким как i и member_funcX.

      Отдельные  функции-компоненты  класса  Х  также  могут   быть
 друзьями класса Y:

    class X {
       ...
       void member_funcX();
    }

    class Y {
       int i;
       friend void X::member_funcX();
       ...
    };

      "Дружба"  классов  не транзитивна: если X друг Y, а Y друг Z,
 это не означает, что X - друг Z. Однако, "дружба" наследуется.
                          Конструкторы и деструкторы

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

 1. Они не имеют объявлений значений возврата (даже void).

 2. Они не могут быть унаследованы, хотя производный класс может
    вызывать конструкторы и деструкторы базового класса.

 3. Конструкторы, как и большинство функций С++, могут иметь аргу-
    менты по умолчанию или использовать списки инициализации ком-
    понентов.

 4. Деструкторы могут иметь атрибут virtual, но конструкторы не
    могут.

 5. Вы не можете работать с их адресами:

    main()
    {
       ...
       void *ptr = base::base;    // недопустимо
       ...
    }

 6. Если конструкторы и деструкторы не были заданы явно, то они мо-
    гут быть сгенерированы Turbo C++; во многих случаях они также
    могут быть запущены при отсутствии явного вызова в вашей прог-
    рамме. Любой конструктор или деструктор, создаваемый компилято-
    ром, должен иметь атрибут public.

 7. Вызвать конструктор тем же образом, что и обычную функцию,
    нельзя. Вызов деструктора допустим только с полностью квалифи-
    цированным именем.

 8.
    {
    ...
       X *p;
    ...
       p->X::-X();      // допустимый вызов деструктора
       X::X();          // недопустимый вызов конструктора
    ...
    }

 9. При определении и разрушении объектов компилятор выполняет вы-
    зов конструкторов и деструкторов автоматически.

 10.Конструкторы и деструкторы при необходимости распределения объ-
    екту памяти могут выполнять неявные вызовы операций new и
    delete.

 11.Объект с конструктором или деструктором не может быть использо-
    ван в качестве компонента объединения.

      Если класс Х имеет один или более конструкторов, то  один  из
 них  запускается  всякий  раз  при определении объекта х класса Х.
 Конструктор создает объект х  и  инициализирует  его.  Деструкторы
 выполняют  обратный  процесс,  разрушая  объекты класса, созданные
 конструкторами.

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

      Констукторы отличаются от прочих компонентов функций тем, что
 имеют то же самое имя, что и класс, к которому они относятся.  При
 создании или копировании объекта данного класса происходит неявный
 вызов соответствующего конструктора.

      Конструкторы   глобальных  переменных  вызываются  до  вызова
 функции main. При использовании  стартовой  директивы  pragma  для
 инсталлирования  некоторой  функции  до функции main, конструкторы
 глобальных переменных вызываются до стартовых функций.

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

    class x
    {
    public:
       X();    //конструктор класса Х
    };

      Конструктор класса Х не может принимать Х как аргумент:

    class X {
    ...
    public
       X(X);   // недопустимо
    }

      Параметры конструкторы могут быть любого типа, за исключением
 класса,  компонентом   которого   является   данный   конструктор.
 Конструктор  может  принимать  в качестве параметра ссылку на свой
 собственный класс; в  таком  случае  он  называется  конструктором
 копирования.   Конструктор,   не  принимающий  параметров  вообще,
 называется  конструктором  по  умолчанию.  Далее   мы   рассмотрим
 конструкторы  по  умолчанию;  описание же конструктора копирования
 начинается со стр.116 оригинала.

 Конструктор по умолчанию

      Конструктором по умолчанию  для  класса  Х  называется  такой
 конструктор, который не принимает никаких аргументов: Х::Х(). Если
 для    класса    не    существует    конструкторов,   определяемых
 пользователем, то Turbo C++ генерирует конструктор  по  умолчанию.
 При  таких  объявлениях, как Х х, конструктор по умолчанию создает
 объект х.

 Важное примечание:

      Как и все функции,  конструкторы  могут  иметь  аргументы  по
 умолчанию. Например, конструктор

    X::X(int, int = 0)

 может  принимать  один  или два аргумента. Если данный конструктор
 будет представлен только с одним  аргументом,  недостающий  второй
 аргумент будет принят как int 0. Аналогичным образом, конструктор

    X::X(int = 5, int = 6)

 может  принимать  два  аргумента, один аргумент, либо не принимать
 аргументов   вообще,   причем   в   каждом   случае    принимаются
 соответствующие умолчания. Однако, конструктор по умолчанию Х::Х()
 не  принимает  аргументов  вообще,  и  его  не  следует  путать  с
 конструктором,  например,  X::X(int  =  0),  который  может   либо
 принимать один аргумент, либо не принимать аргументов.

      При вызове конструкторов следует избегать неоднозначностей. В
 следующем  примере  возможно неоднозначное восприятие компилятором
 конструктора   по   умолчанию   и    конструктора,    принимающего
 целочисленный параметр:

    class X
    {
    public:
       X();
       X(int i = 0);
    };

    main()
    {
       X one(10);  // так можно: используется X::X(int)
       X two;      // так нельзя; неоднозначно задано, используется
                   // ли X::X() или X::X(int = 0)
    ...
       return 0;

 Конструктор копирования

      Конструктор  копирования  для класса Х это такой конструктор,
 который может быть вызван с одним единственным аргументом типа  X:
 X::X(const  X&)  или  X::(const  X&,  int  =  0).  В  конструкторе
 копирования допустимыми также  являются  аргументы  по  умолчанию.
 Конструкторы   копирования  запускаются  при  копировании  объекта
 данного  класса,  обычно  в  случае  объявления  с  инициализацией
 объектом другого класса: X x = y. Turbo C++ генерирует конструктор
 копирования  для  класса  X  автоматически, если такой конструктор
 необходим, но в классе Х неопределен.

 Перегрузка конструкторов

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

    class X
    {
       int   integer_part;
       double double_part;
    public:
       X(int i)    { integer_part = i; }
       X(double d) { double_part = d }
    };

    main()
    {
       X one(10);   // запускает X::X(int) и устанавливает
                    // integer_part в значение 10
       X one(3.14); // запускает X::X(double) ш устанавливает
                    // double_part
     ...
       return 0;
     }

 Порядок вызова конструкторов

      В случае, когда  класс  использует  один  или  более  базовых
 классов,  конструкторы  базовых  классов  запускаются до того, как
 будут  вызваны  конструкторы  производного  класса.   Конструкторы
 базового класса вызываются в последовательности их объявления.

      Например, в следующем примере

    class Y {...}
    class X : public Y {...}
    X one;

 вызов конструкторов происходит в следующей последовательности:

    Y();    // конструктор базового класса
    X();    // конструктор производного класса

      В случае множественных базовых классов:

    class X : public Y, public Z
    X one;

 конструкторы вызываются в последовательности их объявления:

    Y();   // первыми следуют конструкторы базового класса
    Z();
    X();

      Конструкторы   виртуальных  базовых  классов  запускаются  до
 каких-либо не-виртуальных базовых классов. Если иерархия  содержит
 множественные   виртуальные   базовые   классы,   то  конструкторы
 виртуальных базовых классов запускаются  в  последовательности  их
 объявления.   Затем,   перед  вызовом  конструкторов  производного
 класса, конструируются не-виртуальные базовые классы.

      Если виртуальный класс является производным от не-виртуальной
 базы,  то  для  того,  чтобы   виртуальный   базовый   класс   был
 сконструирован  правильно, эта не-виртуальная база должна являться
 первой. Код

    class X : public Y, virtual public Z
    X one;

 определяет следующую последовательность:

    Z();   // инициализация виртуального базового класса
    Y();   // не-виртуальный базовый класс
    X();   // произвольный класс

      Либо, в более сложном случае,

    class base;
    class base2;
    class level1 : public base2, virtual public base;
    class level2 : public base2, virtual public base;
    class toplevel : public level1, virtual public level1;
    toplevel view;

      Порядок конструирования view будет принят следующий:

    base();      // старший в иерархии виртуальный базовый класс
                 // конструируется только однажды
    base2();     // не-виртуальная база виртуальной базы level2
                 // вызывается для конструирования level2
    level2();    // виртуальный базовый класс
    base2();     // не-виртуальная база для level1
    level1();    // другая не-виртуальная база
    toplevel();

      В  случае,  когда  иерархия  класса  содержит   множественные
 вхождения  виртуального  базового  класса,  данный  базовый  класс
 конструируется  только  один  раз.  Однако,  если   существуют   и
 виртуальные,   и  не-виртуальные  вхождения  базового  класса,  то
 конструктор класса  запускается  один  раз  для  всех  виртуальных
 вхождений  и  один раз для всех не-виртуальных вхождений базового
 класса.

      Конструкторы  элементов   массива   запускаются   в   порядке
 возрастания индексов массива.

 Инициализация класса

      Объект   класса   с   компонентами   только   public   и  без
 конструкторов или базовых классов  (обычно  это  структура)  может
 быть   инициализирован  при  помощи  списка  инициализаторов.  При
 наличии конструктора класса объекты  либо  инициализируются,  либо
 имеют  конструктор  по  умолчанию. Последний используется в случае
 объектов без явной инициализации.

      Объекты классов с конструкторами могут быть  инициализированы
 при  помощи  задаваемых в круглых скобках списков инициализаторов.
 Этот список  используется  как  список  передаваемых  конструктору
 аргументов.   Альтернативным   способом   инициализации   является
 использование  знака  равенства,  за  которым  следует   отдельное
 значение.   Это   отдельное   значение  может  иметь  тип  первого
 аргумента,  принимаемого  конструктором  данного  класса;  в  этом
 случае  дополнительных  аргументов  либо  не  существует, либо они
 имеют  значения  по  умолчанию.  Значение  может  также   являться
 объектом данного типа класса. В первом случае для создания объекта
 вызывается   соответствующий   конструктор.   В  последнем  случае
 вызывается  конструктор   копирования,   инициализирующий   данный
 объект.

    class X
    {
       int i;
    public:
       X();         // тела функций для ясности опущены
       X(int x);
       X(const X&);
    };

    main()
    {
       X one;        // запуск конструктора по умолчанию
       X two(1);     // используется конструктор X::X(int)
       X three = 1;  // вызывает X::X(int)
       X four = one; // запускает X::X(const X&) для копирования
       X five(two);  // вызывает X::X(cont X&)

      Конструктор  может  присваивать  значения  своим  компонентам
 следующим  образом.  Он  может  принимать  значения   в   качестве
 параметров   и   выполнять   присваивание  компонентам  переменным
 собственно в теле функции конструктора:

    class X
    {
       int a, b;
    public:
       X(int i, int j) { a = i; b = j }
    };

      Либо он может использовать находящийся до тела функции список
 инициализаторов:

    class X
    {
       int a, b;
    public:
       X(int i, int j) : a(i), b(j) {}
    };

      В обоих случаях инициализация X x(1, 2) присваивает  значение
 1  x::a  и  значение  2  x::b.  Второй  способ,  а  именно  список
 инициализаторов,   обеспечивает   механизм    передачи    значений
 конструкторам базового класса.

      Конструкторы   базового   класса   должны  быть  объявлены  с
 атрибутами public  или  protected,  для  того,  чтобы  обеспечить
 возможность их вызова из производного класса.

    class base1
    {
       int x;
    public:
       base1(int i) { x = i; }
    };

    class base2
    {
       int x;
    public:
       base2(int i) : x(i) {}
    };

    class top : public base1, public base2
    {
       int a, b;
    public:
       top(int i, int j) : base(i*5), base2(j+i), a(i) { b = j;}
    };

      В  случае  такой  иерархии  класса  объявление  top one(1, 2)
 приведет к инициализации base1 значением 5, а base2  значением  3.
 Методы инициализации могут комбинироваться друг с другом.

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

    class X
    {
       int a, b;
    public:
       X(int i, j) : a(i), b(a+j) {}
    };

      В пределах класса объявление X x(1,1) приведет к присваиванию
 числа 1 x::a и числа 2 x::b.

      Конструкторы     базовых     классов     вызываются     перед
 конструированием  любых  компонентов производных классов. Значения
 производного класса не могут изменяться и затем влиять на создание
 базового класса.

    class base
    {
       int x;
    public:
       base(int i) : x(i) {}
    };

    class derived : base
    {
       int a;
    public:
       derived(int i) : a(i*10), base(a) {}  // Обратите внимание!
                     // base будет передано неинициализированное a
    }

      В данном  примере  вызов  производного  d(1)  не  приведет  к
 присвоению  компоненту  базового  класса  х значения 10. Значение,
 переданное конструктору базового класса, будет неопределенным.

      Если вы хотите иметь список инициализаторов  в  не-встроенном
 конструкторе,  не  помещайте  этот  список  в  определении класса.
 Вместо этого поместите его в точку определения функции:

    derived::derived(int i) : a(i)
    {
    ...
    }
                                  Деструкторы

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

    class X
    {
    public:
       -X();  // деструктор класса X
    };

      Если  деструктор  не  объявлен  для  класса  явно, компилятор
 генерирует его автоматически.

 Запуск деструкторов

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

      Когда   указатели  объектов  выходят  за  пределы  контекста,
 неявный вызов деструктора  не  происходит.  Это  значит,  что  для
 разрушения такого объекта операция delete должна быть задана явно.

      Деструкторы  вызываются строго в обратной последовательности
 относительно   последовательности   вызова   соответствующих    им
 конструкторов (см. стр.117 оригинала).

 atexit, #pragma exit и деструкторы

      Все глобальные объекты остаются активными до тех пор, пока не
 будут   выполнены   коды  во  всех  процедурах  выхода.  Локальные
 переменные, включая те, что  объявлены  в  main,  разрушаются  при
 выходе  их  из  контекста.  Последовательность  выполнения в конце
 программы Turbo C++ в этом смысле следующая:

 - выполняются функции atexit в последовательности их вставки в
   программу

 - выполняются функции #pragma exit в соответствии с кодами их при-
   оритетов

 - вызываются деструкторы глобальных переменных.

 exit и деструкторы

      При вызове  exit  из  программы  деструкторы  для  каких-либо
 локальных переменных в текущем контексте не вызываются. Глобальные
 переменные разрушаются в обычной последовательности.

 abort и деструкторы

      При   вызове  abort  где-либо  из  программы  деструкторы  не
 вызываются, даже для переменных глобального контекста.

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

    class X {
    ...
       -X();
    ...
    };

    void* operator new(size_t size, void *ptr)
    {
       return ptr;
    }
    char buffer[sizeof(x)];

    main()
    {
       X* pointer = new X;
       X* exact_pointer;

       exect_pointer = new(&buffer) X;  // указатель инициализиру-
                                        // ется адресом буфера
    ...
       delete pointer;                  // delete служит для раз-
                                        // рушения указателя
       exact_pointer->X::-X();          // прямой вызов для отмены
                                        // распределения памяти
    }

 Виртуальные деструкторы

      Деструктор может быть объявлен  как  virtual.  Это  позволяет
 указателю  объекта базового класса вызывать необходимый деструктор
 в  случае,  когда  указатель  фактически   ссылается   на   объект
 производного  класса.  Деструктор класса, производного от класса с
 виртуальным деструктором, сам является виртуальным.


 Внимание:       пример проограммы см в файле prog_1.app



      Однако,  если  ни  один  из  деструкторов  не  был   объявлен
 виртуальным,   delete   palette[0],  delete  palette[1]  и  delete
 palette[2]  вызывают  только  деструктор  для  класса  color.  Это
 приведет к неправильному разрушению первых двух элементов, которые
 фактически имели тип red и brightred.
Next
Используются технологии uCoz