







                           TURBO C++

                          Version 1.0

                       Programmer's Guide





. 
                           - 2 -



   Оглавление

 Введение...................................................
  Содержание данного руководства............................
 Глава 1     Стандарт языка Turbo C++...................
  Синтаксис и терминология..................................
  Грамматические правила структуры фраз и лексики языка.....
  Пробельные символы........................................
    "Склеивание" строк символом \...........................
  Комментарии...............................................
    Комментарии C...........................................
    Вложенные комментарии...................................
    Комментарии C++.........................................
    Разделители комментариев и пробелы......................
  Лексемы...................................................
    Ключевые слова..........................................
    Идентификаторы..........................................
    Константы...............................................
    Константы и их внутреннее представление.................
  Описание операций.........................................
    Унарные операции  ......................................
    Бинарные операции.......................................
    Пунктуаторы   ..........................................
  Объявления................................................
    Объекты   ..............................................
    Именующие выражения (Lvalues)   ........................
    Типы и классы памяти   .................................
    Контекст   .............................................
    Видимость...............................................
    Продолжительность.......................................
    Единицы трансляции .....................................
    Компоновка .............................................
  Синтаксис объявления......................................
    Предварительные определения   ..........................
    Возможные объявления   .................................
    Внешние объявления и определения   .....................
    Спецификаторы типа .....................................
    Таксономия типа   ......................................
    Фундаментальные типы   .................................
    Инициализация...........................................
    Простые объявления......................................
    Спецификаторы класса памяти.............................
    Модификаторы............................................
    Сложные объявления и деклараторы........................
  Указатели.................................................
    Указатели объектов......................................
    Указатели функций.......................................
    Объявления указателей   ................................
    Указатели и константы   ................................
    Арифметические операции с указателями   ................
    Преобразования указателей...............................
    Объявления ссылок в С++   ..............................
  Массивы   ................................................
  Функции...................................................
    Объявления и определения   .............................
    Объявления и прототипы   ...............................
    Объявления .............................................
    Объявления формальных параметров   .....................
    Вызовы функций и преобразования аргументов .............
  Структуры.................................................
    Структуры без тегов и определения типов (typedef).......
    Объявления компонентов структуры   .....................
    Структуры и функции   ..................................
    Доступ к компоненту структуры   ........................
 
                           - 3 -
    Выравнивание по границе слова   ........................
    Пространство имен структур .............................
    Неполные объявления   ..................................
    Битовые поля   .........................................
  Объединения...............................................
    Объявления объединений  ................................
  Перечислимые данные.......................................
  Выражения.................................................
    Выражения и Turbo C++   ................................
    Последовательность вычислений   ........................
    Ошибки и переполнения   ................................
  Семантика операций........................................
    Постфиксные и префиксные операции.......................
    Операции инкремента и декремента   .....................
    Унарные операции   .....................................
    Операция sizeof    .....................................
    Операции типа умножения   ..............................
    Операции типа сложения   ...............................
    Операции поразрядного сдвига   .........................
    Операции отношения .....................................
    Операции типа равенства   ..............................
    Операция поразрядного И  & .............................
    Операция поразрядного исключающего ИЛИ  ^...............
    Операция поразрядного включающего ИЛИ  \!...............
    Операция логического И  &&..............................
    Операция логического ИЛИ  \!\!..........................
    Условная операция ?:....................................
    Операции присвоения   ..................................
    Операция с запятой......................................
  Операторы.................................................
    Блоки   ................................................
    Операторы-с-метками   ..................................
    Операторы-выражения   ..................................
    Операторы выбора   .....................................
    Операторы итерации .....................................
    Операторы перехода .....................................
  С++.......................................................
    Ссылки   ...............................................
    Операция доступа к контексту   .........................
    Операции new и delete   ................................
    Классы   ...............................................
    Виртуальные базовые классы .............................
    "Друзья" классов (friend)...............................
    Конструкторы и деструкторы .............................
    Конструкторы   .........................................
    Деструкторы   ..........................................
    Перегруженные операции   ...............................
    Операции-функции  ......................................
    Виртуальные функции.....................................
    Абстрактные классы .....................................
    Контекст С++   .........................................
  Директивы препроцессора Turbo C++.........................
    Пустая директива # .....................................
    Директивы #define и #undef .............................
    Включение файлов директивой #include   .................
    Условная компиляция   ..................................
    Директива управления нумерацией строк #line   ..........
    Директива #error   .....................................
    Директива #pragma.......................................
    Предопределенные макросы   .............................
 Глава 2.  Перекрестные ссылки по библиотеке исполняющей
  системы...................................................
  Зачем нужен доступ к исходным кодам библиотеки исполняющей
   системы..................................................
  Файлы заголовка Turbo C++.................................
  Категории библиотечных подпрограмм........................
 Глава 3    Потоки С++..................................
 
                           - 4 -
  Новые потоки вместо старых................................
  Использование потоков 2.0.................................
    Что такое поток?  ......................................
    Библиотека iostream ....................................
    Четыре стандартных потока  .............................
    Вывод   ................................................
    Ввод....................................................
    Инициализация потоков  .................................
    Простой ввод/вывод в файл  .............................
    Состояния ошибки потока ввода/вывода....................
  Использование потоков прошлых версий......................
  Рекомендации по переходу к потокам версии 2.0.............
 Глава 4      Модели памяти, операции с плавающей точкой и
  оверлеи...................................................
  Модели памяти.............................................
    Регистры 8086...........................................
    Сегментация памяти......................................
    Указатели...............................................
    Шесть моделей памяти....................................
  Программирование с использованием различных моделей памяти
    адресные модификаторы...................................
    Объявление ближних или дальних функций  ................
    Объявление указателей near, far или huge................
    Использование библиотечных файлов.......................
    Компоновка смешанных  модулей ..........................
  Опции типа чисел с плавающей точкой.......................
    Эмулирование платы 80х87................................
    Получение кода только для машин с 80х87  ...............
    Получение кода без операций с плавающей точкой  ........
    Опция быстрых вычислений с плавающей точкой  ...........
    Переменная операционной среды 87........................
    Регистры и 80х87  ......................................
  Математические операции с комплексными числами............
    Использование двоично-десятичной (BCD) математики.......
  Использование оперативной памяти Turbo C++................
  Оверлеи (VROOMM)..........................................
    Работа программ с оверлеями.............................
    Требования..............................................
    Использование оверлеев .................................
    Разработка программ с перекрытиями......................
    Свопинг  ...............................................
    Дополнительная память (EMS).............................
    Расширенная память (Ext)................................
 Глава 5    Видео функции.......................
  Несколько слов о видео режимах............................
  Несколько слов о текстовых и графических окнах............
    Что такое окно ?   .....................................
    Что такое графическое окно ?  ..........................
    Координаты..............................................
  Программирование в текстовом режиме.......................
    Функции консольного ввода/вывода .......................
    Текстовые окна..........................................
    Тип text_mode...........................................
    Цвета текста  ..........................................
    Высокоскоростной вывод: переменная directvideo .........
  Программирование в графическом режиме.....................
    Функции библиотеки graphics.............................
    Управление цветом на CGA................................
 Глава 6....................................................
  Смешанное программирование................................
    Последовательности передачи параметров   ...............
  Подготовка к вызову .ASM из Turbo C++.....................
    Упрощенные сегментные директивы  .......................
    Стандартные сегментные директивы   .....................
    Определение данных - констант и переменных .............
    Определение глобальных и внешних идентификаторов   .....
  Подготовка к вызову Turbo C++ из .ASM.....................
 
                           - 5 -
    Ссылки к функциям.......................................
    Ссылки к данным   ......................................
  Определение подпрограмм на языке ассемблера...............
    Передача параметров.....................................
    Обработка значений возврата ............................
  Соглашения о регистрах....................................
  Вызов функций С из модулей .ASM...........................
  Псевдопеременные, встраиваемые ассемблерные коды и функции
   прерывания...............................................
    Псевдопеременные   .....................................
    Встраиваемые ассемблерные коды   .......................
    Функции прерывания......................................
    Практические примеры программ низкого уровня   .........
 Глава 7      Сообщения об ошибках......................
  Сообщения об ошибках времени выполнения...................
  Сообщения об ошибках компилятора..........................
  Фатальные ошибки..........................................
  Ошибки....................................................
  Предупреждения............................................
 Приложение АСтандарты ANSI, зависящие от
  реализации................................................
. 
                           - 6 -


   Введение

     Документ "Начало работы" содержит обзор всего комплекта
документации к 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: "Сообщения об ошибках" перечисляет и объясняет
все  фатальные  ошибки,  ошибки и предупреждения исполняющей
 
                           - 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&TC++
версии 2.00,  объектно-ориентированного надмножества C, раз-
работанного  Бьерном  Строструпом из AT&T Bell Laboratories.
Помимо того,что C++ дополняет C многими новыми  средствами и
возможностями,  он  также  и  отличен  от него в большей или
меньшей степени.  В настоящей главе содержатся замечания  по
этим отличиям. Все средства языка Turbo C++,имеющие отклоне-
ния от C++, более подробно рассматриваются, начиная со стра-
ницы 98.

   Синтаксис и терминология

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

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

    восьмеричная цифра: одно из
 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;

 и

 
                           - 9 -
    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++
 
                           - 10 -

    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;

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

Лексемы

     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
 -----------------------------------------------------------




 
                           - 12 -

 Ключевые слова, специфичные для 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  \!
 
                           - 13 -
Case-Sensitive Link,либо использовав опцию  командной строки
/C при запуске TLINK.  В таком режиме глобальные имена Sum и
sum рассматриваются как идентичные,  и при компоновке  может
быть выдано предупреждение "Duplicate symbol" ("Повторяющие-
ся символические имена").

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

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

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

   Константы

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

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

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

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

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

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

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

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

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

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

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

 восьмеричная-константа:
    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).  При отсутствии переопре-
деляющих суффиксов типданныхцелочисленной константы выводит-
 
                           - 15 -
ся из ее значения,  как показано в таблице 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),  то  она  будет  иметь  тип  данных
 
                           - 16 -
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, и вызовет генерирование ошибки. Первое
же не-восьмеричноеили не-шестнадцатиричное значение,  встре-
ченное  в восьмеричнойили шестнадцатиричной управляющей пос-
 
                           - 17 -
ледовательности, означает конец данной последовательности.

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

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

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

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

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

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

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

 Управляющие последовательности Turbo C++       Таблица 1.7
 -----------------------------------------------------------
Последовательность  Значение  СимволыВыполняемая функция
 -----------------------------------------------------------
    \a      0x07BELЗвуковой сигнал
    \b      0x08BSЗабой
    \f      0x0CFFПеревод бланка
    \n      0x0ALFНовая строка (перевод строки)
    \r      0x0DCRВозврат каретки
    \t      0x09HTТабуляция (горизонтальная)
    \v      0x0BVTВертикальная табуляция
    \\      0x5c\Обратная наклонная черта


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

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

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

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


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

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

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

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

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

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

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

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

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

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

    "Имя "\  Адрес

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

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

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

    #include <stdio.h>

    main()
    (*
       char    *p;

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

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

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

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

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

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

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

 char8     -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)
       ----------------------
 
                           - 22 -
      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++  предлагают  дополнительные операции для
 
                           - 23 -
доступа к компонентам класса и их объектам,  атакже механизм
перегрузки  операций.  Перегрузка  позволяет  переопределять
действие любых стандартных операций применительно к объектам
заданного класса. В данном разделе мы ограничимся рассмотре-
нием стандартных операций TurboC++.  Перегрузка рассматрива-
ется, начиная со стр.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++)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  Пунктуаторы

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

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

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

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

    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);

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

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

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

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

     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,но также могут иметь и любое число
дополнительных аргументов.

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

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

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

    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 оригинала.

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

Объявления

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

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

    Объекты

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

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

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

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

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

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

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

 
                           - 29 -
 Именующие выражения (Lvalues)

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

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

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

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

     Типы и классы памяти

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

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

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

   Контекст

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

   Видимость

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

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

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

    ...
    (*
     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.
 
                           - 32 -

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

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

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

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

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

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

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

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

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

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

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

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

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

  Компоновка

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

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

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

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

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

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

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

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

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

     1. Любой идентификатор,  объявленный иначе,  чем объект
 
                           - 34 -
или функция (например, идентификатор 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;   /* недопустимо, т.к. оба определения
      инициализированы */
     Возможные объявления

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

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

     Полный синтаксис  объявлений показан в следующих табли-
 
                           - 35 -
цах.  Рекурсивная природа  синтаксиса  описателей  позволяет
создавать  сложные  описатели.  Для  улучшения читаемости мы
пошли на использование определяемого типа (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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 имя-функции-операции:
    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 (с некоторыми предосторожностя-
ми), можно объявлять производные типы следующим образом:






 
                           - 40 -






 Объявление типов      Таблица 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 - это модификаторы, применяемые к ин-
тегральным типам.

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

    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 означает положительное число,
 
                           - 42 -
1 - отрицательное (что объясняет  диапазоне,  приведенные  в
таблице 1.9). В версиях unsigned дляхранения числа использу-
ются все биты, что дает диапазон 0-(2^n-1), где n = 8,16 или
32.

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

     Представления и  множества принимаемых значений для ти-
пов с плавающей точкой зависят от конкретной  реализации; то
есть  каждая новая реализация компилятора С свободна опреде-
лять их по-своему.  TurboC++ использует форматы с  плавающей
точкой 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.

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



 
                           - 43 -


 Методы стандартных арифметических преобразований Таблица 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, если что указатель

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    int days[7] = (* 1, 1, 1, 1, 1, 1, 1)

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

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

       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
 
                           - 46 -

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

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

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

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

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

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

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

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

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

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

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

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

    static long int biggy;
    typedef long int BIGGY;

     Первое объявление  создает  32-битовый объект типа long
int,  со статической продолжительностью существования и име-
нем  biggy.  Второе  объявление  устанавливает идентификатор
BIGGY в качестве спецификаторановоготипа,  не  создавая  при
 
                           - 47 -
этомкакого-либо объекта времени выполнения.  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        Переменные   отменяет учет регистра идентифи-
 
                           - 48 -
   катора и ведущие знаки подчерки-
   вания

 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 не может быть присвоен
 
                           - 49 -
указателю на не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++  позволяет  вашим  программам  легко вызывать
 
                           - 50 -
подпрограммы,  написанные на других языках,  и  наоборот.При
смешанном  программировании  приходится  иметь  дело с двумя
важными вопросами: идентификаторы и передача параметров.

     В Turbo C++ все глобальные идентификаторы сохраняются в
своем исходном виде (строчные,  заглавныебуквы и их комбина-
ции) с присоединенным в начале идентификатора  знакомподчер-
кивания     (_),    если    вы    не    выберете    опцию-u-
(GenerateUnderbars...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 и позво-
ляет вызывать такую функцию как правильную функцию С. Напри-
мер,   если   вы    компилируете    предыдущую    программус
 
                           - 51 -
установленной опцией -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) команды вызова.
 
                           - 52 -

     В случае моделей памяти 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
 
                           - 53 -
      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,  они имеют свои собственные правила  и
ограничения на присвоения, преобразования ивыполнение с ними
арифметических действий. Примеры в следующих нескольких раз-
делах иллюстрируют эти правила и ограничения.


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

 
                           - 54 -
     "Указатель на объект типа 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;/* Опасно - неинициализированный указатель */

 
                           - 55 -
     объявит 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;      // Присвоение константы-указателя-на
      // константу указателю-на-константу
 
                           - 56 -

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

    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)) байт вперед,  пока  указа-
 
                           - 57 -
тель  остается  в  допустимых  границах (не далее первого за
концом массива элемента).

     Вычитание междудвумя указателями на элементы  одного  и
того  же  массива дает интегральное значение типа 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];

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

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

Функции

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

   Объявления и определения

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

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

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

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

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

    Объявления и прототипы

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

    <type> func()

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

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

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

     При помощи IDE или опции компилятора  командной  строки
можно  разрешить выдачу следующего предупреждения: "Function
called without 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 содержит макросы, которые можно использовать в
функциях,  определяемых  пользователем,  с переменным числом
 
                           - 60 -
параметров.

     Прототип функции обычно объявляет  функцию, принимающую
фиксированное число параметров.  Для функции С,  принимающей
переменное  число  параметров  (например,  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-оператор

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

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

 
                           - 61 -
     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.

  Вызовы функций и преобразования аргументов
 
                           - 62 -

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

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

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

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

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

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

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

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

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


   Структуры

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

     Структура -  это производный тип данных,  обычно предс-
тавляющий собой определяемый пользователем набор именованных
компонентов. Эти компоненты могут быть любого типа, как фун-
даментального, так и производного (с некоторыми описываемыми
 
                           - 63 -
далее ограничениями), и располагаться в любой последователь-
ности. Кроме того, компонент структуры может иметь тип бито-
вого поля, более нигде не разрешаемого. Тип струтуры в 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;// недопустимо

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

     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 выполняется через два
одновременно задаваемых селектора компонента структуры:






 
                           - 65 -



    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++ заполняет структуры байтами таким образом, что структура
была выравнена по следующим правилам:

 
                           - 66 -
     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 *);

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

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

     Целочисленные компоненты типа 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.

 Примечание

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

     Выражение &mystruct.x недопустимо,  так как x это иден-
тификатор битового поля,  а никакой гарантии, что mystruct.x
имеет адрес на границе байта, нет.

  Объединения

     Объединения соответствуют типам вариантных записей язы-
ков Pascal и Modula-2.

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

    union myunion (*   /* тег объединения = myunion  */
       int i;
       double d;
       char ch;
    *) mu, *muptr=&mu;

     идентификатор 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. Объединения могут содержать битовые поля,  но актив-
ным бывает только одно изних.  Все они начинаются  в  начале
объединения.

 
                           - 69 -
     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 не требуются,  тег может быть
опущен:

 
                           - 70 -
    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  показано,  каким образом комбинируются
идентификаторы и операции для составления грамматически вер-
ных "фраз".

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

 Выражения Turbo C++      Таблица 1.19
 -----------------------------------------------------------
 первичное-выражение:
    литерал
    псевдо-переменная
    (выражение)
    this (только С++)
    :: идентификатор (только С++)
    :: имя-функции-операции (только С++)
    имя

 литерал:
    целочисленная-константа
    символьная-константа
    константа-с-плавающей-точкой
    строка

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

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

 постфиксное-выражение:
    первичное-выражение
    постфиксное-выражение[выражение]
    постфиксное-выражение (<список-выражений>)
     постфиксное-выражение (<список-выражений>) (только С++)
постфиксное-выражение .  имя  постфиксное-выражение  ->  имя
постфиксное-выражение ++ постфиксное-выражение --

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

 унарное-выражение:
    постфиксное-выражение
    ++ унарное-выражение
    -- унарное-выражение
    унарная-операция  выражение-приведения
    sizeof  унарное-выражение
    sizeof  (имя-типа)
    выражение-распределения (только С++)
    выражение-отмены-распределения (только С++)

 унарная-операция: одно из
    & * + - тильда !

 выражение-распределения: (только С++)
     <::> new  <местоположение> имя-ограниченного-типа <ини-
циализатор
    <::> new <местоположение> имя-типа <инициализатор>
 
                           - 72 -

 местоположение: (только С++)
    (список-выражений)

 имя-ограниченного-типа: (только С++)
    спецификатор-типа <декларатор-ограничения>

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

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

 выражение-приведения:
    унарное-выражение
    (имя-типа) выражение-приведения

 выражение-типа-ссылки:
    выражение-приведения
     выражение-типа-ссылки .* выражение-приведения (только С
++) выражение-типа-ссылки -> выражение-приведения (только С+
+)

 выражение-типа-умножения:
    выражение-типа-ссылки
    выражение-типа-умножения * выражение-типа-ссылки
    выражение-типа-умножения / выражение-типа-ссылки
    выражение-типа-умножения % выражение-типа-ссылки

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

 выражение-типа-сдвига:
    выражение-типа-сложения
    выражение-типа-сдвига <<  выражение-типа-сложения
    выражение-типа-сдвига >>  выражение-типа-сложения

 выражение-отношения:
    выражение-типа-сдвига
    выражение-отношения < выражение-типа-сдвига
    выражение-отношения > выражение-типа-сдвига
    выражение-отношения <= выражение-типа-сдвига
    выражение-отношения >= выражение-типа-сдвига

 выражение-типа-равенства:
    выражение-отношения
    выражение-типа-равенства = выражение-отношения
    выражение-типа-равенства != выражение-отношения

 выражение-И:
    выражение-типа-равенства
    выражение-И & выражение-типа-равенства

 выражение-исключающее-ИЛИ:
    выражение-И
    выражение-исключающее-ИЛИ выражение-логическое-И

 выражение-включающее-ИЛИ:
    выражение-исключающее-ИЛИ
    выражение-включающее-ИЛИ  \!  выражение-исключающее-ИЛИ

 выражение-логическое-И:
    выражение-включающее-ИЛИ
 
                           - 73 -
    выражение-логическое-И && выражение-включающее-ИЛИ

 выражение-логическое-ИЛИ:
    выражение-логическое-И
    выражение-логическое-ИЛИ !! выражение-логическое-И

 условное-выражение:
    выражение-логическое-ИЛИ
    выражение-логическое-ИЛИ ? выражение : условное-выражение

 выражение-присвоения:
    условное-выражение
  унарное-выражение операция-присвоения выражение-присвоения

 операция-присвоения: одно из
    =  *= /=%=     +=     -=
    <<=   ??= &=^=     \!=

 выражение:
    выражение-присвоения
    выражение, выражение-присвоения

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

     Стандартные преобразования  подробно рассматриваются на
стр.42 оригинала, в таблице 1.15.

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

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

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













 
                           - 74 -





 Ассоциативность и приоритеты операций Turbo C++ Tаблица 1.20
 ------------------------------------------------------------
    Операции Ассоциативность
 ------------------------------------------------------------
    () [] -> :: . Слева-направо
    ! тильда - ++ -- & * Справа-налево
    sizeof new delete .* ->* / %    Слева-направо
    + -     Слева-направо
    << >>    Слева-направо
    < <= > >=    Слева-направо
    &    Слева-направо
    ^    Слева-направо
    \!    Слева-направо
    &&    Слева-направо
    \!\!    Слева-направо
    ?:условное выражение    Справа-налево
    = += /= %= += -=    Справа-налево
    &= ^= \!= ,    Слева-направо
 ------------------------------------------------------------

     Приоритеты обозначаютсяпоследовательностью расположения
в  данной  таблице.  Первый  элемент таблицы имеет наивысший
приоритет.

Выражения и Turbo C++

     С++ позволяет перегрузку некоторых стандартных операций
С,  как описано начиная со стр.125 оригинала.  Перегруженной
называется такая операция, которая применительно к выражени-
ям  типа  класса  ведет  себя некоторым специальным образом.
Например, оператор отношения == может быть определен в клас-
се  complex  для  проверки  равенства двух комплексныхчисел,
причем действие его для типов данных других классов остается
прежним. Перегруженный оператор реализуется как функция; эта
функция  определяет  тип   операнда,   именующее   выражение
(lvalue)  и  последовательность вычислений,  устанавливаемая
при использовании перегруженного оператора.  Однако,  перег-
рузка не может изменять приоритеты операций. Аналогичным об-
разом,  С++ позволяет выполнять  определяемые  пользователем
преобразования между объектами класса и фундаментальными ти-
пами.  Учтите, что некоторые правила относительно операций и
преобразований,  обсуждаемые в данном разделе, неприменимы к
выражениям в С++.

 Последовательность вычислений

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

    i = v[i++];  // i неопределено

     Значение i зависит от того,  выполняется ли инкременти-
рование до или после присвоения. Аналогичным образом,

    int total = 0;
 
                           - 75 -
    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++ может встретить
многие  проблематичные ситуации,  как то деление на ноль или
получение значений с плавающей точкой,  выходящих за пределы
допустимого  диапазона.  Переполнение целочисленных значений
игнорируется (С использует арифметические действия по модулю
2  в n-разрядных регистрах),  однако ошибки,  обнаруживаемые
математическими библиотечными функциями, могут обрабатывать-
ся  стандартными или определяемыми пользователем подпрограм-
мами. См. matherr и signal в Справочнике по Библиотеке.

                           - 76 -
Семантика операций

     Описанные здесь  операции Turbo C++ являются операциями
стандарта ANSI C.

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

     Если операция перегружена, то приводимые здесь сведени-
ядля  нее  недействительны.Таблица  1.19 на стр.74 оригинала
приводит синтаксис для всех операций и выражений с операция-
ми.
       Постфиксные и префиксные операции

     Шесть постфиксных операций [] () .  -> ++и -- использу-
ются для построения постфиксных выражений, показанных в таб-
лице   синтаксиса   выражений   (таблица   1.19).   Операции
инкремента и декремента (++ и --) также являются префиксными
и унарными операциями;они  обсуждаются,  начиная  со  стр.79
оригинала.

 Операция индексации массива []   --------------------------

      В выражении

    постфиксное-выражение [выражение]

     в С,  но не обязательно в С++, выражение выраж1[выраж2]
определяется как

    *((выраж1) + (выраж2))

     где либо выраж1 это указатель, а выраж2 это целочислен-
ное значение,  либо выраж1 это это целочисленное значение, а
выраж1 это указатель.  (Каждый из пунктуаторов [], * и + мо-
жет быть перегружен в С++).

 Операция вызова функции ()

      Выражение

    постфиксное-выражение(<список-аргументов-выражения>)

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

 Операция задания компонента структуры/объединения . (точка)

   В выражении

    постфиксное-выражение . идетификатор

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

      Именующие выражения определяются на стр.28 оригинала.

 Операция указателя структуры/объединения ->

      В выражении

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

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

 Операция постфиксного инкремента ++

      В выражении

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

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

 Постфиксная операция декремента --

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

       Операции инкремента и декремента

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

 Операция префиксного инкремента ++

      В выражении

    ++ унарное-выражение

 операндом является унарное выражение:
     оно должно быть скалярного  типа  (арифметического  или
типа  указателя)  и должно являться модифицируемым именующим
выражением.  Операцию префиксного инкремента также  называют
операцией  преинкремента.  Операнд  инкрементируется на 1 до
вычисления выражения; значением всего выражения является ин-
крементированное значение операнда.  Величина инкремента за-
 
                           - 78 -
висит от типа операнда.  Значения типа указателя вычисляются
по правилам арифметических действий с указателями.

 Префиксная операция декремента --

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

    --унарное-выражение

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

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

      Существует шесть унарных операций (кроме ++ и --):
 .  *  +  -  тильда и  !.  Их синтаксис:

    унарная-операция  выражение-приведения

    выражение-приведения:
       унарное-выражение
       (имя-типа) выражение-приведения

 Операция адресации &

     Символ & также используется в С++ для задания ссылочных
типов данных. См. стр.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.

 Операция обращения по ссылке *

      В выражении
 
                           - 79 -

    * выражение-приведения

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

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

 Унарная операция плюс +

      В выражении

    +выражение-приведения

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

 Унврная операция минус -

      В выражении

    -выражение-приведения

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

 Операция поразрядного дополнения (тильда)

      В выражении

    <тильда>выражение-приведения

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

 Операция логического отрицания !

      В выражении

    !выражение-приведения

     операнд выражение-приведения должен быть скалярного ти-
па.  Результат иммет тип int и представляет собой логическое
отрицание операнда:  0 при ненулевом операнде и 1  в  случае
нулевого операнда. Выражение !E эквивалентно выражению (0 ==
Е).
Операция sizeof

     Существует два  раздельных способа использования опера-
ции sizeof:

    sizeof унарное-выражение
    sizeof (имя-типа)
 
                           - 80 -

     Размер выделяемой  для  каждого  типа памяти зависит от
конкретной машины.

     В обоихсучаяхрезультат представляет собой целочисленную
константу, выражающую размер в байтах областипамяти, занима-
емой операндом (определяемый за некоторыми исключениями  ти-
пом операнда).  В первом случае тип выражения операнда опре-
деляется  без  расчета  выражения  (и   следовательно,   без
побочных эффектов).  Если операнд имеет тип 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.
 
                           - 81 -

      Округление всегда выполняется к нулю.
    Операции типа сложения

     Существует две операции типа сложения: + и -. Синтаксис
этих операций следующий:

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

 Операция сложения +

     Допустимыми являются следующие типы операндов выражения
операнд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 должны иметь тип
 
                           - 82 -
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 (истина); в против-
 
                           - 83 -
ном случае результат равен 0 (ложь).

     В случаях 2 и 3,  где Е1 и Е2 являются указателями сов-
местимых типов, результат операции E1 <E2 зависит от относи-
тельного   расположения   (адресов)   двух  указываемых  ими
объектов. При сравнении компонентов одной и той же структуры
"старший"  указатель обозначает более позднее объявление.  В
массивах "старший" указатель обозначает большее значение ин-
декса массива.  Сравнение указателей компонентов одного объ-
единения дает равенство.

     Обычно сравнение указателей разных  структур,  массивов
или  объединений  ,  либо сравнение указателей вне диапазона
объекта типа массив дает неопределенные  результаты;  однако
исключение  делается для указателя "за последним элементом",
как указывается в разделе "Арифметические действия с  указа-
телями"  на  стр.57  оригинала.  Если Р указывает на элемент
массива,  а Q указывает на его последний элемент, то выраже-
ние H<Q+1 допустимо и дает 1 (истина),  хотя Q+1 и не указы-
вает на какой-либо элемент этого массива.

 Операция больше чем  >

     Выражение E1 > E2 дает 1  (истина),  если  значение  Е1
больше  значения  Е2;в  противном  случае  результат равен 0
(ложь),  причем используются  те  же  способы  интерпретации
арифметических сравнений и сравнений указателей, что опреде-
лены для операции "больше чем".  К операндам применимы те же
правила и ограничения.

 Операция меньше или равно <=

     Аналогичным образом,  выражение  E1 <= E2 дает 1 (исти-
на),  если значение Е1 меньше или равно значению Е2.  В про-
тивномслучаерезультат равен 0 (ложь), причем используются те
же способы интерпретации арифметических сравнений и  сравне-
ний указателей,  что определены для операции "меньше чем". К
операндам применимы те же правила и ограничения.

 Операция больше или равно >=

     И наконец,  выражение E1 >= E2 дает  1  (истина),  если
значение Е1 больше или равно значению Е2. В противном случае
результат равен 0 (ложь),  причем используются те же способы
интерпретации  арифметических сравнений и сравнений указате-
лей,  что определены для операции "меньше чем".  К операндам
применимы те же правила и ограничения.
    Операции типа равенства

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

    выражение-типа-равенства:
       выражение-отношения
       выражение-типа-равенства  ==  выражение-отношения
       выражение-типа-равенства  !=  выражение-отношения

 Операция проверки равенства ==

     В выражении  E1  ==  E2  операнды  должны удовлетворять
 
                           - 84 -
одному из следующего набора условий:

     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.









 
                           - 85 -



 Таблица истинности для поразрядных операций    Таблица 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 не вычисляется вообще.

Операция логического ИЛИ  \!\!

       Синтаксис этой операции следующий:

     выражение-логическое-ИЛИ:
     выражение-логическое-И
     выражение-логическое-ИЛИ \!\! выражение-логическое-И

 
                           - 86 -
     В выражении 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.

      Операции присвоения

     Существует одиннадцать   операций   присвоения.   самым
простым  из  них  является операция =;  остальные называются
составными операциями присвоения.

 
                           - 87 -
      Синтаксис операций присвоения следующий:

    выражение-присвоения:
       условное-выражение
     унарное-выражение операция присвоения выражение-присво-
ения

    операция-присвоения: одно из
       =     *=    /= %=    +=    -=
       <<=   >>=   &= ^=    \!=

 Простая операция присвоения =

     В выражении  Е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.

     Правила для составных операций присвоения, следователь-
но,  такие  же,  как  и  описанные в предыдущем разделе (для
простой операции присвоения =).

      Операция с запятой

      Синтаксис этой операции следующий:
 
                           - 88 -

    выражение:
       выражение-присвоения
       выражение , выражение-присвоения

      В выражении с запятой

    Е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 : оператор

 составной-оператор:
    (* <список-объявления> <список-операторов> *)

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

 список-операторов:
 
                           - 89 -
    <выражение>;

 оператор-выбора:
    if (выражение) оператор
    if (выражение) оператор else оператор
    switch (выражение) выражение

 выражение-итерации:
    while (выражение) оператор
    do оператор while (выражение);
     for (оператор-нач-условия   <выражение>   ;    <выраже-
ние>)оператор

 оператор-нач-условия:
    оператор-выражение
    объявление (только С++)

 оператор-перехода:
    goto идентификатор;
    continue ;
    break ;
    return <выражение>;
 -----------------------------------------------------------

Блоки

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

Операторы-с-метками

      Оператору можно присвоить метку следующим образом:

 1. идентификатор-метки : оператор

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

 2. case выражение-типа-константы : оператор
    default : оператор

     Операторы с метками case и default  используются только
в сочетании с операторами выбора.

Операторы-выражения

     Любое выражение, за которым следует двоеточие, образует
оператор-выражение:

      <выражение>

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

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

       Операторы выбора

     Операторы выбора,  или операторы управления потоком вы-
полнения программы,  выполняют выбор одной из альтернативных
ветвей программы,  проверяя для этого определенные значения.
Сущесвует два типа операторов выбора: if...else и switch.

 Операторы if

      Базовый оператор if имеет следующий шаблон:

     if(условное-выражение) оператор-если-"истина"    <else>
оператор-если-"ложь"

     Заключение условного-выражения в круглые скобки являет-
ся важным моментом синтаксиса этого оператора.

     Условное-выражение должно быть скалярного типа. Это вы-
ражение вычисляется. Если оно является нулевым (или пустым в
случае типа указателя),  мы говорим,  что условное-выражение
ложно; в противном случае оно истинно.

     Если предложение 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)
 
                           - 91 -
    (*
       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 (условное-выражение) оператор-пока-"истина"

     Оператор тела цикла, оператор-пока-"истина", будет цик-
лически  повторяться  до тех пор,  пока вычисление условного
выражения не даст значения ноль (ложь).

     Условное выражение  вычисляется  и  проверяется  первым
 
                           - 92 -
(как описано на стр.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. Если выражение-проверки ненулевое (ис-
 
                           - 93 -
тина),  то оператор тела цикла выполняется. Пустое выражение
трактуется в данном случае как 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
 
                           - 94 -

      Синтаксис следующий:

    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, "Краткий
справочник по С++" в том же документе поможет вам быстро по-
нять, как именно работают конструкции языка.

 
                           - 95 -
     Хотя С++ вводит новые ключевые слова и операции для ра-
боты  с классами,  некоторые средства С++ применимы вне кон-
текста  классов.   Сначала   мы   рассмотрим   тиенно   эти,
используемые незвисимо от классов средства, а затем займемся
спецификой работы с классами и связанными с ними  механизма-
ми.
    Ссылки

     Установка ссылок при помощи указателей и  обращение  по
ссылкам рассматриваются на стр.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 непосредственно по значению ведет к
бесполезным затратам памяти на копирование  такой  структуры
 
                           - 96 -
данных.

      Сравним три различных реализации функции 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 остается без измене-
ний

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

Операция доступа к контексту

     Операция доступа к контексту (или разрешения контекста)
::  (два  двоеточия  подряд) позволяет осуществлять доступ к
глобальному (или с продолжительностью файла)  имени  даже  в
том  случае,  когда это имя скрыто локальным переобъявлением
 
                           - 97 -
этого имени (см.стр.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, будучи ключевым словом, не нуждается в прототипе.


 
                           - 98 -

 Операция 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,
"Доступ к базовым и производным  классам").  Базовый  список
содержит  спецификаторы  доступа по умолчания и опциональные
 
                           - 99 -
их переопределения,  которые могут модифицировать права дос-
тупа  производного  класса к компонентам базовых классов(см.
стр.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); // так можно: это вызов функции
 
                           - 100 -
    *)

      С++ позволяет также неполное объявление класса:

    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 передается  как
скрытый аргумент при вызове не-статических функций-компонен-
тов.

 
                           - 101 -
     Ключевоеслово 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 это указатель объекта клас-
 
                           - 102 -
са 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;

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

     - уменьшения числа видимых глобальных имен

     - того, чтобы сделать очевидным, какие именно статичес-
кие объекты какому классу принадлежат

     - разрешения управления доступам к их именам.

 Контекст компонента

     Выражение 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.

     Компоненты класса не могут быть добавлены к нему в дру-
гом разделе вашейпрограммы.  Класс Х не может содержать объ-
екты класса Х,  но может содержать указатели или  ссылки  на
объекты  класса Х (отметим аналогию с типами структур и объ-
единений С).

 Управление доступом к компонентам
 
                           - 104 -

     Компоненты класса получают  атрибуты  доступа  либо  по
умолчанию (взависимости от ключакласса и местоположения объ-
явления), либо при использовании какого-либо из спецификато-
ров доступа:  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
сразу, и т.д.

 
                           - 105 -
 Доступ к базовым и производным классам

     При объявлении производного класса  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 является производным от класса А */
 
                           - 106 -

     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 (* ... *):  // недопустимо

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

 
                           - 107 -
    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 друзьями класса
Х в одном объявлении:






 
                           - 108 -




    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. Вы не можете работать с их адресами:




 
                           - 109 -




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

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


 
                           - 110 -


    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), который мо-
жет либо принимать один аргумент, либо не принимать аргумен-
тов.

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







 
                           - 111 -




    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;
     *)

 Порядок вызова конструкторов

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

      Например, в следующем примере

 
                           - 112 -
    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();

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

      Конструкторы  элементовмассива   запускаютсяв   порядке
 возрастания индексов массива.

 Инициализация класса

     Объект классас компонентами только 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 *)
    *);

     Либо он может использовать находящийся до  тела функции
список инициализаторов:






 
                           - 114 -





    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.

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

    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 оригинала).

 
                           - 116 -
 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.  Это позво-
ляет указателю объекта базового класса  вызывать необходимый
деструктор в случае,  когда указатель фактически ссылаетсяна
объект производного класса.Деструктор  класса,  производного
 
                           - 117 -
от класса с виртуальным деструктором, сам является виртуаль-
ным.


 Внимание: пример проограммы см в файле prog_1.app



     Однако, если  ни  один  из деструкторов не был объявлен
виртуальным,delete palette[0],  delete palette[1]  и  delete
palette[2]  вызывают  только  деструктор  дляклассаcolor.Это
приведет к неправильному разрушению первых  двух  элементов,
которые фактически имели тип red и brightred.

    Перегруженные операции

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

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

    ..*   ::   ?:

     Также невозможна  перегрузка символов препроцессора # и
##.

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

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

    class complex (*
       double real, imag; // по умолчанию private
    public:
       ...
     complex() (* real = imag = 0;  *) //  встроенный  конс-
труктор
       complex(double r, double i = 0) (* // еще один
    real = r; imag = i;
       *)
       ...
    *)

     Данный класс был создан только для  примера.  Онотличен
от класса complex из библиотеки исполняющей системы.

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

    complex AddComplex(complex c1, complex c2);

 однако будет более естественным иметь возможность записать:

    complex c1(0,1), c2(1,0), c3
    c3 = c1 + c2;
 
                           - 118 -

 вместо

    c3 = AddComplex(c1, c2);

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

    friend complex operator +(complex c1, complex c2*);

     и сделав его определение (возможно, встроенное) следую-
щим образом:

    complex operator +(complex c1, complex c2)
    (*
       return complex(c1.real + c2.real, c1.imag + c2.imag);
    *)
       Операции-функции

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

    c3 = c1.operator + (c2);   // то же, что c3 = c1 + c2

     В отличие от new и delete,  которые имеют своисобствен-
ные правила (см.  следующий раздел), операция-функция должна
быть  либо  не-статической функцией-компонентом,  либо иметь
как минимум один аргумент типа класса.  Операций-функции  =,
(),  [] и -> должны являться нестатическими функциями-компо-
нентами.

 Перегруженные операции и наследование

     За исключением  операции-функции  присвоения  =()  (см.
раздел  "Перегрузка операции присвоения =" на стр.127 ориги-
нала) все функции операций для класса X наследуются классом,
производным  от X,  согласно стандартным правилам разрешения
для перегруженных функций.  Если Х является базовым  классом
для Y, перегруженная функция операции для Х может далее быть
перегружена для Y.

 Перегрузка new и delete

     Операцииnew и delete могут быть перегружены таким обра-
зом, чтобыдаватьальтернативныеварианты подпрограммы управле-
ния свободной памятью  (кучей).  Определяемая  пользователем
операция new должна возвращать void* и иметь в качестве пер-
вого аргумента size_t.  Определяемая пользователем  операция
delete должна иметь тип void и первый аргумент void*; второй
аргумент, типа size_t, является опциональным.

      Тип size_t определяется в stdlib.h.

      Например,

    #include <stdlib.h>

    class X (*
       ...
    public:
     void* operator     new(size_t     size)    (*    return
newalloc(size);*)
       void operator delete(void* p*) (*newfree(p); *)
       X() (* /* здесь инициализация */
       X(char ch) (* /* здесь тоже */ *)

 
                           - 119 -



-X() (* /* очистка */ *)
       ...
    *);

     Аргумент size задает  размер  создаваемого  объекта,  а
newalloc  и  newfree  это определяемые пользователем функции
распределения и отмены распределения  памяти.  Вызовы  конс-
труктора  и деструктора для объектов класса Х (или объектов,
производных от Х,  для которых не существует собственных пе-
регруженных  операций new и delete) приведет к запуску соот-
ветствующихопределяемых пользователем  X::operator  new()  и
X::operator delete(), соответственно.

     Операции-функции X::operator  new  иX::operator  delete
являются статическими компонентами Х, как при явном объявле-
нии  их  static,  так и без него,  поэтому они не могут быть
виртуальными функциями.

     Стандартные, предопределенные (глобальные) операции new
и  delete могут при этом также использоваться в контексте Х,
как явно с операциямиглобального контекста (::operator new и
::operator delete),  так и неявно, при создании и разрушении
объектов классов,  отличных от класса Х и не являющихся про-
изводными  от класса Х.  Например,  можно использовать стан-
дартныеnew иdeleteпри определении перегруженных версий:

    void* X::operator new(size_t)
    (*
       void* ptr = new char[s];  // вызов стандартной new
       ...
       return ptr;
    *)
    void X::operator delete(void* ptr)
    (*
       ...
       delete (void*) ptr; // вызов стандартной delete
    *)

     Причинойтого, что размер аргумента  определяется  size,
является   то,что   классы,   производные  от  Х,  наследуют
X::operator new.  Размер объектапроизводного  классаможетсу-
щественно отличаться от размера, определяемого базовым клас-
сом.

 Перегрузка унарных операций

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

     Следует быть внимательным при перегрузке ++ и --,  пос-
кольку постфиксное и префиксное использование не  может быть
определено в перегруженной функции. Например,




 
                           - 120 -







    class X (*
       ...
     X operator ++() (* /* здесь подпрограмма инкремента X *
/ *)
    *)
    ...
    X x, y;
    y = ++x; // то же, что и  y = x++ !

 Перегрузка бинарных операций

     Перегрузка бинарной  операции  выполняется  при  помощи
объявления  не-статической  функции компонента,  принимающей
один аргумент, либо при помощи объявления не являющейся ком-
понентом функции (обычно friend), принимающей два аргумента.
Если @ представляет собой бинарную операцию, тоx@y можно ин-
терпретировать   либо   как   x   operator@(y),   либо   как
operator@(x,y), в зависимости от выполненных объявлений. Ес-
ли объявлены обе формы,  то разрешение неоднозначности зави-
ситот переданныхпри вызове операции  стандартных аргументов.

 Перегрузка операции присвоения =

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

 Внимание :    пример программы смотрите в файле  struct.app


     Данный код,        совместно       с       объявлениями
String::operator=(),  позволяет выполнять строковые присвое-
ния str1 = str2, как это делается в прочих языках программи-
рования. Вотличие от прочих функций операций, функция опера-
ции присвоения не может наследоваться производными классами.
Если для какого-либо класса  Х  не  существует  определяемой
операции =,  то операция = определяется по умолчанию как по-
компонентное присвоение компонентов класса Х:

    X& X::operator = (const X& source)
    (*
       // покомпонентное присвоение
    *)

      Перегрузка операции вызова функции ()

      Вызов функции

    первичное-выражение(<список-выражений>)

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

 Перегрузка операции индексирования []
 
                           - 121 -

      Аналогичным образом, операция индексирования

    первичное-выражение [выражение]

     рассматривается как двоичнаяоперация с операндами  пер-
вичное-выражение и выражение. Соответствующая функция опера-
ции это operator[];  она может быть определена пользователем
для  класса  Х (и любых производных классов) только посредс-
твом не-статической функции-компонента.  Выражение x[y], где
x это объект класса Х,  интерпретируется в данном случае как
x.operator[](y).

 Перегрузка операции доступа к компоненту класса ->

      Доступ к компоненту класса при помощи

    первичное-выражение->выражение

     рассматривается как унарнаяоперация. Функция operator->
должна  рассматриваться как не-статическая функция-компонен-
та.  Выражение x->m, где x это объект класса (*, интерпрети-
руетсякак (x.operator->())->m, таким образом, что operator->
() должен либо возвращать указательна объект данного класса,
либо  возвращать  объект  класса,  для которого определяется
operator->.
       Виртуальные функции   -------------------------------

     Виртуальные функциипозволяют  производным классам обес-
печивать разные версии  функциибазового  класса.  Вы  можете
объявить  виртуальную функцию в базовом классе и затем пере-
определить ее влюбом производном классе,  даже если число  и
тип  ее  аргументов остается без изменений.  Вы также можете
объявить функции int  Base::Fun  (int)  и  int  Derived::Fun
(int),  даже если они не являются виртуальными. Версия базо-
вого класса доступна объектам производного класса через  пе-
реопределение контекста.  Если они являются виртуальными, то
доступна только функция,  связанная с фактическим типом объ-
екта.

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

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

     Если базовый класс В содержит виртуальную функцию vf, и
класс D,  являющийся производным от класса B, содержит функ-
цию vf того же типа,то если функция vf вызывается дляобъекта
d или D, выполняется вызов D::vf, даже если доступ определен
через указатель или ссылку на B. Например,

    struct B (*
       virtual void vf1();
       virtual void vf2();
       virtual void vf3();
       void f();
    *)
    class D : public B (*
     virtual void vf(); // спецификатор virtual допустим, но
 
                           - 122 -
  // избыточен
       void vf2(int);  // не virtual, поскольку здесь
  // используется другой список аргументов
       char f();  // так нельзя: изменяется только тип
  // возврата
       void f();
    *);

    void extf()
    (*
       D d;  // объявление объекта D
       B* bp = &d;  // стандартное преобразование из D* в B*
       bp->vf1();  // вызов D::vf1
       bp->vf2();  // вызов B::vf2, так как vf2 из D имеет
  // отличные аргументы
       bp->f();   // вызов B::f (не виртуальной)
    *)

     Переопределяющаяфункция vf1 в D автоматически становит-
ся виртуальной.  Спецификатор virtual может быть использован
в определении переопределяющейфункции в  производном классе,
но на самом деле он является в данном случае избыточным.

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

 Примечание:

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

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

    class B (*
    virtual void vf(int) = 0;// = 0 означает "чистую" функцию

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

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

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

      Абстрактные классы

     Глава 5  документа "Начало работы" приводит пример абс-
трактного класса в действии.

     Абстрактным называется класс с как минимум одной чистой
 
                           - 123 -
виртуальной функцией. Виртуальная функция задается как "чис-
тая" при помощи соответствующего спецификатора.

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

    class shape  (*    // абстрактный класс
       point center;
       ...
    public:
       where() (* return center; *)
       move(point p) (* center = p; draw(); *)
virtual void rotate(int) = 0;   // чистая виртуальная функция
virtual void draw() = 0;        // чистая виртуальная функция
virtual void hilite() = 0;      // чистая виртуальная функция
       ...
    *)

     shape x;  // ошибка: попытка создания объекта абстракт-
ного
     // класса
     shape* sptr; // указатель на абстрактный класс допустим
     shape f();  //  ошибка:  абстрактный класс не может яв-
ляться
     // типом возврата
     int q(shape s);  // ошибка:  абстрактный класс не может
являться
     // типом аргумента функции
     shape& h(shape&);//  ссылка  на абстрактный класс в ка-
честве типа
     // возврата или аргумента функции допустим

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

     Например, с   использованием  показанного  выше  класса
shape:

     class circle :  public shape (* // circle является про-
изводным // от абстрактного класса
       int radius;    // private

    public:
       void rotate(int) (* *)    // определяется виртуальная
    // функция:
    // действия по вращению окруж-
    // ности отсутствуют
       void draw();    // circle::draw должна быть
    // где-либо определена
       void hilite() = 0;    // переопределение функции
    // как "чистой"

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

 Контекст С++
 
                           - 124 -

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

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

     Имя М  компонента  класса  Х имеет контекст класса "ло-
кальный по отношению Х";  оно может использоваться в следую-
щих ситуациях:

     - в функциях-компонентах Х

     - в выражениях типа x.M, где x есть объект Х

     - в  выражениях  типа xptr->M,  где xptr есть указатель
объекта Х

     - в выражениях типа X::M или D::M, где D есть производ-
ный класс от X

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

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

 Скрытые имена

     Имя может  бытьскрытоявным  объявлением того же имени в
объемлющем блоке или в классе.  Скрытый компонент класса тем
не менее остается доступным при помощи модификатора контекс-
та,  заданного с именем класса X:M. Скрытое имя с контекстом
файла (глобальное) позволяет ссылку на него при помощи унар-
ной операции ::, например ::g. Имя класса Х может быть скры-
то  именем объекта,  функции,  либо нумератора,  заданного в
контексте Х,  независимо  от  последовательности  объявления
имен.  Однако, имя скрытого класса Х доступно прииспользова-
нии префикса Х  с  соответствующим  ключевым  словом  class,
struct или union.

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

 Краткое изложение правил определения контекста С++

     Следующие правила  применимы  ко  всем именам,  включая
имена typedef и имена классов,  при условии, что то или ино-
еимя допустимо в С++ в конкретном обсуждаемом контексте:

     1. Сначала  само имя проверяется на наличие неоднознач-
ностей. Если в пределах контекста неоднозначности отсутству-
ют, то инициируется последовательность доступа.

     2. Если  ошибок  управления  доступа не обнаружено,  то
проверяется тип объекта, функции, класса, typedef, и т.д.

     3. Если имя используется  вне  какой-либо  функции  или
класса, либо имеет префикс унарной операции контекста досту-
па ::,  и если имя  не  квалифицировано  бинарной  операцией
 
                           - 125 -
::или операциямивыборакомпонента . или ->, то это имя должно
быть именем глобального объекта, функции или нумератора.

     4. Если имя n появляется в одном из  видов:  X::n,  x.n
(где  x  это  объект класса Х или ссылка на Х),  либо ptr->n
(где ptrэто указатель на Х),то n является  именем компонента
Хили компонентом класса, производным от которого является Х.

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

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

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

     8. Инициализатор конструктора (см. "инициализатор-конс-
труктора" в описании синтаксиса декларатора класса в таблице
1.12 на стр.37 оригинала)  вычисляется  в  контексте  самого
внешнего  блока конструктора,  и поэтому разрешает ссылки на
имена аргументов конструктора.

       Директивы препроцессора Turbo C++

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

     CPP позволяет обращение к документации по нему в диало-
говом режиме.

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

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

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

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

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

     - Установка условной компиляции для  улучшения  мобиль-
ности получаемых кодов и для целей отладки.

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

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

     Полный синтаксис препроцессора Turbo C++ показан в сле-
дующей таблице:

 Синтаксис директив препроцессора Turbo C++     Таблица 1.23
 -----------------------------------------------------------
 файл-для-препроцессора:
    группа

 группа:
    часть группы
    группа часть-группы

 часть-группы:
    <лексемы-препроцессора> новая-строка
    if-раздел
    управляющая строка

 if-раздел:
    if-группа <elif-группы> <else-группа> endif-строка

 if-группа:
    #if  выражение-типа-константы  новая-строка  <группа>
    #ifdef идентификатор новая-строка <группа>
    #ifndef идентификатор новая-строка <группа>

 elif-группы:
    elif-группа
    elif-группы  elif-группа

 elif-группа:
    #elif  выражение-типа-константы  <группа>

 else-группа:
 
                           - 127 -
    #else  новая-строка <группа>

 endif-строка:
    #endif новая-строка

 управляющая-строка:
     #include лексемы-препроцессора новая-строка
     #define идентификатор список-замены новая-строка
     #define идентификатор              левая-круглая-скобка
<список-идентификаторов>) список-замены новая-строка
    #undef    идентификатор  новая-строка
    #line     <лексемы-препроцессора> новая-строка
    #pragma   <лексемы-препроцессора> новая-строка
    #pragma   warn действие сокращение новая-строка
    #pragma   inline  новая-строка
    ?      новая-строка

 действие: одно из
    + - .

 сокращение:
    amb   ampapt   aus   big   cincpt
    def   dupelf   mod   par   piapro
    rch   retrng   rpt   rvf   sigstr
    stu   stvsus   ucp   use   volzst

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

 список-замены:
    <лексемы-препроцессора>

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

 имя-заголовка:
    <последовательность-символов-заголовка>

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

 символ-заголовка:
     любой символ из исходного множества символов, за исклю-
чением  символа  новой-строки  (\n) или символа "больше чем"
(>).

 новая-строка:
    символ новой строки
 -----------------------------------------------------------

Пустая директива #

     Пустая директива состоитиз строки,  вкоторой содержится
единственныйсимвол  #.  Эта  директива  всегда  игнорируется
препроцессором.

  Директивы #define и #undef

 
                           - 128 -
     Директива #define определяет макрос.  Макросы обеспечи-
вают  механизм  замены лексемы набором формальных,  подобных
используемых в функциях параметров, либо пустой замены.

 Простые макросы #define

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

     #define идентификатор_макроса  <последовательность_лек-
сем>

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

     Любые вхожденияидентификаторамакроса,   обнаруженное  в
литеральных строках,  символьных константах или комментариях
расширению не подлежат.

     Пустая последовательность  лексем  позволяетэффективное
удаление всех найденных идентификаторов макросов из исходно-
го кода:

    #define HI "Добрый день!"
    #define empty
    #define NIL ""
    ...
    puts(HI);   /* расширяется в: puts("Добрый день!"); */
    puts(NIL);   /* расширяется в: puts(""); */
    puts("empty"); /* расширения empty не происходит ! */
     /* расширение empty не будет выполнено и в  комментари-
ях! */

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

    #define GETSTD #include <stdio.h>
    ...
    GETSTD   /* ошибка компиляции */

     GETSTD будет   расширен  в  #include<stdio.h>.  Однако,
препроцессор не станет сам обрабатывать эту вполне  допусти-
мую в других условиях директиву,  а передаст ее в таком виде
компилятору.  Компилятор воспримет#include <stdio.h> как не-
допустимый  ввод.  Макрос  не  может  быть расширен во время
собственногорасширения.  Поэтому выражения типа #define A  A
недопустимы вследствие неопределенности результата.

 Директива #undef

     Можно отменить определение макроса при помощи директивы
#undef:

    #undef идентификатор_макроса

     Данная строка удаляетлюбую ранее  введенную  последова-
тельность  лексем из идентификатора макроса;определение мак-
роса теряется,  и идентификатор его становится  неопределен-
 
                           - 129 -
ным.

      Макрорасширения внутри строк #undef не выполняются.

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

     После того,     как    идентификатор    макроса    стал
неопределенным,  он  может  бытьдалеепереопределендирективой
#define, с использованием той же самой или другой последова-
тельности лексем.

    #define BLOCK_SIZE 512
    ...
    buff = BLOCK_SIZE*blks;   /* расширяется в:  512*blks */
    ...
    #undef BLOCK_SIZE
    /* использование BLOCK_SIZE теперь невозможно - это
       "неизвестный" препроцессору идентификатор */
    ...
    #define BLOCK_SIZE 128 /*переопределение размера блока*/
    ...
    buf = BLOCK_SIZE*blks;    /* расширяется в:  128*blks */
    ...

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

    #ifndef BLOCK_SIZE
       #define BLOCK_SIZE 512
    #endif

     Если идентификатор  BLOCK_SIZE в текущий момент опреде-
лен,  то средняя строка не обрабатывается препроцессором;  в
противном  же случае выполняется определение средней строки.

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

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

     #define BLOCK_SIZE = 512 /*  почему  последовательность
лексем включает символ = */

 Опции -D и -U

     Определение и отмена определения идентификаторов выпол-
няется также при помощи опций компилятора командной строки -
D  и  -U (см.  Главу 4,"Компилятор командной строки" в Руко-
водстве пользователя). Идентификаторы могут быть определены,
но не могут бытьявно отменены, при помощи меню интегрирован-
 
                           - 130 -
ной среды разработки Options \!  Compiler  \!  Defines  (см.
Главу 1,"Справочник по интегрированнойсредеразработки", так-
же в Руководстве пользователя.)

      Командная строка

    tcc -Ddebug=1; paradox=0; X -Umysym myprog.c

 эквивалентна помещению в программу строк:

    #define debug 1
    #define paradox 0
    #define X
    #undef mysym

 Ключевые слова и защищенные слова

     Допустимо, но  не рекомендуется,  использовать ключевые
слова Turbo C++ в качестве идентификаторов макросов:

     #define int long /* допустимо,  но может привести к ка-
тастрофическим последствиям */
    #define INT long /* допустимо и, вероятно, полезно */

     Следующие предопределенные глобальные идентификаторы не
могут   появляться  непосредственно  следом  за  директивами
#defineили #undef:

    __STDC__      __DATE__
    __FILE__      __TIME__
    __LINE__

     Отметим наличие в этих именах ведущих и хвостовых двой-
ных символов подчеркивания.

 Макросы с параметрами

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

     #define идентификатор_макроса(<список-аргументов>) пос-
ледовательность-лексем

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

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

      Вызов таких макросов выполняется записью

     идентификатор-макроса<пробельный-символ>(<список-факти-
ческихаргументов>)

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

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

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

    #define CUBE(x)  ((x)*(x)*(x))
    ...
    int n,y
    n = CUBE(y):

 дает в результате следующую замену:

    n = ((y)*(y)*(y));

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

    #define SUM ((a) + (b))
    ...
    int i,j,sum;
    sum = SUM(i,j);

     при расширении даст sum = ((i)+(j)). Причина кажущегося
избытка круглых скобок станет  очевидной,  если  рассмотреть
пример:

    n = CUBE(y+1);

     Без внутренней пары круглых скобок в определении расши-
рение даст запись: n=y+1*y+1*y+1, что при лексическом анали-
зе равно:

     n = y + (1*y) + (1*y) + 1; // если y не равен 0 или -3,
то в // куб возводится (y+1) !

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

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

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

    #define ERRMSG(x, str) showerr("Error",x,str)
    #define  SUM(x,y) ((x) + (y))
    ...
    ERRMSG(2, "Press Enter, then Esc");
     // расширится в:  showerr("Error",2,"Press Enter,  then
Esc"); */ return SUM(f(i,j), g(k.l));
    // расширится в: return ((f(i,j)) + (g(k,l))); */

     2. Склеивание  лексем  при  помощи ##:  можно выполнить
 
                           - 132 -
склеивание (слияние) двух лексем,  разделив их символами  ##
(и  плюс опциональными пробельными символами с каждой сторо-
ны).  Препроцессор удаляет пробельные символы и##, объединя-
ядве отдельные лексемыв одну новуюлексему. Это средство мож-
но    использовать    для    конструированияидентификаторов;
например, выполнив определение

    #define VAR(i,j) (i##j)

     и затем  вызвав  VAR(x,6),  можно  получить  расширение
(x6).  Этот метод заменяет старый (не обеспечивающий мобиль-
ность кода) метод использования (i/**/j).

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

    #define TRACE(flag) printf(#flag "=%d\n",flag)

    фрагмент кода

    int highval = 1024;
    TRACE(highval);

    станет равным

    int highval = 1024;
    printf("highval" "= %d\n", highval);

    что в свою очередь будет рассматриваться как

    int highval = 1024;
    printf("highval=%d\n", highval);

     4 Символ обратной наклонной черты для продолжения стро-
ки:     длинная     последовательность     лексем      может
продлитьстрокупри помощи обратной наклонной черты (\).  Сим-
волы обратной наклонной черты и новой строки оба вырезаются,
и  в  результате  образуется  фактическая последовательность
лексем, используемая в расширении:

    #define WARN "фактически это одно\
    строчное сообщение"
    ...
    puts(WARN);
     /* на экране будет: фактически это однострочное сообще-
ние */

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

    int cube(int x) (*
       return x*x*x;
    *)
    #define CUBE(x) ((x)*(x)*(x))
    ...
    int b =0, a = 3;
 
                           - 133 -
    b = cube(a++);
     /* cube() передается фактический аргумент =  3; поэтому
b = 27, и теперь a = 4 */
    a = 3;
    b = CUBE(a++);
    /* расширяется в:  ((a++)*(a++)*(a++)); и теперь a = 6 */

     Итоговое значение b зависит от того, что компилятор де-
лает с расширенным выражением.

     Включение файлов директивой #include

     Директива #include подключает к исходному коду заданные
в ней файлы,  известные как включаемые файлы, файлы заголов-
ковили заголовки.  Синтаксис этой директивы имеет три формы:

    #include <имя_заголовка>
    #include "имя_заголовка"
    #include идентификатор_макроса

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

     Третий вариант записи предполагает, что ни символ <, ни
символ " не являются первым не-пробельным символом,  следую-
щим за #include;  кроме того, предполагается, что существует
такое макроопределение,  которое расширит идентификатор мак-
роса в допустимое, следующее за разделителем имя заголовка в
формате либо <имя_заголовка>, либо "имя_заголовка".

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

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

     Если поместить в имя_заголовка полное имя  пути доступа
к файлу,  то поиск файла будет выполнентольков указанной та-
ким образом директории.

     Различиемежду форматами <имя_заголовка> и "имя_заголов-
ка" заключается в алгоритме поиска включаемого файла, приме-
няемом в каждом случае;  эти алгоритмы описаны  в  следующих
двух разделах.

 Поиск файла заголовка при формате <имя_заголовка>

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

 Поиск файла заголовка при формате "имя_заголовка"

     Вариант "имя_заголовка" задает включаемый файл,  созда-
 
                           - 134 -
ваемый  пользователем;  сначала  выполняется  его  поиск   в
текущей директории (обычно в той директории, в которой нахо-
дится исходный компилируемый файл). Если там файл не найден,
то поиск продолжается во всех включаемых директориях,  как в
случае формата <имя_заголовка>.

      Приводимые ниже примеры поясняют описанные различия:

    #include <stdio.h>
    /* заголовок из стандартной включаемой директории */

    #define  myinclude"c:\tc\include\mystuff.h"
    /* Примечание: здесь допустимы одинарные символы обратной
       наклонной черты; в операторе С пишется:
       "c:\\tc\\include\\mystuff.h"  */

    #include myinclude
    /* макрорасширение */

    #include "myinclude.h"
    /* макрорасширение отсутствует */

     После расширения второй оператор#include заставит преп-
роцессор  искать  нужный  файл в C:\TC\INCLUDE\mystuff.h,  и
нигде более.  Третий пример #includeзаставляет  препроцессор
выполнить  поискmyinclude.h сначала в текущей директории,  а
затем во включаемых директориях.

      Условная компиляция

     Turbo C++ поддерживаетусловную компиляцию  путем замены
соответствующих строк исходного кода пустой строкой. Игнори-
руемые таким образом строки это те строки,  что начинаются с
символа  #  (за исключением директив #if,  #ifdef,  #ifndef,
#else,  #elif и #endif), а также любые строки, которые в ре-
зультате  выполнения директив не должны компилироваться. Все
директивы условной компиляции должны завершаться  в  том  же
исходном или включаемом файле, где находится их начало.

 Директивы условной компиляции #if, #elif, #else и #endif

     Директивы условной   компиляции  #if,  #elif,  #else  и
#endif работают аналогично обыкновенным  условным операторам
С.Они используются следующим образом:

    #if выражение-1-типа-константы
    <раздел-1>
    <#elif выражение-2-типа-константы новая-строка раздел-2>
       ...
    <#elif выражение-n-типа-константы новая-строка раздел-n>

    <#else последний-раздел>
    #endif
    ...

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

     В случае "истина" после обработки раздела-1  препроцес-
 
                           - 135 -
сором  управление передается соответствующейдирективе #endif
(которая заканчивает даннуюусловную конструкцию) и продолжа-
ется в следующем-разделе. В случае "ложь" управление переда-
ется следующей строке #elif (если она  определена  в  данной
конструкции),где   вычисляетсявыражение-2-типа-константы.  В
случае "истины" обрабатывается раздел-2, после чего управле-
ние  передается соответствующей директиве #endif,  и т.д.,до
тех пор,  пока не будет достигнута последняя директива #else
или #endif.  Опциональная директива #else используется в ка-
честве альтернативного условия в том случае, если все преды-
дущие проверки дали значение "ложь".  Последовательность ус-
ловных директив заканчивается директивой #endif.

     Обработанный препроцессором раздел может в свою очередь
содержать  вложенные  на любую глубину условные предложения,
причем каждой директиве #if должна соответствовать закрываю-
щая директива #endif.

     Итоговым результатом  вышеописанного  сценария является
то, что для дальнейшей обработки передается только один раз-
дел (возможно,  пустой).  Опущенныеразделы служат только для
отслеживания возможных вложенных условных  конструкций,  так
что  каждая директива #if должна обязательно иметь соответс-
твующую ейзавершающую директиву #endif.

     Проверяемые выражения-типа-константы   при   вычислении
должны давать целочисленную константу.

 Операция defined

     Операция defined дает альтернативный, более гибкий спо-
соб проверки того, определены ли комбинации идентификаторов,
или нет. Данная операция допустима только в выражениях #if и
#elif.

     Выражение defined(идентификатор)или defined идентифика-
тор (скобки необязательны) дает 1 (истина), если данное сим-
волическое имя было ранее определено (при помощи директивы #
defined) иэто определение не было впоследствии отменено (при
помощи #undef); в противном случае оно дает 0 (истина). Поэ-
тому директива

    #if defined(mysym)

 это то же, что

    #ifdef mysym

     Преимущество заключается в том,  что можно использовать
defined циклически в сложном выражении,  следующем за дирек-
тивой #if, например

    #if defined(mysym) && !defined(yoursym)

 Условные директивы #ifdef и #ifndef

     Условные директивы  #ifdef  и  #ifndef позволяют прове-
рить,  определен лив текущий момент данный идентификатор, то
есть была ли обработана предыдущаядиректива #define для дан-
ного идентификатора и продолжает ли она действовать в  теку-
щий момент.

 Строка

    #ifdef идентификатор

 имеет точно такой же эффект, что и
 
                           - 136 -

    #if 1

     если идентификатор в текущий момент определен,  и такой
же эффект, что и

    #if 0

 если идентификатор в текущий момент не определен.

     #ifndef, напротив, дает значение "истина", если иденти-
фикатор "не определен", поэтому строка

    #ifndef идентификатор

 имеет точно такой же эффект, что и

    #if 0

     если идентификатор в текущий момент определен,  и такой
же эффект, что и

    #if 1

 если идентификатор в текущий момент не определен.

     Синтаксис следует синтаксису директив #if, #elif, #else
и #endif, описанному в предыдущем разделе.

     Идентификатор, определенный  как  имеющий пустое (NULL)
значение, считается определенным.

  Директива управления нумерацией строк #line

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

    #line целочисленная-константа <"имя-файла">

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

    /* TEMP.C: пример директивы #line  */

    #include <stdio.h>

    #line 4 "junk.c"










 
                           - 137 -






    void main()
    (*
       printf(" in line %d of %s",__LINE__,__FILE__);
    #line 12 "temp.c"
       printf("\n");
       printf(" in line %d of %s",__LINE__,__FILE__);
    #line 8
       printf("\n");
       printf(" in line %d of %s",__LINE__,__FILE__);
    *)

     Включение stdio.h означает, что на выходе препроцессора
будет нечто большее.

     Если запустить TEMP.C через CPP (cpp temp), то на выхо-
де получится файл TEMP.I, который выглядит так:

    temp.c 1:
    c:\borland\tc\cpp\include\stdio.h 1:
    c:\borland\tc\cpp\include\stdio.h 2:
    c:\borland\tc\cpp\include\stdio.h 3:
    ...
    c:\borland\tc\cpp\include\stdio.h 212:
    c:\borland\tc\cpp\include\stdio.h 213:
    temp.c 2:
    temp.c 3:
    junk.c 4: void main()
    junk.c 5: (*
    junk.c 6: printf(" in line %d of %s",6,"junk.c");
    junk.c 7:
    temp.c 12: printf("\n");
    temp.c 13: printf(" in line %d of %s",13,"temp.c");
    temp.c 14:
    temp.c 8: printf("\n");
    temp.c 9: printf(" in line %d of %s",9,"temp.c");
    temp.c 10: *)
    temp.c 11:

     Если вызатем компилируете TEMP.C,  то получится следую-
щий выход:

    in line 6 of junk.c
    in line 13 of temp.c
    in line 9 of temp.c

     Макросы расширяются в аргументах #line,  как в директи-
вах #include.

     Прежде всего назначение директивы #line  заключается  в
использовании ее в утилитах,  имеющих на выходе коды С, а не
в кодах, создаваемых человеком "вручную".

       Директива #error

      Директива #error имеет следующий синтаксис:

    #error сообщение-об-ошибке

      Директива генерирует сообщение:

     Error: имя-файла номер-строки :  Error directive: сооб-
 
                           - 138 -
щение

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

     Например, вы определили #define MYVAL, принимающую зна-
чения 0 или 1.  Затем можновключить в исходный код  условную
директиву, которая будет проверять MYVAL на предмет неверно-
го значения:

    #if (MYVAL != 0 && MYVAL != 1)
    #error MYVAL must be defined to either 0 or 1
    #endif
       Директива #pragma

     Директива #pragma позволяет использовать специфичные для
конкретных реализаций директивы в форме

    #pragma имя-директивы

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

      Turbo C++ поддерживает следующие директивы #pragma:

 - #pragma argsused

 - #pragma exit

 - #pragma inline

 - #pragma option

 - #pragma saveregs

 - #pragma startup

 - #pragma warn


 Директива #pragma argsused

     Директива #pragma argsused допустима только между опре-
делениями  функций  и действует только на следующую функцию.
Она отменяет сообщение уровня предупреждения:

     "Parameter name is never used in  function имя-функции"
("имя  параметра  нигде  не используется в функции имя-функ-
ции")

 Директивы #pragma exit и #pagma startup

     Данные две директивы позволяют программе задать функцию
(функции),  которые должны вызываться либо призагрузке прог-
раммы (перед вызовам main),  либо при  выходе  из  программы
(непосредственно перед выходом из программы через _exit).

      Синтаксис этих директив следующий:
 
                           - 139 -

    #pragma exit имя-функции <приоритет>
    #pragma startup имя-функции <приоритет>

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

    void func(void);

     Опциональный параметр  приоритет  должен являться целым
числом в диапазоне от 64 до 255. Старшим приоритетом являет-
ся  0.  (Приоритеты от 0 до 63 используются библиотеками С и
не должны использоваться пользователем). Функции со старшими
приоритетами  вызываются  первыми  при  загрузке программы и
последними при выходе из нее. Если приоритет не задан, то по
умолчанию он равен 100. Например,

    #include <stdio.h>

    void startFunc(void)
    (*
       printf("Startup function.\n");
    *)

    #pragma startup startFunc 64
    /* приоритет 64 --> вызывается при загрузке первой */

    void exitFunc(void)
    (*
       printf("Wrapping up execution.\n");
    *)

    #pragma exit exitFunc
    /* приоритет по умолчанию равен 100 */

    void main(void)
    (*
       printf(This is main.\n");
    *)

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

 Директива #pragma inline

     Данная директиваэквивалентна  опции компилятора команд-
ной строки -B или соответствующей опции интегрированной сре-
ды  Turbo.  Она сообщаеткомпилятору,  что программа содержит
встроенные ассемблерные коды (см. главу 6, "Интерфейс с язы-
ком ассемблера"). Синтаксис ее следующий:

    #pragma inline

     Эту директиву  лучше всего помещать вверху файла,  пос-
кольку,  встретив директиву #pragma inline, компилятор пере-
запускает  себя  с  опцией  -B.  Фактически можно опустить и
опцию -В,  и директиву #pragma inline,  и компилятор тем  не
менее выполнитперезапуск, когда встретит операторы asm. Наз-
начение опции и директивы состоит в  том,  чтобы  сэкономить
время компиляции.

 Директива #pragma option

 
                           - 140 -
     Директива #pragma  option используетсядля включения оп-
ций компилятора командной строки в код вашей программы. Син-
таксис этой директивы следующий:

    #pragma option [опции...]

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

     Ниже приводятсяопции,которые  не могут находиться в ди-
рективе pragma option:

    -B (компиляция с использованием ассемблерных кодов)

    -c (компиляция без компоновки)

    -dxxx (определение макроса)

    -Dxxx = ccc (определение макроса с текстом)

    -efff (имя .EXE-файла fff)

    -lfff (имя включаемой директории)

    -Lfff (имя директории с библиотекой)

    -lxset (опция компоновщика x)

    -M (создание при компоновке .MAP-файла)

    -o оверлеи

    -Q EMS

    -S (создание на выходе .ASM-файла и остановка)

    -Uxxx (отмена определения макроса)

    -V (virtual)

    -Y (оверлеи)

     Существует два состояния компилятора.  В первом состоя-
нии  в  директиву pragma option можно включить большее коли-
чество опций, нежели во втором. Первое состояние компилятора
называется состоянием лексического анализа,а второе - состо-
янием кодирования.

     Использование имени макроса, начинающегося двумя симво-
лами подчеркивания (которое может являться именем встроенно-
го  макроса),  в  директивах  #if,   #ifdef,   #ifndef   или
#elifизменяет  состояние  компилятора на состояние кодирова-
ния.

     Появление первой реальной лексемы  (первого  объявления
С) также изменяет состояние компилятора на кодирование.

     Другими словами, можноиспользовать директивы #pragma, #
include, #define и некоторые разновидности #if во время сос-
тояния  лексического анализакомпилятора.  Во время этой фазы
вы имеете возможность при помощи #pragma option изменять оп-
ции командной строки.

     В числоопций,которые могут быть заданы в #pragma option
только в состоянии лексического анализа компилятора, входят:

 
                           - 141 -
    -Efff (строка с ассемблерным именем)

    -f* (любая опция плавающей точки, кроме -ff)

    -l# (значащие символы идентификатора)

    -m* (любая опция модели памяти)

    -nddd (выходная директория)

    -offf (имя выходного файла fff)

    -u (использование символов подчеркивания в именах cdecl)

    -z* (опция задания любого имени сегмента)

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

    -1      Управление набором команд

    -2      Управление набором команд

     -a Управление выравниванием. (Отметим, что выравнивание
компонентов  структурыустанавливается  в  точке  определения
структуры, а не далее, при использовании этой структуры объ-
ектами.)

     -ff Управление быстрыми операциями с плавающей точкой

     -G Генерация кода,  оптимизированного по быстродействию

    -k      Стандартное управление стековым фреймом

    -N      Управление контролем стека

    -O      Управление оптимизацией

    -P      Установка по умолчанию соглашений о связях Pascal

    -r и -rd  Управление регистровыми переменными

    -v      Управление отладкой по действиям

    -y      Управление строчной информацией


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

    -A      Управление ключевыми словами

    -C      Управление вложенностью комментариев

    -d      Слияние повторяющихся строк

    -gn       Остановка компиляции после n предупреждений

    -jn       Остановка компиляции после n ошибок

    -K      Тип char устанавливается как unsigned

     -wxxx Предупреждение (то же самое,  что и #pragma warn)

     Любые из  имеющих  два  переключающихся состояния опций
(такие как -a или -K) могут быть  переведены  во  включенное
 
                           - 142 -
или  выключенное  состояние,  как  это  делается в командной
строке. Дополнительно имеется возможность задавать их с точ-
кой (.), что устанавливает такие опции в состояние, задавае-
мое командной строкой.

 Директива #pragma saveregs

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

 Директива #pragma warn

     Данная директива  позволяет  переопределять  конкретные
опции командной строки -wxxx (или управлять  опцией  Display
Warnings в диалоговом поле Options \! Compiler \! Messages).

      Например, если в вашем исходном коде имеются директивы

    #pragma warn +xxx
    #pragma warn -yyy
    #pragma warn .zzz

     то выдача предупреждения xxx будет разрешена (даже если
в меню Options \! Compiler \! Messages она была переведена в
состояние off), предупреждения yyy запрещена, а статус выда-
чи сообщения zzz будет восстановлен в то  состояние, которое
было к моменту начала компиляции файла.

     Полный список  трехбуквенных сокращений и сообщений,  к
которым они относятся,  см. в Главе 4, "Компилятор командной
строки" в Руководстве пользователя.

   Предопределенные макросы

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

 __CDECL__

     Данный макрос специфичен для Turbo C++.  Он сообщает  о
том,   что   флаг   -p   не   использовался   (меню  Cflling
Conventions...C) :  он устанавливается равным  целочисленной
константе 1,  если -pне использовался; в противном случае он
неопределен.

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

    __COMPACT__       __MEDIUM__
    __HUGE__      __SMALL__
    __LARGE__      __TINY__

     Для конкретной  компиляции определенным является только
один из этих макросов;  прочие, по определению, не определе-
ны.  Например,  если  при  компиляциивыбрана  модель  памяти
small,  то макрос __SMALL__ определен, а остальные неопреде-
лены, поэтому директива

    #if defined(__SMALL__)

 даст значение "истина", а
 
                           - 143 -

    #if defined(__HUGE__)

     либо любая  другая из оставшихся) даст значение "ложь".
Фактическое значение любого из этих макросов, когда он опре-
делен, равно 1.

 __cplusplus

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

 __DATE__

     Данный макрос дает дату начала обработки препроцессором
данного исходного файла (в виде строкового литерала).

     Каждое включение __DATE__ в данный файл дает одно  и то
же значение, независимо от того, сколько времени продолжает-
ся  обработка  файла  препроцессором.   Дата   имеет   форму
mmmddyyyy,где mmm это месяц (Jan, Feb и т.д.), dd равно чис-
лу месяца (от 1 до 31,  причем если это число меньше 10,  то
первый символ d равен пробелу), а yyyy это год (1990, 1991 и
т.д.)

 __FILE__

      Данныймакрос  дает  имя текущего   обрабатываемого
     препроцессором исходного файла (в виде строкового лите-
рала).  Когда препроцессор обрабатывает  директиву  #include
или  #line,  либопри завершении обработки включаемого файла,
данный макрос соответствующим образом меняет  свое значение.

 __LINE__

     Данный макрос дает количество обработанных препроцессо-
ром к данному моменту строк текущего исходного файла. Обычно
первая строка исходного файла определяется с номером 1, хотя
на это может повлиять директива #line. Информацию о директи-
ве #line см. на стр. 144 оригинала.

 __MSDOS__

     Данный макрос специфичен для Turbo C++.  Он дает  цело-
численную константу 1 для всех случаев компиляции.

 __OVERLAY__

     Данный макрос специфичен для С++. Он предопределен рав-
ным  1,  еслимодуль  компилируетсяс  опцией   -Y   (включена
поддержка оверлейных структур). Если оверлейные структуры не
поддерживаются, то данный макрос неопределен.

 __PASCAL__

     Данный макрос специфичен для С++.  Он сообщает  о  том,
чтобыл  использован флаг -p.  Макрос установлен равным цело-
численной константе 1, если флаг -p использовался; в против-
ном случае он неопределен.

 __STDC__

     Данный макрос определен равным константе 1, если компи-
ляция выполняется при установленном  флаге  совместимости  с
 
                           - 144 -
ANSI (-Aили менюANSI Keywords Only...On); в противном случае
макрос неопределен.

 __TIME__

     Данный макрос дает время начала обработки  препроцессо-
ром текущего исходного файла (в виде строкового литерала).

     Как и  в  случае  макроса  __DATE__,  каждое  включение
__TIME__ будет содержать одно и то же  значение,  независимо
от  того,  сколько времени продолжалась обработка файла.  Он
имеет формат hh:vv:ss, где hh это часы (от 00 до 23), mm это
минуты (от 00 до 59), а ss это секунды (от 00 до 59).

 __TURBOC__

     Данный макрос специфичендля С++.  Он дает номер текущей
версии TurboC++ ввиде шестнадцатиричной константы. Например,
версия 1.0 будет представлена в виде 0x0100.

     Глава 2.  Перекрестные ссылки по библиотеке исполняющей
системы

     Данная глава содержит  обзор  библиотечных  подпрограмм
Turbo C++ и включаемых файлов.

      В данной главе

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

     - перечисляются и описываются файлы заголовка

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

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

     Подпрограммы Turbo C++содержатся в  библиотечных файлах
(Cx.LIB,   CPx.LIB,  MATHx.LIB  и  GRAPHICS.LIB).  Поскольку
TurboC++ поддерживает шесть различных моделей памяти, каждая
модель,  за исключением модели tiny,  имеет свой собственный
библиотечный файл и файл математических функций,  в  которых
находятся  версии  этих подпрограмм специально для соответс-
твующей модели памяти.  (Модель tiny использует одни файлы с
моделью small).

     В С++ всегда нужно использовать прототипы. Дополнитель-
ную информацию о прототипах функций см.  на стр.60  оригина-
ла).

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

     Библиотечные подпрограммы Turbo C++ объявляются со сво-
ими прототипами в одном или более файлов заголовка.
     Зачем нужен доступ к исходным кодам библиотеки исполня-
ющей системы
 
                           - 145 -

     Библиотека исполняющей системы Turbo C++ содержит свыше
450 функций,  относящихся к широкому диапазону задач: управ-
ление IBM PC на нижнем уровне,  интерфейс с DOS, ввод/вывод,
управление обработкой,  манипуляции со строками  и  памятью,
математические вычисления,  сортировка и поиск,  и т.д.  Су-
ществует несколько веских причин, по которым вам может пона-
добиться доступ к исходным кодам этих функций:

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

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

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

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

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

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

   Файлы заголовка Turbo C++

     Файлы заголовка,  определяемые стандартом ANCI C, соот-
ветственно обозначены на полях.  Файлы заголовка  С++  также
обозначены на полях слева.

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

 alloc.h     Объявляет функции управления памятью (распре-
     деление и отмена распределения памяти и т.д.)

 ANSI C  assert.h    Определяет отладочный макрос assert

 C++ bcd.h     Определяет класс С++ bcd и перегруженные опе-
     рации для класса bcd и математические функции
     для bcd

 bios.h      Объявляет различные функции, используемые при
 
                           - 146 -
     вызове подпрограмм ROM BIOS IBM PC

     C++ complex.h   Объявляет   комплексные  математические
функции С++

 conio.h     Объявляет различные функции, используемые при
     вызове подпрограмм DOS ввода/вывода с консоли

     ANSI C ctype.h Содержит информацию, используемую макро-
сами символьной классификации  и  символьных  преобразований
(такими, как isalpha и toascii)

 dir.h     Содержит структуры, макросы и функции для
     работы с директориями и путями доступа

     dos.h Определяет различные константы и содержит  объяв-
ления,  необходимые  при  вызовах  DOS и специальных вызовах
8086

 ANSI C  errno.h     Определяет мнемонические константы кодов
     ошибок

 fcntl.h     Определяет символические константы, исполь-
     зуемые совместно с библиотечной подпрограммой
     open

     ANSI C float.h Содержит параметры подпрограмм обработки
чисел с плавающей точкой.

 C++ fstream.h   Объявляет классы потоков С++, поддерживающие
     ввод-вывод в файлы

     C++ generic.h  Содержит  макрос  для объявлений родовых
файлов

 graphics.h  Объявляет прототипы графических функций

 io.h     Содержит структуры и объявления подпрограмм
     ввода/вывода низкого уровня

 C++ iomanip.h   Объявляет манипуляторы ввода/вывода потоков
     С++ и содержит макрос для создания параметри-
     зованных манипуляторов

     C++ iostream.h  Объявляет  подпрограммы  (ввода/вывода)
потоков базового (версии 2.0) С++

     ANSI C limits.h Содержит параметры среды программирова-
ния,  информацию об ограничениях времени компиляции, а также
численные диапазоны интегральных типов

     ANSI C locale.h Объявляет функции,  содержащие информа-
цию, специфичную для конкретной страны и языка

     ANSI C  math.h Объявляет прототипы математических функ-
ций; определяет макрос HUGE_VAL и объявляет структуру исклю-
чения, используемую подпрограммой matherr

 mem.h     Объявляет функции манипулирования памятью.
     (Многие из них также определены в string.h)

 process.h   Содержит структуры и объявления для функций
     spawn... и exec...

     ANSI C  setjmp.h  Определяет тип jmp_buf,  используемый
функциями
     longjmp и setjmp, и объявляет подпрограммы
 
                           - 147 -
     longjmp и setjmp

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

     ANSI C  signal.h  Определяет макросы,  используемые для
чтения списков аргументов функций, объявленных как принимаю-
щие переменное число аргументов (например, vprintf, vscanf и
т.д.)

     ANSI C stddef.h Определяет несколько общих типов данных
и макросов

     ANSI C stdio.h Определяет типы данных и макросы,  необ-
ходимые для пакета стандартного ввода/вывода  (Standard  I/O
Package),  определенного Керниганом и Ритчи и расширенного в
Системе UNIX V.
     Определяет предопределенные потоки стандарт-
     ного ввода/вывода stdin, stdout, stdprn и
     stderr, а также объявляет подпрограммы ввода/
     вывода уровня потоков

     C++ stdiostr.h Объявляет классы потоков С++ для исполь-
зования в файловых структурах stdio FILE.

     ANSI C stdlib.h Объявляет некоторые широко используемые
подпрограммы:  подпрограммы преобразования, подпрограммы по-
иска/сортировки и прочие

     C++ stream.h  Объявляет подпрограммы (ввода/вывода) по-
токов С++ (версии 1.2)

     ANSI C string.h Объявляет несколько подпрограмм строко-
вых манипуляций и манипуляций с памятью

 C++ strstrea.h  Объявляет классы потоков С++ для работы
     с байтовыми массивами в памяти

 sys\stat.h  Объявляет символические константы, используе-
     мые при открытии и создания файлов

 sys\timeb.h Объявляет функцию time и структуру timeb,
     возвращаемую time

 sys\types.h Объявляет тип type_t, используемый функциями
     времени

     ANSI C   time.h   Определяет   структуру,   заполняемую
подпрограммами преобразования времени asctime,  localtime  и
gmtime,  а  также  тип,  используемый  подпрограммами ctime,
difftime, gmtime, localtime и stime; также содержит прототи-
пы этих подпрограмм.

     values.h Определяет важные константы, включая машиноза-
висимые константы;  обеспечивает совместимостью  с  системой
UNIX V
      Категории библиотечных подпрограмм
 -----------------------------------------------------------

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

 
                           - 148 -
 Подпрограммы классификации   ------------------------------

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

   isalnum (ctype.h)   isascil (ctype.h)   isdigt  (ctype.h)
   isalpha (ctype.h)   iscntrl (ctype.h)   isgraph (ctype.h)
   islower (ctype.h)   ispunct (ctype.h)   isupper (ctype.h)
   isprint (ctype.h)   isspace (ctype.h)   isxdigit(ctype.h)

 Подпрограммы преобразования   -----------------------------

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

 atof    (stdlib.h)  itoa  (stdlib.h)  _tolower (ctype.h)
 atof    (stdlib.h)  itoa  (stdlib.h)  tolower  (ctype.h)
 atof    (stdlib.h)  strtod  (stdlib.h)  _tolower (ctype.h)
 ocvt    (stdlib.h)  strtol  (stdlib.h)  tolower  (ctype.h)
 fcvt    (stdlib.h)  strtcul (stdlib.h)  ultoa    (stdlib.h)
 gcft    (stdlib.h)  toascil (ctype.h)

 Подпрограммы управления директориями-----------------------

     Эти подпрограммы  манипулируют  директориями  и именами
пути доступа.

  cndir(dir.h)     fnsplit   (dir.h)  mkdir    (dir.h)
  findfirst (dir.h)     getcurdir (dir.h)  mktemp   (dir.h)
  findnext(dir.h)     getcwd    (dir.h)  rmdir    (dir.h)
  fnmerge(dir.h)     detdisk   (dir.h) searchpath(dir.h)
  setdisk  (dir.h)

 Диагностические подпрограммы-------------------------------

     Эти подпрограммы реализуют встроенные  средства  поиска
ошибки.

      assert(assert.h)
      matherr(math.h)
      perror(errno.h)

 Графические подпрограммы   --------------------------------

     Эти подпрограммы позволяют создавать экранные графичес-
кие представления с текстовой частью.

arc  (graphics.h)   fillellipse     (graphics.h)
bar  (graphics.h)   fillpoly     (graphics.h)
bar3d  (graphics.h)   floofill     (graphics.h)
circle  (graphics.h)   getarccoords      (graphics.h)
cleardevice (graphics.h)   getaspectratio    (graphics.h)
clearviewport(graphics.h)    getbkcolor     (graphics.h)
closgraph    (graphics.h)    getcolor     (graphics.h)
detectgraph  (graphics.h)    getdefaultpallette(graphics.h)
drawpoly   (graphics.h)    getdrivername     (graphics.h)
ellipse   (graphics.h)    getfillpattern    (graphics.h)
getfillsettings (graphics.h) outtext (graphics.h)
getgraphmode    (graphics.h) outtextxy(graphics.h)
getimage      (graphics.h) pieslice (graphics.h)
getfinesettings (graphics.h) pufimage (graphics.h)
getmaxcolor     (graphics.h) pulpixel (graphics.h)
getmaxmode      (graphics.h) rectangle(graphics.h)
 
                           - 149 -
getmaxx      (graphics.h) registerbgidriver(graphics.h)
getmaxy      (graphics.h) registerbgifont  (graphics.h)
getmodename     (graphics.h) restorecrtmode   (graphics.h)
getmoderange    (graphics.h) sector    (graphics.h)
getpalette      (graphics.h) settaffpalette   (graphics.h)
getpixel      (graphics.h) setaspectratio   (graphics.h)
gettextsettings (graphics.h) setbkcolor    (graphics.h)
getviewsettings (graphics.h) setcolor    (graphics.h)
getx      (graphics.h) setcursortype    (conio.h)
gety      (graphics.h) setfillpattern   (graphics.h)
graphdefaults   (graphics.h) setfillstyle     (graphics.h)
grapherrormsg   (graphics.h) setgraphbufsize  (graphics.h)
_graphfreemem   (graphics.h) setgraphmode     (graphics.h)
_graphgetmem    (graphics.h) setlinestyle     (graphics.h)
graphresult     (graphics.h) setpalette    (graphics.h)
imagesize       (graphics.h) setrgbpalette    (graphics.h)
initgraph       (graphics.h) settextjunistify (graphics.h)
installuserdriver(graphics.h)settexttyle    (graphics.h)
installuserfont  (graphics.h)setusercharsize  (graphics.h)
line       (graphics.h)setviewport    (graphics.h)
linerel       (graphics.h)setvisualpage    (graphics.h)
lineto       (graphics.h)setwritemode     (graphics.h)
moverei       (graphics.h)textheight    (graphics.h)
moveto       (graphics.h)textwidth    (graphics.h)
Подпрограммы ввода/вывода   --------------------------------

     Эти подпрограммы  реализуют  средства  ввода/вывода  на
уровне потоков и DOS.

      access  (io.h)creatnew (io.h)
      cgets  (conio.h)creattemp (io.h)
      _chmod  (io.h)cscanf      (conio.h)
      chmod  (io.h)dup      (io.h)
      chsize  (io.h)dup2      (io.h)
      clearerr  (stdio.h)eof      (io.h)
      _close  (io.h)fclosse       (stdio.h)
      close  (io.h)fcloseali     (ctdio.h)
      cprintf  (conio.h)fdopen      (stdio.h)
      cputs  (conio.h)foof      (stdio.h)
      _creat  (io.h)ferror      (stdio.h)
      creat  (io.h)fflush      (stdio.h)
      fgetc  (stdio.h)printf      (stdio.h)
      fgetchar  (stdio.h)putc      (stdio.h)
      fgetpos  (stdio.h)putch      (conio.h)
      fgets  (stdio.h)putchar       (stdio.h)
      fllelength  (io.h)puts      (stdio.h)
      flleno  (stdio.h)putw      (stdio.h)
      flushall  (stdio.h)_read      (io.h)
      fopen  (stdio.h)read      (io.h)
      fprintf  (stdio.h)remove      (stdio.h)
      fputc  (stdio.h)rename      (stdio.h)
      fputchar  (stdio.h)rewind      (stdio.h)
      fputs  (stdio.h)scanf      (stdio.h)
      fread  (stdio.h)setbuf      (stdiio.h)
      freopen  (stdio.h)setcursortype (conio.h)
      fscanf  (stdio.h)setftime      (io.h)
      fseek  (stdio.h)setmode       (io.h)
      fsetpos  (stdio.h)setvbuf       (stdio.h)
      fstat  (sys\stat.h)sopen      (io.h)
      ftell  (stdio.h)sprintf       (stdio.h)
      fwrite  (stdio.h)sscanf      (stdio.h)
      getc  (stdio.h)stat      (sys\stat.h)
      getch  (conio.h)      _strerror (string.h,stdio.h)
      getchar  (stdio.h)      strerorr      (stdio.h)
      getche  (conio.h)      tell      (io.h)
      getftime  (io.h)      tmpfile      (stdio.h)
      getpaus  (conio.h)      tmpnam      (stdio.h)
 
                           - 150 -
      gets  (stdio.h)      ungetc      (stdio.h)
      getw  (stdio.h)      ungetch      (conio.h)
      iocti  (io.h)      unlock      (io.h)
      isatty  (io.h)      vfprintf      (stdio.h)
      kbhit  (conio.h)      vfscanf      (stdio.h)
      lock  (io.h)      vprintf      (stdio.h)
      iseek  (io.h)      vscanf      (stdio.h)
      _open  (io.h)      vsprintf      (stdio.h)
      open  (io.h)      vsscanf      (io.h)
      perror  (stdio.h)      _write      (io.h)

 Подпрограммы интерфейса   ---------------------------------
 (DOS, 8086, BIOS)

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

      absread  (dos.h) bioskey (bios.h)  dosexterr (dos.h)
      abswrite (dos.h) bioskey (bios.h)  enable    (dos.h)
      bdos     (dos.h) biosprint (bios.h)  FP_OFF    (dos.h)
      bdosptr  (dos.h) biostime  (bios.h)  FP_SEG    (dos.h)
      bioscom  (bios.h)country (dos.h)   freemem   (dos.h)
      blosdisk (bios.h)ctrlbrk (dos.h)   geninterrupt(dos.h)
      biosequip(bios.h)disable (dos.h)   getcbrk    (dos.h)
      getdfree (dos.h) int86 (dos.h)   poke       (dos.h)
      getdta   (dos.h) int86x (dos.h)   pokeb      (dos.h)
      getfat   (dos.h) intdos (dos.h)   randbrd    (dos.h)
      getfatd  (dos.h) intdosx (dos.h)   randbwr    (dos.h)
      getpsp   (dos.h) intr (dos.h)   segread    (dos.h)
      getvect  (dos.h) keer (dos.h)   setcbrk    (dos.h)
      getverity(dos.h) MK_FP (dos.h)   setdta     (dos.h)
      harderr  (dos.h) outport (dos.h)   setvect    (dos.h)
      hardresume(dos.h)outportb  (dos.h)   setverity  (dos.h)
      hardretn(dos.h)parsfnm (dos.h)   sleep      (dos.h)
      inport(dos.h)peek (dos.h)   unlink     (dos.h)
      inportb(dos.h)peekb (dos.h)

 Подпрограммы манипуляции   --------------------------------

     Эти подпрограммы обрабатывают строкии блоки памяти: ко-
пирование, сравнение, преобразования и поиск.

      memccpy(mem.h,string.h)      stricmp(string.h)
      memchr(mem.h,string.h)      stricmpi(string.h)
      memcmp(mem.h,string.h)      sprien(string.h)
      memcpy(mem.h,string.h)      striwr(string.h)
      memicmp(mem.h,string.h)      stncat(string.h)
      memmoye(mem.h,string.h)      stncmp(string.h)
      memset(mem.h,string.h)      strncmpi(string.h)
      movedata(mem.h,string.h)      strncpy(string.h)
      movmem(mem.h,string.h)      strnicmp(string.h)
      setmem(mem.h)       strnset(string.h)
      stpcpy(string.h)      strpbrk(string.h)
      strcat(string.h)      strrchr(string.h)
      strchr(string.h)      strrev(string.h)
      strcmp(string.h)      strset(string.h)
      strcoll(string.h)      strspn(string.h)
      strcpy(string.h)      strstr(string.h)
      strcspn(string.h)      strtok(string.h)
      strdup(string.h)      strupr(string.h)
      strerror(string.h)      strxfrm(string.h)

 Математические подпрограммы   -----------------------------
     Эти подпрограммы выполняют математические  вычисления и
преобразования.

      abs     (complex.h,stdlib.h)     atof(stdlib.h,math.h)
 
                           - 151 -
      acos    (complex.h,math.h)       atoi(stdlib.h)
      arg     (complex.h)       atol(stdlib.h)
      asin    (complex.h,math.h)       bcd(std.h)
      atan    (complex.h,math.h)       cabs(math.h)
      atan2   (complex.h,math.h)       ceil(math.h)
      clear87 (float.h)        ltoa(stdlib.h)
      complex (complex.h)       _matherr (math.h)
      conj    (complex.h)       matherr(math.h)
      _control(float.h)        modf(math.h)
      cos     (complex.h,math.h)       norm(complex.h)
      cosh    (complex.h,math.h)       polar(complex.h)
      div     (math.h)       poly(math.h)
      ecvt    (stdlib.h)       pow(complex.h,math.h)
      exp     (math.h)       pow10(math.h)
      fabs    (math.h)       rand(stdlib.h)
      fcvt    (stdlib.h)       random(stdlib.h)
      floor   (math.h)       randomize(stdlib.h)
      fmod    (math.h)       real(complex.h)
      _fpreset(float.h)        _rotl(stdlib.h)
      frexp   (math.h)       _rotr(stdlib.h)
      gcvt    (stdlib.h)       sin(complex.h,math.h)
      hypot   (math.h)       sinh(complex.h,math.h)
      imag    (complex.h)       sqrt(complex.h,math.h)
      itoa    (stdlib.h)       srand(stdlib.h)
      labs    (stdlib.h)       _status87(float.h)
      ldexp   (math.h)       strtod(stdlib.h)
      ldiv    (math.h)       strtol(stdlib.h)
      log     (complex.h,math.h)       strtoul(stdlib.h)
      log10   (complex.h,math.h)       tan(complex.h,math.h)
      _lrotl  (stdlib.h)       tanh(complex.h,math.h)
      _lrotr  (stdlib.h)       ultoa(stdlib.h)

 Подпрограммы управления памятью   -------------------------

     Эти подпрограммы обеспечивают динамическое  распределе-
ние памяти для моделей данных small и large

 allocmem      (dos.h)       farrealloc      (alloc.h)
 brk      (alloc.h)        free       (alloc.h,
 calloc      (alloc.h) stdlib.h)
 coreleft      (alloc.h,        heapcheck       (alloc.h)
 stdlib.h)       heapcheckfree   (alloc.h)
 farcalloc       (alloc.h)        heapcheckknode  (alloc.h)
 farcoreleft     (alloc.h)        heapwalk        (alloc.h)
 farfree      (alloc.h)        malloc       (alloc.h,
 farheapcheck    (alloc.h) stdlib.h)
 farheapcheckfree(alloc.h)        realloc       (alloc.h,
 farheapchecknode(alloc.h) stdlib.h)
 farheapfllfree  (alloc.h)        sbrk       (alloc.h)
 farheapwalk     (alloc.h)        setblock        (dos.h)
 farmalloc       (alloc.h)
 Разные подпрограммы   -------------------------------------

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

  delay      (dos.h)       setjmp     (setjmp.h)
  localeconv      (locale.h)       setlocale     (locale.h)
  longjmp      (setjmp.h)       sound     (dos.h)
  nosound      (dos.h)

 Подпрограммы управления процессами   ----------------------

     Эти подпрограммы запускают и завершают выполнение одних
процессов из других.

 
                           - 152 -
  abort  (process.h) execvp (process.h) spawnl (process.h)
  execl  (process.h) execvpe(process.h) spawnle (process.h)
  execle (process.h) _exit  (process.h) spawnlp (process.h)
  execlp (process.h) exit   (process.h) spawnlpe(process.h)
  execlpe(process.h) getpid (process.h) spawnv(process.h)
  execv  (process.h) reise  (signal.h)spawnve (process.h)
  execve (process.h) signal (signal.h)spawnvp (process.h)
spawnvpe(process.h)

 Стандартные подпрограммы   --------------------------------

      Эти подпрограммы являются стандартными.

  abort  (stdlib.h)  exit   (stdlib.h)malloc(stdlib.h)
  abs (stdlib.h)  fcvt   (stdlib.h)putenv(stdlib.h)
  atexit (stdlib.h)  free   (stdlib.h)qsort(stdlib.h)
  atof (stdlib.h)  gcvt   (stdlib.h)rand(stdlib.h)
  atol (stdlib.h)  getenv (stdlib.h)realloc (stdlib.h)
  atol (stdlib.h)  itoa   (stdlib.h)srand(stdlib.h)
  bsearch(stdlib.h)  labs   (stdlib.h)stdtod(stdlib.h)
  calloc (stdlib.h)  lfind  (stdlib.h)strtol(stdlib.h)
  ecvt (stdlib.h)  lsearch(stdlib.h)swab(stdlib.h)
  _exit  (stdlib.h)  itoa   (stdlib.h)system(stdlib.h)

 Подпрограммы вывода на дисплей текстовых окон -------------

      Эти подпрограммы выводят текст на экран.

      clreol (conio.h)    gotoxy (conio.h)
      clrscr (conio.h)    highvideo  (conio.h)
      delline (conio.h)    insline (conio.h)
      gettext (conio.h)    lowvideo (conio.h)
      gettextinvo(conio.h)    movetext (conio.h)
      normvideo     (conio.h) textcolor  (conio.h)
      puttext    (conio.h) textmode   (conio.h)
      necursortype  (conio.h) wherex     (conio.h)
      textattr    (conio.h) wherey     (conio.h)
      textbackground(conio.h) window     (conio.h)

 Подпрограммы времени и даты   -----------------------------

     Эти подпрограммы предназначены для преобразований и ма-
нипуляций временем и датой.

      asctime    (time.h) mktime     (time.h)
      ctime    (time.h) setdate    (dos.h)
      difftime    (time.h) settime    (dos.h)
      dostounix     (dos.h) stime    (time.h)
      ftime    (sys\timeb.h)strftime   (time.h)
      getdate    (dos.h) time    (time.h)
      gettime    (dos.h) tzset    (time.h)
      gmtime    (time.h) unixtodos  (dos.h)
      locoltime     (time.h)

 Подпрограммы для обработки переменного списка аргументов

     Эти подпрограммы  используютсядля  доступа к переменным
спискам аргументов (например, vprintf и т.д.).

      va_arg    (stdarg.h)
      va_end    (stdarg.h)
      va_stsrt    (stdarg.h)





 
                           - 153 -








Глава 3    Потоки С++

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

Новые потоки вместо старых

     Turbo C++   поддерживает   как  старую  библиотеку  С++
stream,  так и новую усовершенствованную библиотеку iostream
С++ версии 2.0. Возможность работы с обеими версиями поможет
вам, если у вас имеются программы, написанные по старым сог-
лашениям, и вы желаете использовать Turbo C++ для перехода к
более эффективным потокам ввода/вывода версии2.0. Мы настоя-
тельно  рекомендуем  вампри  создании новых программ пользо-
ваться библиотекой iostream версии  2.0.  Приводя  некоторые
материалы, необходимые дляперехода к потокам версии 2.0 (на-
чиная со стр.184 оригинала),  данная глава  главным  образом
посвящается классам иправилам потоков iostream версии 2.0.

   Использование потоков 2.0

     Усовершенствованные потоки iostream версии 2.0,  хотя и
обеспечивают по большей части совместимость для старой  вер-
сии С++,  предлагают тем не менее новые возможности, связан-
ные с использованием множественного наследования  и  прочими
средствами, появившимися в С++ версии 2.0.

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

     Концепция потоков С++ нацелена  на  решение  нескольких
проблем, решаемых стандартными библиотечнымифункциями ввода/
вывода С, такими какprintfи scanf. Последние, разумеется ос-
таются  доступными для программиста,  работающего в С++,  но
улучшенная гибкость и  элегантность  потоков  С++  уменьшают
привлекательность функций библиотеки stdio.h.  Классы,  свя-
занные с потоками С++,  предлагают вам расширяемые библиоте-
ки,  позволяющие  вам  выполнять форматированный ввод/выводс
контролемтипов какдля предопределенных, так и для определяе-
мых пользователем типов данных спомощью перегруженных опера-
цийи прочих объектно-ориентированных методов.

     Для обращения к  вводу/выводу  потоком  ваша  программа
должна включать файл iostream.h. Для некоторых функций пото-
ков требуются и другие файлы заголовка. Например, для выпол-
нения  форматирования  в оперативной памяти с использованием
классов istrstream и ostrstream необходим  файл strstream.h.
Файл  заголовка strstream.h включает также iostream.h.  Если
вам требуется класс fstream,  включите  файл  fstream.h,так-
жевключающий iostream.h. И разумеется, можно включить однов-
 
                           - 154 -
ременно и fstream.h, и strstream.h.

Что такое поток?

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

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

      Библиотека iostream

     Библиотека iostream   имеет  два  параллельных  класса:
streambuf и ios.  Оба они являются классами низкого уровня и
каждый выполняет свой круг задач.


 streambuf
 ___________________________________________________________

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

 ios
 ___________________________________________________________

     Класс ios (и следовательно, производные от него классы)
содержит указатель на streambuf.

     ios имеет два производных класса: istream (для ввода) и
ostream (для вывода).  Другой класс, iostream, является про-
изводным  классом  сразуот  istream иostream вследствие мно-
жественного наследования:

    class ios;
    class istream : virtual public ios;
    class ostream : virtual public ios;
    classiostream : public istream, public ostream;

     Кроме того, существуеттри класса withassign, являющихся
производными классами от istream, ostream и iostream:

    class istream_withassign : public istream;
    class ostream_withassign : public ostream;
    class iostream_withassign : public iostream;

 Классы потоков
 ___________________________________________________________

     - Класс ios содержит переменные состояния для интерфей-
са с streambuf и обработки ошибок.

     - Класс istream поддерживает как форматированные, так и
 
                           - 155 -
неформатированные преобразования потоков символов, извлекае-
мых из streambuf.

     - Класс ostream поддерживает как форматированные, так и
неформатированные преобразования потоков символов,  помещае-
мых в streambuf.

     - Класс iostream объединяет классы  istream  и  ostream
для двунаправленных операций, в которых один поток действует
и как источник, и как приемник.

     - Производные  классы  withassign  обеспечивают  четыре
предопределенных  "стандартных" потока:  cin,  cout,  cerr и
clog, описываемые в следующем разделе. Классы withassign до-
бавляют  к соответствующим базовым классам операции присвое-
ния, следующим образом:

    class istream_withassign : public istream (*
       istream_withassign();
       istream& operator=(istream&);
       istream& operator=(streambuf*);
     *)

     и аналогично       для       ostream_withassign       и
iostream_withassign.

     Классом потока  называется любой класс,  производный от
классов istream и ostream.

   Четыре стандартных потока

     Программы С++ начинаются с  четырьмя  предопределенными
открытыми   потоками,   объявленными   как  объекты  классов
withassign в iostream.h следующим образом:

    extern istream_withassign cin;
    extern ostream_withassign cout;
    extern ostream_withassign cerr;
    extern ostream_withassign clog;

     Их конструкторывызываются   всякий  раз  при  включении
iostream.h,  но фактическая инициализация выполняется только
один раз.

      Четыре стандартных потока выполняют следующее:

    cin    Стандартный ввод (дескриптор файла 0)
   (Соответствует stdin)

    cout   Стандартный вывод (дескриптор файла 1)
   (Соответствует stdout)

     cerr Стандартный  вывод  ошибок  (дескриптор  файла 2).
cerr буферизуется поблочно;  очистка буфера  происходит  при
каждой новой вставке
   (Соответствует stderr)

     clog Данный поток представляет собой полностью  буфери-
зуемую версию cerr.

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

     Вывод потока осуществляетсяоператором вставки,или поме-
щения, <<. Для операций вывода перегружаетсястандартная опе-
 
                           - 156 -
рация сдвига влево <<.  Ее левый операнд представляет  собой
объект типа класса ostream. Правый операнд может иметь любой
тип, для которого определен вывод потоком (подробнее об этом
говорится ниже).  Вывод потоком определен для встроенных ти-
пов.  Операция<<,  перегруженнаядля  типа  type,  называется
вставкойэтого типа. Например,

    cout << "Hello!\n";

     записывает строку  "Hello!"  в  cout (стандартный поток
вывода,  который обычно направлен на экран), после чего сле-
дует новая строка. Здесь << - это строка вставки типа char*.

     Операция << обладает ассоциативностью слева и возвраща-
ет ссылку на объект ostream, для которого она вызывалась.Это
позволяет организовать каскадные вставки:

    void function_display(int i, double d)
    (*
       cout << "i=" << i << ", d=" << d << "\n";
    *)

      Это вызовет вывод чего-либо вроде:

    i = 8, d = 2.34

 на стандартное устройство вывода.

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

    cout << "sum = " << x+y << "\n";

 без круглых скобок. Однако, в случае

    cout << (x&y) << "\n";

 круглые скобки нужны.

 Встроенные типы
 ___________________________________________________________

     Типы вставок, поддерживаемые непосредственно,это : char
(signed и unsigned), short (signed иunsigned), int (signed и
unsigned),  long (signed и unsigned), char* (рассматриваемый
как строка),  float, double, long double и void*. Интеграль-
ные типы преобразовываются по правилам,  по умолчанию  дейс-
твующим  для printf (еслиэти правила не изменены путем уста-
новки различных флагов ios).Например, если заданы объявления
int i; long l;, то следующие два оператора

    cout << i << " " << l;
    printf("%d %ld, i, l);

 приведут к одному и тому же результату.

     Аналогичным образом,  типы сплавающей точкой преобразо-
вываются правилам умолчания для printf с преобразованием %g.
Итак, в случае объявления double d;, операторы

    cout << d;
    printf("%g", d);

 дают один и тот же результат.

      Вставка указателя (void*) также предопределена:

 
                           - 157 -
    int i = 1;
     cout << &i;  // указатель выводится на дисплей в  шест-
надцати// ричном формате

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

    char ch = 'A';
    cout << ch;       // на дисплей выводится А

 Функции put и write
 __________________________________________________________

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

    ostream ostream::put (char ch);

     При объявлении int ch='x'; следующие две строки эквива-
лентны:

    cout.put(ch);
    cout << (char)ch;

     Функция-компонент writeпозволяет  вывод больших по раз-
меру объектов:

  ostream& ostream::write(const signed char* ptr, int n);
  ostream& ostream::write(const unsigned char* ptr, int n);

     Функции write выводят nсимволов (включая любые входящие
пустые символы) в двоичномформате.  В отличие  от  строковой
вставки, write не прекращает работу, встретив пустой символ.
Например,

    cout.write((char *)&x, sizeof(x))

     пошлет непреобразованное представление  х настандартное
устройство вывода.

     Существует тонкое различие между форматированной опера-
цией << и неформатированнымифункциями putи write. Форматиро-
ванная  операция  может  вызвать очистку связанных потоков и
иметь атрибут ширины поля.Неформатированные операции не  об-
ладают  этими  свойствами.  Поэтому  cout  << 'a' и cout put
('a') могут давать разные результаты. Все флаги форматирова-
ния применимы к <<,  но ни один из них не применим к put или
write.

 Форматирование вывода
 ___________________________________________________________
     Форматирование ввода и вывода  определяется  различными
флагами состояний формата,  перечисленными в классе ios. Эти
состояния определяются битами числа типа long  int следующим
образом:

    public:
       enum (*
  skipws   = 0x0001, // пропуск пробельного символа на вводе
  left   = 0x0002, // вывод с левым выравниванием
  right    = 0x0004, // вывод с правым выравниванием

  internal = 0x0008, // заполнитель после знака или
     // указателя системы счисления
  dec   = 0x0010, // десятичное преобразование
  oct   = 0x0020, // восьмеричное преобразование
  hex   = 0x0040, // шестнадцатиричное преобразование
 
                           - 158 -
  showbase = 0x0080, // показать на выходе указатель
     // системы счисления
 showpoint = 0x0100, // показать позицию десятичной точки
     // (на выходе)
 uppercase = 0x0200, // вывод шестнадцатиричных значений
     // буквами верхнего регистра
  showpos  = 0x0400, // показать знак "+" для положительных
     // чисел
scientific = 0x0800, // использовать запись чисел с плава-
     // ющей точкой с выводом экспоненты Е
     // например, 12345E2
  fixed    = 0x1000, // использовать запись чисел с плава-
     // ющей точкой типа 123.45
  unitbuf  = 0x2000, // стирание всех потоков после вставки
  stdio = 0x4000,    // стирание stdout и stderr после
     // вставки
    *);

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

 Основание системы счисления при преобразованиях
 ___________________________________________________________

     По умолчанию вставкаинтегральных  типов  выполняется  в
десятичной записи. Это можно изменить соответствующими уста-
новками флагов ios::dec,  ios::oct и  ios::hex  (см.  раздел
"манипуляторы"). Есливсе эти флаги равнынулю (по умолчанию),
то вставка выполняется в десятичном формате.

 Ширина
 ___________________________________________________________

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

    int ios::width(int w);  // устанавливает поле ширины в w
    // и возвращает предыдущую ширину
    int ios::width();    // возвращает текущую ширину --
    // не внося никаких изменений

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

    int i = 123;
    int old_w = cout.width(6);
    cout << i;      // на выводе будет bbb123, где bbb =
      // пробелы. Затем ширина устанавлива-
      // ется равной 0
     cout.width(old_w); //  восстановление предыдущей ширины
// поля

     Отметим, что после каждой форматированной вставки шири-
 
                           - 159 -
на очищается в ноль, так что в

    int i, j;
    ...
    cout.width(4);
    cout << i << " " << j;

     i будет представлена минимум четырьмя символами, однако
пробел в середине выражения и j будут представлены минималь-
но необходимым числом символов.

 Манипуляторы
 ___________________________________________________________

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

    cout << setw(4) << i << setw(6) << j;

 эквивалентно более широкой записи

    cout.width(4);
    cout << i;
    cout.width(6);
    cout << j;

     setw представляет собой  параметризованный манипулятор,
объявлениекоторого находится в iomanip.h.  Прочие параметри-
зованные  манипуляторы,  setbase,   setfill,   setprecision,
setiosflags и resetiosflags, работают аналогичнымобразом (см
таблицу 3.1). Для того, чтобы использовать эти манипуляторы,
ваша  программа должна включать iomanip.h.  Вы можете писать
свои собственные манипуляторы, без параметров:

    ostream& dingy( ostream os)
    (*
       return os << "\a\a";
    *)
    ...
    cout << i << dingy << j;

     Манипуляторы с параметрами более сложны и требуют вклю-
чения iomanip.h.

 Манипуляторы       Таблица 3.1
 -----------------------------------------------------------
 Манипулятор     Синтаксис    Действие
 -----------------------------------------------------------
 dec     outs<<dec    Установка флага форма-
     ins>>dec    тирования с десятичными
    преобразованиями

 hex     outs<<hex    Установка флага форма-
     ins>>hex    тирования с шестнадцати-
    ричными преобразованиями

 oct     outs<<oct    Установка флага форма-
     ins>>oct    тирования с восьмеричными
    преобразованиями

 
                           - 160 -
 ws     ins>>ws    Извлечение пробельных
    символов

 endl     outs<<endl     Вставка символа новой
    строки и очистка потока

 ends     outs<<ends     Вставка оконечного
    пустого символа в строку

 flush     outs<<flush    Очистка ostream

 setbase(int)     outs<<setbase(n)    Установка системы счис-
                                      ления при преобразова-
                                      ниях с основой n (0, 8,
                                    10 или 16). Ноль означает
                                      по умолчанию десятичную
                                    систему при выводе и пра-
                                     вила С для литералов це-
                                       лых чисел при вводе.

     resetiosflags(long) ins>>resetiosflags(l) Очистка  фор-
матных битов outs<<resetiosflags(l) в ins или outs, заданных
аргументом l

     setiosflags(long) ins>>setiosflags(l) Установка формат-
ных битов outs<<setiosflags(l) в ins или outs,  заданных ар-
гументом l

     setfill(int) ins>>setfill(n)   Установка    символа-за-
полнителяouts<<setfill(n) в n

     setprecision(int) ins>>setprecision(n)  Установка  точ-
ности представления outs<<setprecision(n) чисел  с плавающей
точкой равной n разрядам

 setw(int)     ins>>setw(n)    Установка ширины поля
     outs<<setw(n)    в значение n
 -----------------------------------------------------------

     Не-параметризованные манипуляторы dec,  hex и oct (объ-
явленные в ios.h) непринимают никаких аргументови просто из-
меняют основание системы счисления при преобразовании (и ос-
тавляютэто изменение в силе):

    int i = 36;
    cout << dec << i << " "
 << hex << i << "
 << oct << i << endl;
    // вывод на дисплей 36 24 44

     Манипулятор endl  вставляет  символ  новой   строки   и
очищает поток.  Можно также в любой момент очистить ostream,
записав

    ostream << flush;


 Заполнители и дополнение вправо и влево
 ___________________________________________________________

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

     По умолчанию символом-заполнителем является пробел. Из-
менить данное умолчание позволяет функция fill:


 
                           - 161 -



    int i = 123;
    cout.fill("*");
    cout.width(6);
    cout << i;       // на дисплей будет выведено ***123

     По умолчанию  устанавливается  выравнивание  по правому
краю (дополнение символами-заполнителями влево). Эти умолча-
ния  (а также прочие форматные флаги) можно изменять при по-
мощи функций setf и unsetf:

    int i = 56;
    ...
    cout.width(6);
    cout.fill('#');
    cout.setf(ios::left,ios::adjustfield);
    cout << i       // на дисплей будет выведено 56####

     Второй аргумент, ios::adjustfield, сообщает setf, какие
биты должны быть установлены. Первый аргумент,ios::left, со-
общает setf,в какие именно значения устанавливаютсяэти биты.
Альтернативно  можно  использовать   манипуляторы   setfill,
setiosflags иresetiosflags, которые позволяют модифицировать
символ-заполнитель и направление дополненияпри  форматирова-
нии (см. табл.3.1).

 Вставки, определяемые пользователем
 ___________________________________________________________

     Вы можете писать собственныевставки  для  вывода  своих
собственных  типов данных,  перегружая для этого операцию<<.
Предположим, у вас имеется тип

    struct info (*
       char *name;
       double val;
       char *units;
    (*;

      Вы можете перегрузить << следующим образом:

    ostream& operator << (ostream& s, info& m)
    (*
       s << m.name << " " << m.val << " " << m.units;
    *)

      Операторы

    info x;
    ...
    // здесь инициализируется x
    ...
    cout << x;

 даст на выходе что-либо вроде "capacity 1.25 liters".
       Ввод   ----------------------------------------------

     Ввод потоком аналогичен выводу,  но использует перегру-
женную  операцию сдвига вправо,  >>,  и называется операцией
извлечения,  или извлечением. Операция >> обеспечивает более
компактную и читаемую альтернативу семейству функций scanf в
stdio (она также лучше защищена от ошибок). Левый операнд >>
представляет собой объект типа класса istream. Как и для вы-
вода,  правый операнд может быть любого типа,  для  которого
определен вывод потоком.
 
                           - 162 -

     Все встроенные типы, перечисленные вышедля вывода, так-
же имеют предопределенные операции извлечения.  Операция  >>
может  быть  такжеперегружена  для ввода потоком ваших собс-
твенных типов данных.  Операция  >>,перегруженная  для  типа
type, называется извлечением type. Например,

    cin >> x;

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

     По умолчанию  >> опускает пробельные символы (как опре-
делено функцией isspace в ctype.h), а затем считывает симво-
лы,  соответствующие  типуобъекта ввода.  Пропуск пробельных
символов управляется флагом ios::skipws в перечислимой пере-
менной  состояний  формата  (см.  "Форматирование вывода" на
стр.170 оригинала). Флаг skipws обычно устанавливает пропуск
пробельных символов.  Очистка этогофлага (например, припомо-
щиsetf)выключает пропуск пробельных символов.  Отметим также
специальныйманипулятор  "приемника",  ws,  который позволяет
игнорировать пробельные символы (см. таблицу 3.1).

 Изменение извлечений
 ___________________________________________________________

     Как и в случае <<, операция >> обладает свойством ассо-
циативности слева и возвращает левый операнд.  Левый операнд
является ссылкой на объект istream, для которого была вызва-
на данная операция. Это позволяет объединятьв одном операто-
ре несколько операций ввода. Рассмотрим следующий пример:

    int i;
    double d;
    cin >> i >> d;

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

 Извлечения для встроенных типов
 ___________________________________________________________

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

    Интегральные извлечения

     Для типов  short,  int и long (signed и unsigned) дейс-
твие операции >> по умолчанию заключаетсяв пропуске  не-про-
бельныхсимволов  и преобразовании интегрального значения пу-
тем чтения символов ввода до тех  пор,  пока  не  встретится
символ,  который  не может являться допустимой частью предс-
тавления данного типа.  Формат  распознаваемых  интегральных
 
                           - 163 -
значений тот же, что и для целочисленных констант С++, заис-
ключением целочисленных суффиксов. (См. стр.11 оригинала).

 Предупреждение

     Если вы задали преобразования типа hex, dec или oct, то
именно такиерезультаты выи получите.  0x10 становится0 в де-
сятичном или восьмеричном представлении; 010 становится 10 в
десятичном представлении и 16 в шестнадцатиричном.


   Извлечения с плавающей точкой

     Для типов float и double действие операции >> состоит в
пропуске  пробельных  символов  и  преобразовании значения с
плавающей точкой путем чтения вводимых символов до  тех пор,
пока не встретится символ,  который не может являться частью
представлениячисла с плавающей точкой. Формат распознаваемых
значений  с  плавающей  точкой тот же,  что и для констант с
плавающей точкой С++,  за исключением суффиксов. (См. стр.16
оригинала).


       Символьные извлечения

     Для типа char (signed или unsigned)  действие  операции
>> состоит в пропускепробельных символов и записи следующего
(не-пробельного) символа. Если вам требуетсяпрочесть следую-
щий символ,  неважно,  является ли он пробельным или нет, то
можно использовать одну из функций-компонентов get:

    char ch;
     cin.get(ch); //  ch устанавливается на следующий символ
потока // даже если это пробельный символ

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

    istream& istream::get(char *buf, int max, int term='\n');

     Эта функция считывает символы из входного потока в сим-
вольный массив buf до тех пор,  пока не будет считано  max-1
символов,  либо пока не встретится символ,  заданный term, в
зависимости от того,  что произойдет раньше. Завершающийпус-
тойсимволдобавляется автоматически. По умолчаниютерминатором
(который не требуется задавать) является символ новой строки
('\n').  Сам  терминатор  в  массив  buf не считывается и из
istream не удаляется. Массив buf должен иметь размер как ми-
нимум max символов.

     По аналогии  с  функцией-компонентом ostream write (см.
стр.170 оригинала) можно прочитать "сырые"  двоичные  данные
следующим образом:

    cin.read ( (char*)&x, sizeof(x) );

     Для типа  char*  (рассматриваемого как строка) действие
операции >> состоит в пропуске пробельныхсимволов  и  записи
следующих  (не-пробельных)  символов  до  тех  пор,  пока не
встретится следующий пробельный  символ.  Затем  добавляется
завершающий  нулевой  (0) символ.Следует предъявлятьосторож-
ность и избегать "переполнения" строки. Ширина по умолчанию,
равная  нулю (означает,  что предельное значение не задано),
может быть изменена при помощи setw следующим образом:

 
                           - 164 -
    char array[SIZE];
    ...
    // инициализация массива
    ...
    cin.width(sizrof(array));
    cin >> array      // позволяет избежать переполнения

     В случае любого вводавстроенных типов, если конец ввода
встретится ранее первого не-пробельного  символа, вмишеньbuf
ничего записано не будет, а состояние istream будет установ-
лено равным "отказу".  Таким образом,  если мишень  была  не
инициализирована,  то она и останется не инициализированной.

 Функция возвращения
 ___________________________________________________________

      Функция-компонент

    istream istream::putback(char c);

     возвратит обратно в istream один символ  c;  если  этот
символ  не  может  быть помещен обратно,  то устанавливается
состояние потока "отказ". Следующая простая подпрограмма вы-
полняет  считывание  идентификатора С++ со стандартного уст-
ройства ввода:

     void getident (char *s /* сюда помещается идентификатор
*/ )
    (*
       char c = 0;   // защита от конца файла
       cin >> c;   // пропуск пробельных символов
       if (isalpha(c) \!\! c == '_')
  do (*
     *s++ = c;
     c = 0;   // защита от конца файла
     cin.get(c);
   *) while (isalnum(c) \!\! c =='_');
       *s = 0;   // терминатор строки
       if (c)
  cin.putback(c);  // один символ всегда лишний
       *)

 Ввод типов, определяемых пользователем
 ___________________________________________________________

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

    istream& operator >> (istream& s, info& m);
    (*
       s >> m.name >> m.val >> m.units;
       return s;
    *)

     (В реальных прикладных программах, разумеется, вы може-
те добавить коды для проверки ошибок ввода).  Для считывания
строки ввода, такойкак "capacity 1.25 liters", можно исполь-
зовать следующую запись:

    cin >> m;
     Инициализация потоков

     Потоки cin, cout, cerr и clog инициализируются и откры-
ваются  при  загрузке программы и затем подключаются к соот-
ветствующим      стандартным      файлам.      Инициализация
 
                           - 165 -
(конструирование) потока означает ассоциирование его с буфе-
ром потока. Класс ostream имеет следующий конструктор:

    ostream::ostream(streambuf*);

     который инициализирует переменные состояния ios и ассо-
циирует буфер потока с объектом ostream. Конструктор istream
работает аналогичным образом.  В большинстве случаев вам  не
требуется специально рассматривать вопросами буферизации.

     Библиотека iostream предлагает множество классов,  про-
изводных от streambuf,  ostream и istream,  что дает широкий
выбор  методов  создания  потоков с различными источниками и
приемниками, а также различными методами буферизации.

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

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

 stdiobufstdiobuf поддерживает ввод/вывод через структуры
stdio FILE и предназначается исключительно для
совместимости кодов С++ при их комбинировании с
существующими программами С.

 strstreambufstrstreambuf позволяет ввод и вывод символов из
байтовых массивов в памяти. Два дополнительных
класса, istrstream и ostrstream, обеспечивают ввод/
вывод с форматированием в памяти.

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

    ifstream  является производным от istream
    ofstream  является производным от ostream
     fstream  является производным от iostream

     Эти триклассаподдерживают форматированный  ввод/вывод в
файлы при помощи буферов файлов (filebuf).

   Простой ввод/вывод в файл

     Класс ofstream  наследует операции вставки отostream, а
ifstream наследует операцииизвлечения отistream.  Они  также
обеспечивают  конструкторы и функции-компоненты для создании
файлов и обработки ввода/вывода в этот файл. Следуетвключать
fstream.h  во  все  программы,  где  используются эти файлы.
Рассмотрим следующий пример,  в котором файл FILE_FROM копи-
руется в FILE_TO:

    #include fstream.h
    ...
    char ch;
    ifstream f1("file_from");
    if (!f1) errmsg("Cannot open 'filr_from' for input");
    ofstream f2("file_to");
    if (!f2) errmsg("Cannot open 'filr_to' for output");
    while ( f2 && f1.get(ch) ) f2.put(ch);

     Ошибки, связанные с потоками,  подробно обсуждаются  на
стр.181 оригинала.

     Отметим, что если конструкторы ifstream или ofstream не
могут открыть указанные файлы,  то устанавливается соответс-
твующее состояние ошибки потока.
 
                           - 166 -

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

    ofstream ofile;     // создание выходного потока файла
    ...
    ofile.open("payroll");   // поток ofile ассоциируется с
     // файлом payroll
    // работа с некоторым паролем
    ofile.close();     // payroll закрывается
     ofile.open("employee"); //  поток ofile можно использо-
вать // повторно

     По умолчанию файлы открываются в  текстовом  режиме.Это
означает,  что  на  вводе последовательность возврата карет-
ки/перевода строки преобразуется в символ  '\n'.  На  выводе
символ  '\n'  преобразуется в последовательность возврат ка-
ретки/перевод строки. В двоичном режиме такие преобразования
не выполняются.

     Функциякомпонента ofstream::open  объявляется следующим
образом:

     void open(char * name, int=ios::out, int prot=filуbuf::
openprot);

      Аналогично, объявление ifstream::open имеет вид:

     void open(char     *     name,     int=ios::in,     int
prot=filуbuf::openprot);

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

 -----------------------------------------------------------
 Бит режимаДействие
 -----------------------------------------------------------
     ios::appДобавление данных - запись всегда в конец файла
    ios::ateПоиск конца файла после первоначального открытия
     ios::inОткрытие на ввод (подразумевается для ifstream)
    ios::outОткрытие на вывод (подразумевается для ofstream)
     ios::truncУничтожение содержимого в случае,  если  файл
существует  (подразумевается,  если  ios::out  задано,  и ни
ios::ate, ни ios::app не заданы)
   ios::nocreateЕсли файл не существует, то open дает ошибку
     ios::noreplace Если файл существует,  open  для  файлов
вывода дает ошибку, если не установлены ate или app
 -----------------------------------------------------------

     Мнемоника режима берется из перечислимого значения open
_mode в ios:

    class ios (*
    public:
     enum open_mode   (*   in,   out,  app,  ate,  nocreate,
noreplace *);
    *);

      Оператор

    ofstream ofile("data",ios::app\!ios::nocreate);

     попытается открыть  файл DATA на вывод в режиме append;
если файл не существует,  это приведет к неудаче. Информация
 
                           - 167 -
об  этой неудаче будет обозначена состоянием ошибки ofile. В
случае удачного завершения поток ofile будет добавлен к фай-
лу DATA. Класс fstream (производный от двух классов ifstream
и ofsrtream) может использоваться для создания  файлов,  од-
новременно позволяющих и ввод, и вывод:

    fstream inout("data:,ios::in\!ios::out);
    inout << i;
    ...
    inout >> j;

     Для определения текущей позиции "get" или текущей пози-
ции  "put"  файла  можно  воспользоваться  функциями tellg и
tellp; они определяют положение в потоке, гдебудет выполнена
следующая операция вывода или ввода:

     streampos cgp = inout.tellg(); // cgp - это текущая по-
зиция get

     где streampos это typedef в  fstream.h.  Функции-компо-
ненты  seekg и seekp могут сбрасывать значения текущей пози-
ции get и put:

     inout.seekg(cp); //  установка  в  cp  текущей  позиции
"put"

     Варианты seekp и seekg позволяют получить искомые пози-
ции в относительных смещениях:

     inout.seekg(5,ios::beg); // перемещение cp на 5 байт от
начала
     inout.seekg(5,ios::cur); // перемещение cp  на  5  байт
вперед
     inout.seekp(5,ios::end); // перемещение cp на 5 байт до
конца

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

     Каждый поток имеет связанное с ним состояние ошибки, т.
е. набор битов ошибок, объявленный как перечислимое значение
io_state в классе ios:

    class ios (*
    public:
    ...
       // биты состояния потока
       enum io_state (*
  goodbit   = 0x00,
  eofbit    = 0x01,
  failbit   = 0x02,
  badbit    = 0x04,
  hardfail  = 0x10
       *);
    ...
    *);

     Отметим,что goodbit  в действительности не является ви-
том,  а представляет собой нулевое значение,  указывающее на
то,что никакие биты ошибки не устанавливались.

     Ошибки ввода/вывода потоком устанавливает соответствую-
щий бит(ы), как указано в табл.3.2.

 
                           - 168 -
 Биты ошибок ios       Таблица 3.2
 -----------------------------------------------------------
 Бит состояния Его смысл
 -----------------------------------------------------------
 goodbit Если этот бит не установлен, то все в порядке.

 eofbit  "Конец файла": устанавливается, если istream не
 имеет больше битов для извлечения. Последующие
 попытки выполнить извлечение игнорируются.

 failbit Устанавливается, если последняя операции ввода/
 вывода (извлечение или преобразование) окончилась
 неудачей. После сброса данного бита ошибки поток
 готов к последующему использованию.

 badbit  Устанавливается, если последняя попытка ввода/
 вывода являлась недопустимой. Поток может быть ис-
 пользован (не всегда) после сброса условия ошибки.

 hardfail Устанавливается, если для данного потока встрети-
 лось невосстановимое состояние ошибки.
 -----------------------------------------------------------

     После того, как поток получил состояние ошибки, все по-
пытки вставки или извлечения из данного потока будут игнори-
роваться  до  тех  пор,  пока  не  будет исправлено условие,
вызвавшее состояние ошибки,  а бит(ы) ошибки очищен(ы)  (при
помощи, например, функции компонента ios::clear(i). Функция-
компонент ios::clear(i) фактически устанавливает биты ошибки
в   соответствии  с  целочисленным  аргументом  i,  так  что
ios::clear(0)  очищает  все  биты  ошибки,  за   исключением
hardfail, который таким образом очищен быть не может.

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

 Функции-компоненты для обработки
 текущего состояния потокаТаблица 3.3
 -----------------------------------------------------------
 Функция компонент    Действие
 -----------------------------------------------------------
 int rdstate();        Возвращает текущее состояние ошибки

     void clear(int  i=0);  Устанавливает  биты  ошибки в i.
Например, код str.clear(ios::failbit\!str.rdstate());

       устанавливает failbit потока str без разру-
       шения прочих битов

 int good();       Возвращает не-нулевое значение, если биты
       ошибки не устанавливались; в противном случае
       возвращает ноль

 int eof();       Возвращает не-нулевое значение, если
       установлен бит eofbit istream; в противном
       случае возвращает ноль.

 int fail();       Возвращает не-нулевое значение, если был
       установлен один из битов failbit, badbit или
       hardfail; в противном случае возвращает ноль.

 int bad();       Возвращает не-нулевое значение, если был
       установлен один из битов badbit или
 
                           - 169 -
       hardfail; в противном случае возвращает ноль.
 -----------------------------------------------------------

     Вы можете также контролировать наличие ошибок, проверяя
поток, как если бы он был логическим выражением:

    if (cin >> x) return;   // ввод в порядке
    ...     // здесь восстановление в случае ошибки
    if (!cout) errmsg("Ошибка вывода!");

     Эти примеры  подчеркивают  элегантность С++.  Класс ios
имеет следующие объявления функции operator:

    int operator! ();
    operator void* ();

     Операцияvoid*() определена  как "преобразующая" поток в
указатель,  который будет равен 0 (ложь),  если  установлены
failbit,  badbit или hardfail, и не-нулевому значению в про-
тивном случае.  (Отметим,  что возвращаемый указатель должен
использоваться только в логическихпроверках; другого практи-
ческого применения  он  не  имеет).  Перегруженная  операция
"не"   (!)  определена  как возвращающая не-нулевое значение
(истина),  если  установлены  биты  ошибки  потока  failbit,
badbit или hardfail;  в противном случае она возвращает ноль
(ложь).

Использование потоков прошлых версий

     Хотя библиотеки stream версий 1.x  и  iostreamверсии2.0
разделяют  многие имена классов и функцийи предлагают многие
аналогичные средства,  их структуры внекоторых важных облас-
тях несколько отличны друг отдруга.Turbo C++, следовательно,
реализует два потока с разными библиотеками  ифайлами  заго-
ловка.  Для работы целиком со старыми кодами,  использующими
потоки, вы должны включить файл stream.h, избегать включения
iostream.h  и  выполнять  компоновку  со  старой библиотекой
stream. Дополнительная информация о потоках версии 1.х нахо-
дится  в  файле OLDSTR.DOC.  Мы такжерекомендуем вам ознако-
миться с объявлениями и комментариями в stream.h.

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

 Рекомендации по переходу к потокам версии 2.0

     Ключевое различие между старыми и новыми классами пото-
ков состоит  в  том,  чтокомпоненты  public  старого  класса
streambuf  теперь,  в новом классе streambuf,  объявлены как
protected.  Если ваш старый код с потоками выполняет к таким
компонентам прямые ссылки, либо если у вас имеются производ-
ные от streambuf классы,  определенныена основаниитаких ком-
понентов,  то вы должны пересмотреть такие программы, прежде
чем они пойдут сбиблиотекой iostream. Другой аспект, способ-
ныйповлиять  на  совместимость,  состоит  в том,  что старый
streambufпрямоподдерживал использование  символьных массивов
для  форматирования  в оперативной памяти.  В случаеiostream
эта   поддержка   предполагается   в   производном    классе
strstreambuf, объявляемом в strstream.h.

     Старые конструкторы потока,  запускающие буферы файлов,
например

    istream instream(дескриптор_файла)4

 
                           - 170 -
 должны быть заменены на

    ifstream instream(дескриптор_файла);

 в программах с использованием iostream.

     Старые и новыеклассы потоков по-разному взаимодействуют
с  stdio.  Например,  stream.h  включает  stdio.h,  а старые
istream и ostream поддерживаютуказатели на  структуру  stdio
FILE. В случае iostream stdio поддерживается через специали-
зированный класс stdiostream, объявленный в stdiostream.h.

     В старой библиотеке stream предопределенные потоки cin,
cout и cerr связаны непосредственно с файлами структуры FILE
в stdio: stdin, stdout и stderr. Вслучаеiostream они подклю-
чаются  к дескрипторам файлов и используют различные страте-
гии буферизации.  Для того, чтобы избежать проблем с буфери-
зацией  при  смешанном  использовании кодов с stdout и cout,
можно записать:

    ios::sync_with_stdio();

     где выполняется подключение предопределенных  потоков к
файлам stdio в режиме без буферизации.  Отметим, однако, что
такой способ значительно замедляет работу cin,  cout и cerr,
соответственно.

     Старая библиотека   stream   позволяла  непосредственно
присваивать один поток другому; например,

    ostream outs; outs = cout;  // только для старых потоков

     В случае iostream допустимо присвоение только  потоку в
левой  части  выражения  присвоения;  другими словами,  типа
istream_withassign или ostream_withassign.  Если ваша  прог-
рамма содержит такие присвоения, то их можно либо переписать
с использованием ссылок или указателей, либо изменить объяв-
ления:

     ostream_withassign outs = cout; // только для новых по-
токов outs << i; // iostream

                            - 171 -
     Глава 4  Модели  памяти,  операции с плавающей точкой и
оверлеи


      Данная глава рассматривает три основных вопроса:

     - Модели памяти, от tiny до huge. Мы расскажем вам, что
они из себя представляют,  как выбрать модель памяти и зачем
может  понадобиться  (или  почему может не понадобиться) ис-
пользовать ту или иную конкретную модель памяти.

     - Опции операций с плавающей точкой.  Как и  когда  ис-
пользовать зти опции.

 - Оверлеи. Как они работают, и как их использовать.

Модели памяти

     Краткий обзор каждой модели памяти см.  на стр.194 ори-
гинала.

     Turbo C++ предоставляетшесть моделейпамяти,  каждая  из
которых  соответствует определенномутипу программы и размеру
кодов.  Каждая модель памяти по-своему работает  с  памятью.
Почему  необходимо разбираться в моделях памяти?  Для ответа
на этот вопрос следует рассмотреть систему компьютера, с ко-
торой  вы работаете.  В основе блока центрального процессора
системы лежит микропроцессор, принадлежащий к семейству мик-
ропроцессоров  Intel iAPx86;  это могут быть процессоры 8088
или 80286,  на также и 8086, 80186, 80386или 80486. Пока бу-
дем обозначать процессор как 8086. Регистры 8086

     Процессор 8086  содержит некоторый показанныйниже набор
регистров.  Существует,  помимо того,  еще один регистр - IP
(указатель команд) - однако TurboC++ не имеетвозможности не-
посредственного к нему доступа, и потому он не показан.


 Регистры общего назначения
 ___________________________________________________________

      --------------------------------
     AX \! AH \! AL \! сумматор (матем.операции
      --------------------------------
     BX \! BH \! BL \! базовый регистр (индексация)
      --------------------------------
     CX \! CH \! CL \! счетчик (индексация)
      --------------------------------
     DX \! DH \! DL \! данные (содердит данные)
      --------------------------------

Адресные сегментные регистры
      --------------------------------
  CS  \!      \! указатель кодового сегмента
      --------------------------------
  DS  \!      \! указатель сегмента данных
      --------------------------------
  SS  \!      \! указатель сегмента стека
      --------------------------------
  ES  \!      \! указатель вспомог. сегмента
      --------------------------------







 
                           - 172 -




      Регистры специального назначения
      --------------------------------
  SP  \!      \! указатель стека
      --------------------------------
  BP  \!      \! указатель базы
      --------------------------------
  SI  \!      \! исходный индекс
      --------------------------------
  DI  \!      \! индекс назначения
      --------------------------------

      Рис.4.1  Регистры 8086

 Регистры общего назначения
 ___________________________________________________________

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

     - Некоторые математические операции могут быть выполне-
ны только с помощью АХ.

     - ВХ можно использовать как индексный регистр.

     - СХ используется командой LOOP и некоторыми строковыми
командами.

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

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

 Сегментные регистры
 ___________________________________________________________

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

 Регистры специального назначения
 ___________________________________________________________

      8086 имеет несколько регистров специального назначения:

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

     - Регистр SP указывает на текущую вершину стека и часто
содержит смещение для регистра стека.

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

     Функции С используют в качестве базового  адреса  аргу-
ментов  и динамических локальных переменных регистр - указа-
 
                           - 173 -
тельбазы  (ВР).  Параметры  имеют   положительные   смещения
относительно ВР,  зависящие от модели памяти. ВР всегда ука-
зываетна предыдущеесохраненное значение ВР.Функции, не имею-
щие аргументов,  не используют и не сохраняют ВР, если опция
StandartStack Frame ("Стандартный стековый фрейм") находится
в состоянии Off.

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


 Регистр флагов
 ___________________________________________________________

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

      виртуальный режим 8086
      \! возобновление
      \! \!   вложенная задача
      \! \!   \!  уровень защищенного
      \! \!   \!  ввода/вывода
      \! \!   \!  \!  переполнение
      \! \!   \!  \!  \! признак
      \! \!   \!  \!  \! направления
      \! \!   \!  \!  \! \! прерывание
      \! \!   \!  \!  \! \! разрешено
      \! \!   \!  \!  \! \! \! внутреннее
      \! \!   \!  \!  \! \! \! прерывание
      \! \!   \!  \!  \! \! \! \! знак
      \! \!   \!  \!  \! \! \! \! \! ноль
      \! \!   \!  \!  \! \! \! \! \! \!перенос с
      \! \!   \!  \!  \! \! \! \! \! \!заемом
      \! \!   \!  \!  \! \! \! \! \! \!\!   четность
      \! \!   \!  \!  \! \! \! \! \! \!\!   \! перенос
      \! \!   \!  \!  \! \! \! \! \! \!\!   \!\!
 31 23      \!  \!15\! \!   \! \! \!\! 7 \!\!   \!0
 -----------------------------------------------------------
     \! \!  \!  \!  \!  \!  \! \! \! \! \! \! \! \! \!V\!R\!
\!N\!IOP\!O\!D\!I\!T\!S\!Z\! \!A\! \!P\! \!C\!
 -----------------------------------------------------------
  \_____________________________/ \_____/ \_________________/
  только 80386   80286    все процессоры 80х86
   80386

      Рис.4.2  Регистр флагов 8086


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

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

     Обычно регистр флагов не считываетсяи не модифицируется
непосредственно. Обычно обращения к этому региструвыполняют-
ся посредством специальных ассемблерных  команд  (таких  как
CLD,  STI и CMC), а такжепри помощи арифметических илогичес-
ких команд, модифицирующих конкретные флаги. Подобным же об-
разом, содержимое конкретных битов регистра флагов влияет на
работу таких команд, как JZ, RCRи MOVSB. Регистр флагов фак-
тическиникогда не используется как адрес памяти,  а содержит
данные о состоянии и управлении 8086.

      Сегментация памяти

     Микропроцессор Intel 8086 имеет сегментированную  архи-
тектуру памяти.  Он имеет общий объем памяти 1 Мб, но позво-
ляет одновременно адресовать  только  64  Кб  памяти.  Такой
участок памяти называется сегментом; отсюда и название "сег-
ментированная архитектура памяти".

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

     - 8086  имеет  четыре  16-битовых  сегментных  регистра
(один на каждый сегмент) с именами CS, DS, SS и ES; они ука-
зывают на кодовый сегмент,  сенгмент данных, стека и вспомо-
гательный сегмент, соответственно.

     - Сегмент может располагаться в произвольном адресе па-
мяти  - практически везде.  По причинам,  которые станут вам
ясны по мере ознакомления с материалом,  сегмент должен рас-
полагаться в памяти, начиная с адреса, кратного 16 (десятич-
ное).

 Вычисление адреса
 ___________________________________________________________

     Полный адрес в8086 состоит из двух 16-битовых значений:
адреса сегмента и смещения.  Предположим, что адрес сегмента
данных - т.е. значение в регистре DS -- равен 2F84 (шестнад-
цатиричное) и вы желаете вычислить фактический адрес некото-
рого элемента данных, который имеет значение 0532 (основание
16) от начала сегмента данных; как это сделать?

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

     Полученное 20-битовое  значениеи есть фактический адрес
данных, как показано ниже:

     регистр DS (после сдвига):0010 1111 1000  0100  0000  =
2F840 смещение: 0000 0101 0011 0010 = 00532
    --------------------------------------------------------
    Адрес:0010 1111 1101 0111 0010 = 2FD72

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

     Начальный адрес сегмента всегда является20-битовым чис-
лом,  но сегментный регистр имеет всего 16 битов  -  поэтому
младшие 4 бита всегда предполагаются равными нулю. Это озна-
 
                           - 175 -
чает - как было уже сказано - что начало сегмента можетнахо-
диться только в адресах памяти,  кратных 16, т.е. адресах, в
которых последние 4 бита (или один шестнадцатиричный разряд)
равен нулю.  Поэтому если регистр DS содержит значение 2F84,
тофактически сегмент данных начинается в адресе 2F840.

     Стандартная запись адреса имеет форму сегмент:смещение;
например, предыдущий адрес можно записать как 2F84:0532. От-
метим,  что поскольку смещения могут  перекрываться,  данная
пара сегмент:смещение неявляется уникальной; следующие адре-
са относятся к одной и той же точке памяти:

    0000:0123
    0002:0103
    0008:00A3
    0010:0023
    0012:0003

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

   Указатели

     Какое отношение имеют  указатели  к  моделям  памяти  и
Turbo C++? Самое непосредственное. Тип выбранной вами модели
памяти определяет тип по умолчанию  указателей, используемых
для кода и данных (хотявы можете явно объявить указатель или
функцию как имеющие тот или иной конкретный  тип, независимо
от  текущей модели памяти).  Указатели бывают четырех разно-
видностей:  near (16 битов),  far (32 бита),  huge (также 32
бита) и segment (16 битов).

 Ближние указатели (near)
 ___________________________________________________________

     16-битовый (near) указатель для вычисления полного  ад-
реса  связывается с одним из сегментных регистров; например,
указатель  функции  складывает  свое  16-битовоезначение  со
сдвинутым  влево содержимым регистра кодового сегмента (CS).
Аналогичным образом,  ближний указатель данных содержит сме-
щение в сегменте данных,  адресуемом регистром сегмента дан-
ных (DS).  С ближнимиуказателями  легкоманипулировать,  пос-
кольку   все   арифметические   операции  с  ним  (например,
сложение) можно выполнять безотносительно к сегменту.

 Дальние указатели (far)
 ___________________________________________________________

     Дальний (32-битовый)  указатель содержит не только сме-
щение относительно регистра,  но также и (в остальных 16 би-
тах) адрес сегмента, который затем должен быть сдвинут влево
и сложен со значением смещения. Использование дальних указа-
телей  позволяет  иметьв программе несколько кодовых сегмен-
тов; это, в свою очередь, позволяет программе превышать 64К.
Можно также адресовать более 64К данных.

     При использовании дальних указателей для адресации дан-
ных вам следует учитывать некоторые  потенциальные проблемы,
которые  могут возникать при манипулировании такими указате-
лями. Какобъяснялось в разделе, посвященном вычислениямадре-
са,  может существовать множество пар типа сегмент:смещение,
адресующих одну и ту же точку памяти. Например, дальние ука-
затели 0000:0120, 0010Ж0020 и 0012:0000 разрешаются к одному
и тому же 2-битовому адресу.  Однако, если у вас были бы три
 
                           - 176 -
переменных типа дальнего указателя - a,b и c, содержащих со-
ответственно три указанных значения,  то следующие выражения
все давали бы значение "ложь":

    if (a == b) . . .
    if (b == c) . . .
    if (a == c) . . .

     Аналогичная проблема  возникает,  когда  вам  требуется
сравнивать дальние указатели при помощи операций >,  >=, < и
<=.  В  этих  случаях  в сравнении участвует только смещение
(как unsigned); при указанных выше значениях a, b и cследую-
щие выражения дадут значения "истина":

    if (a > b) . . .
    if (b > c) . . .
    if (a > c) . . .

     Операции равенства  (==)  и неравенства (!=) используют
32-битовые значения как unsigned long (а не в  виде  полного
адреса памяти). Операции сравнения(<=, >=, < и >) используют
только смещение.

     Операции== и != требуют  все  32  бита,  что  позволяет
компьютеру  выполнять  сравнение  с пустым (NULL) указателем
(0000:0000). Если дляпроверки равенства использовалось толь-
ко  значение смещения,  то любой указатель со смещением 0000
будет равен пустому указателю,  что явно несовпадает с  тем,
что вы хотели получить.

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

     При сложении некоторого  значения  сдальним  указателем
изменяется только смещение. Если слагаемое настолько велико,
что сумма превышает FFFF (максимально возможная для смещения
величина),  то  указатель  перейдет снова к началу сегмента.
Например, если сложить 1 и 5031:FFFF, то результат будет ра-
вен 5031:0000 (а не 6031:0000). Подобным же образом, при вы-
читании 1 из 5031:0000 получится 5031:FFFF (а не 5030:000F).

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

 Указатели huge
 ___________________________________________________________

     Указатели huge также имеют размер 32 бита. как и указа-
тели far,  они содержат одновременно адрес сегмента и смеще-
ние. Однако, в отличие от дальних указателей, они нормализо-
ваны,  что позволяет избежать проблем,  связанных с дальними
указателями.

     Что такое  нормализованный  указатель?  Это  32-битовый
указатель,  который  содержитв  своем адресе сегмента макси-
мально возможное там значение. Поскольку сегмент может начи-
наться через каждые 16 байт (10 при основании 16), это озна-
чает, что величина смещения будет равна числу от 0 до 15 (от
0 до F с основанием 16).

     Для нормализации указателя он преобразуется к20-битово-
му адресу,  после чего правые 4 бита берутся в качестве сме-
щения, а левые 16 битов - как адрес сегмента. Например, ука-
затель 2F84:0532 можно сначала преобразовать  к  абсолютному
адресу  2FD72,  после  чего  получить нормализованный указа-
 
                           - 177 -
тель2FD7:0002.  Приведемеще ннесколько указателей с нормали-
зованными значениями:

    0000:01230012:0003
    0040:00560045:0006
    500D:9407594D:0007
    7418:D03F811B:000F

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

     1. Поскольку в таком случае любому заданному адресу па-
мяти соответствует только один возможный адрес в  виде  сег-
мент:смещение  типа huge.  Это означает,  что для указателей
huge операции == и !=  всегда  будут  возвращать  правильное
значение.

     2. Кроме того, операции >, >=, < и <= работают с полным
32-битовым значением указателя huge. Нормализация гарантиру-
ет в данном случае корректность результата.

     3. И наконец, вследствие нормализации смещение в указа-
теле huge выполняет автоматический переход через 16 но  вот-
личие от дальних указателей,  переход затрагивает и сегмент.
Например, при инкременте 811B:000Fрезультатбудет равен 811C:
0000; аналогичным образом, при декременте 800C:0000 получит-
ся 811B:000F.  Эта особенность указателей huge позволяет ма-
нипулировать  соструктурами данных сразмером более 64К.  Га-
рантируется,  например,  что  если  у  вас  имеется   массив
структур типа huge, превышающий 64К, индексация этого масси-
ва и выбор поля структуры всегда будут выполняться  правиль-
но, независимо от размера структуры.

     Использование указателей huge имеет свою цену: увеличе-
ние времени обработки. Арифметические операции с указателями
huge выполняются при помощи обращений к специальным подпрог-
раммам. Вследствие этого арифметические вычисления занимают-
существенно  больше  времени по сравнению с вычислениями для
указателей far или near.

     Шесть моделей памяти

     Turbo C++ работает  с  шестью  моделями  памяти:  tiny,
small,  medium,  compact,  large и huge. выбор модели памяти
определяется требованиями  вашей  программы.Ниже  приводятся
краткие описания каждой из них:

 Tiny

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

     Как вы  уже  поняли,  это  минимальная из моделей памя-
ти.Все четыре сегментных регистра (CS, DS, SS и ES) устанав-
ливаются на один и тот же адрес, что дает общий размер кода,
данных и стека, равный 64К. Используютсяисключительноближние
указатели. Программы с моделью памяти tuny могут быть преоб-
разованы к формату .COM при компоновке с опцией /t.

 Small
     Эта модель  хорошо  подходит  для  небольших прикладных
программ.

     Сегменты кода и данных  расположены  отдельно  друг  от
друга и не перекрываются, что позволяет иметь 64К кода прог-
раммы и 64К данных и стека. Используются только ближние ука-
 
                           - 178 -
затели.

 Medium

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

     Для кода,  но не для данных используются дальние указа-
тели. В результате данные плюс стек ограничены размером 64К,
а код может занимать до 1 Мб.

 Compact

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

     Ситуация, противоположная  относительно  модели Medium:
дальние указатели используются для данных,  но не для  кода.
Следовательно,  код здесь ограничен 64К, а предельный размер
данных - 1 Мб.

 Large

     Модели large иhuge применяются только в  очень  больших
программах.

     Дальние указатели  используются  как  для кода,так идля
данных, что дает предельный размер 1 Мб для обоих.

 Huge

     Дальние указатели используютсякак для кода,  так и  для
данных.  Turbo C++ обычно ограничиваетразмерстатических дан-
ных 64К;модельпамятиhuge отменяетэто  ограничение,  позволяя
статическим данным занимать более 64К.

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


     Следующие иллюстрации(Рис.4.3 - 4.8) показывают,как вы-
полняется распределение памяти для всех шести моделей памяти
Turbo C++.






















 
                           - 179 -

     Сегментные регистры:   Размер сегмента:
  Младший ^  CS,DS,SS-----> ---------------------------
   адрес  |  / \! _TEXT класс 'CODE'   \! \
  | |  \!код      \! |
  | |  \!-------------------------\! |
  | |  \! _DATA класс 'DATA'   \! |
  | |  \!инициализированные данные\! |
  | |  \!-------------------------\! |
  | |  \! _BSS класс 'BSS'     \! |
  |DGROUP/   \!неинициализирован. данные\!  \ до 64К
  |\   \!-------------------------\!  /
  | |  \!  куча|      \! |
  | |  \!v      \! |
  | |  \!-------------------------\! |  Свободная
  | |  \!#########################\!-|--область
  |SP(TOS)--|->\!-------------------------\! |  памяти
  | |  \!^      \! |
  Старший |  \ \!  стек|      \!/
   адрес  v  Начало SP----> ---------------------------


      Рис.4.3  Сегментация для модели памяти tiny



     Сегментные регистры:   Размер сегмента:
  Младший ^  CS-----------> ---------------------------
   адрес  |    \! _TEXT класс 'CODE'   \!
  |    \!код      \!  до 64К
  |  DS,SS--------> \!-------------------------\!
  |  / \! _DATA класс 'DATA'   \!\
  | |  \!инициализированные данные\! |
  | |  \!-------------------------\! |
  | |  \! _BSS класс 'BSS'     \! |
  |DGROUP/   \!неинициализирован. данные\!  \ до 64К
  |\   \!-------------------------\!  /
  | |  \!  куча|      \! |
  | |  \!v      \! |
  | |  \!-------------------------\! |  Свободная
  | |  \!#########################\!-|--область
  |SP(TOS)--|->\!-------------------------\! |  памяти
  | |  \!^      \! |
  |  \ \!  стек|      \!/
  |  Начало SP----->\!--------------------------
  |    \!  дальняя|      \! До конца
  |    \!  кучаv      \! памяти
  |    \!-------------------------\!    Свободная
  Старший |    \!#########################\!----область
   адрес  v    ---------------------------    памяти

      Рис.4.4  Сегментация для модели памяти small















 
                           - 180 -






Сегментные регистры:      Размер сегмента:
  Младший ^    ---------------------------
   адрес  |    \!    _TEXT класс 'CODE'\!  до 64К
  |    \! sfileкод      \!каждый sfile
  |  DS,SS--------> \!---/---------------------\!
  |  / \!  / _DATA класс 'DATA'   \!\
  |  Несколько __|__\!_/инициализирован. данные\! |
  |  --------- |  \!-------------------------\! |
  |  \!sfile A\! |  \! _BSS класс 'BSS'     \! |
 CS->\!sfile B\! |  \!неинициализирован. данные\!  \ до 64К
  |  \!     \! |  \!-------------------------\!  /
  |  \!sfile Z\! |  \!  куча|      \! |
  |  --------- |  \!v      \! |
  |DGROUP/   \!-------------------------\! |  Свободная
  |\   \!#########################\!-|--область
  |SP(TOS)--|->\!-------------------------\! |  памяти
  | |  \!^      \! |
  |  \ \!  стек|      \!/
  |  Начало SP----->\!--------------------------
  |    \!  дальняя|      \! До конца
  |    \!  кучаv      \! памяти
  |    \!-------------------------\!    Свободная
  Старший |    \!#########################\!----область
   адрес  v    ---------------------------    памяти

      Рис.4.5  Сегментация для модели памяти medium

      CS указывает одновременно только на один sfile.




     Сегментные регистры:   Размер сегмента:
  Младший ^  CS-----------> ---------------------------
   адрес  |    \! _TEXT класс 'CODE'   \!
  |    \!код      \!  до 64К
  |  DS ----------> \!-------------------------\!
  |  / \! _DATA класс 'DATA'   \!\
  | |  \!инициализированные данные\! |
  |DGROUP/   \!-------------------------\!  \ до 64К
  |\   \! _BSS класс 'BSS'     \!  /
  | |  \!неинициализирован. данные\! |
  |  \ \!      \!/
  |  SS --------->  \!-------------------------\!  Свободная
  |    \!#########################\!----область
  |SP(TOS)---->\!-------------------------\!    памяти
  |    \!^      \!
  |    \!  стек|      \! до 64К
  |  Начало SP----->\!--------------------------
  |    \!  куча|      \! До конца
  |    \!v      \! памяти
  |    \!-------------------------\!    Свободная
  Старший |    \!#########################\!----область
   адрес  v    ---------------------------    памяти

      Рис.4.6  Сегментация для модели памяти compact





 
                           - 181 -



     Несколько
     ---------
     \!sfile A\!
 CS->\!sfile B\!
     \!     \!
     \!sfile Z\!<---
     ---------  \
   \
    \
     \
      \
Сегментные регистры:   \      Размер сегмента:
  Младший ^    ----\----------------------
   адрес  |    \! \  _TEXT класс 'CODE'\!  до 64К
  |    \! sfileкод      \!каждый sfile
  |  DS ----------> \!-------------------------\!
  |  / \! _DATA класс 'DATA'   \!\
  | |  \!  инициализирован. данные\! |
  |DGROUP/   \!-------------------------\!  \ до 64К
  |\   \! _BSS класс 'BSS'     \!  /
  | |  \!неинициализирован. данные\! |
  |  \ \!      \!/
  |  SS --------->  \!-------------------------\!  Свободная
  |    \!#########################\!----область
  |SP(TOS)---->\!-------------------------\!    памяти
  |    \!^      \!
  |    \!  стек|      \! до 64К
  |  Начало SP----->\!--------------------------
  |    \!  куча|      \! До конца
  |    \!v      \! памяти
  |    \!-------------------------\!    Свободная
  Старший |    \!#########################\!----область
   адрес  v    ---------------------------    памяти

      Рис.4.7  Сегментация для модели памяти large





























 
                           - 182 -

     Несколько
     ---------
     \!sfile A\!
 CS->\!sfile B\!
     \!     \!
     \!sfile Z\!<---
     ---------  \
   \
    \
     \
      \
Сегментные регистры:   \      Размер сегмента:
  Младший ^    ----\----------------------
   адрес  |    \! \  _TEXT класс 'CODE'\!  до 64К
  |    \! sfileкод      \!каждый sfile
  |    \!-------------------------\!
  | Несколько  \!     \!
  | ---------  \!     \!
  | \!sfile A\!  \!     \!
  |  DS->\!sfile B\!<-\!sfile _DATA класс 'DATA' \!   до 64К
  | \! \!  \!неинициализирован. данные\! каждый sfile
  | \!sfile Z\!  \!      \!
  |  SS --------->  \!-------------------------\!  Свободная
  |    \!#########################\!----область
  |SP(TOS)---->\!-------------------------\!    памяти
  |    \!^      \!
  |    \!  стек|      \! до 64К
  |  Начало SP----->\!--------------------------
  |    \!  куча|      \! До конца
  |    \!v      \! памяти
  |    \!-------------------------\!    Свободная
  Старший |    \!#########################\!----область
   адрес  v    ---------------------------    памяти

      Рис.4.8  Сегментация для модели памяти huge


     Таблица 4.1.  суммирует различные модели,  а также  ре-
зультаты их сравнения друг с другом.  Эти модели часто груп-
пируются в соответствии с тем,  насколько малы (64К) или ве-
лики  (1М)  размеры  их  модулей  кода и данных;  Эти группы
соответствуют рядами колонкам табл. 4.1.

 -----------------------------------------------------------
 Размер \!       Размер кода
 данных \! -------------------------------------------------
\!   64 K      \!1 Mb
 -----------------------------------------------------------
\!  Tiny (данные, коды     \!
  64K\!  перекрываются     \!
\!  максимальный размер 64К)  \!
\!     \!
\!   Small (без перекрытия    \!   Medium (данные - small
\!  максимальный размер 128К) \!   коды - large)
\!     \!
 ------------------------------------------------------------
  1Mb\!   Compact ( данные - large \!  Large  ( данные и коды
\!      коды -  small) \!     large)
\!     \!
\!     \!   Huge  (также как и large,
\!     \!  но cтатические
\!     \!  данные  >64 K )
 ------------------------------------------------------------



 
                           - 183 -



 Важно!
     Когда Выкомпилируете  модуль (исходный файл с некоторым
количеством процедур в нем), то результирующий код для этого
модуля  не может превышать 64К,  т.к.  он должен вмещаться в
один кодовый сегмент.  Это остается правилом,  даже если  Вы
используете  один из больших кодовых модулей (medium, large,
huge).  Если ваши модули очень велики ( > 64К ),  Вы  должны
разбить их на несколько маленьких исходных файлов,  компили-
ровать их раздельно, а потом собирать в один файл. Аналогич-
ным образом, несмотря на то, что huge модель позволяет набо-
ру статических данных превышать размер 64 К,  все  равно  он
должен быть < 64K в каждом модуле.



  Программирование с использованием различных моделей памяти:
    адресные модификаторы

     Turbo C++ вводит 8 новых ключевых слов,  не имеющихся в
стандартном ANSI C ( near,  far,  huge,  _cs, _ds, _es, _ss,
_seg), которые могут использоваться в качестве модификаторов
адресных  указателей (а иногда и функций) с некоторыми огра-
ничениями.
     В Turbo  C ++ вы можете модифицировать объявления функ-
ций и адресных указателей с помощью ключевых слов near, far,
huge. Мы уже объяснили смыслуказателей near, far, huge ранее
в этой главе. near-функции вызываются near-вызовами с после-
дующим near-выходом из них.  Аналогичным образом far-функции
вызываются far-вызовами с последующим  far-выходом  из  них.
huge-функции аналогичны far-функциям,за исключением того,что
huge-функции устанавливают регистр DS в новое значение.
     Кроме того  имеются  четыре специальных near-указателей
данных:  _cs, _ds, _es, _ss. Это шестнадцатибитовые указате-
ли, которые специально ассоциируются с соответствующими сег-
ментными регистрами,  например,  если бы вы должны были объ-
явитьуказатель равным:

      char _ss *p;

     то рсодержал   бы   в  этом  случае  шестнадцатибитовое
смещение в сегменте стека.

     Существует некоторое ограничение на использование  сег-
ментных указателей:

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

     - Когда  сегментные  указатели используются в косвенном
выражении они также преобразуются в far-указатели.

     - Также как и расширениек двоичному +  оператору,  если
сегментный указатель прибавляется к near-указателю,  резуль-
татом будет far-указатель,  который формируется путем взятия
сегмента из сегментного указателя исмещения из near-указате-
ля.  Такая операция разрешается,  только если оба  указателя
указывают на одинаковый тип, илиесли один из указателей ука-
зывает на тип void.
     - Указатели  сегментов  можно сравнивать.  Сравнение их
выполняется таким образом, как если бы их значения имели це-
лочисленный тип unsigned.
 
                           - 184 -

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

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

 Типы указателей       Таблица 4.2
 -----------------------------------------------------------
     Модель памяти Указатели функции Указатели данных
 -----------------------------------------------------------
     Tiny near, _csnear, _ds
     Small near, _csnear, _ds
     Medium farnear, _ds
     Compact near, _csfar
     Large farfar
     Huge farfar
 -----------------------------------------------------------

     Указатели данных могут быть также объявлены с модифика-
тором _seg. Это 16-битовые указатели сегмента.

    Объявление ближних или дальних функций

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

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

    double  power(double x,int exp)
    (*
       if (exp <= 0)
  return(1);
       else
  return(x * power(x, exp-1));
    *)

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

    double near power(double x,int exp)

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

     Это означает,  что при использовании большой модели па-
мяти (medium, large или huge) power можно вызывать только из
того модуля, в котором она определена.Прочиемодулиимеют свои
собственные кодовые сегменты и  не  могут  вызывать  функции
near из других модулей. Более того, ближняя функциядо перво-
го кней обращения должна быть либоопределена,  либо объявле-
на,  иначе  компилятор не знает о необходимости генерировать
ближний вызов.

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

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

   Объявление указателей near, far или huge

     Только что были рассмотрены случаи, в которых может по-
надобиться объявить функциюс другой моделью  памяти,  нежели
остальная  часть программы.  Зачем то же самое может понадо-
биться для указателей? По тем же причинам, что и дляфункций:
либодля улучшения характеристик быстродействия (объявив near
там,  где по умолчанию было бы far), либо для ссылки за пре-
делы сегмента по умолчанию (объявив far илиhuge там,  где по
умолчанию бывает near).

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

    void myputs(s)
    char *s;
    (*
       int i;
       for (i = 0; s[i] != 0; i++) putc(s[i]);
    *)

    main()
    (*
       char near *mystr;
       mystr = "Hello, world\n";
       myputs(mystr);
    *)




     Эта программа работаетудовлетворительно,  хотя объявле-
ние mystr как near избыточно,  поскольку все указатели,  как
кода, так и данных, будут near по умолчанию.

     Однако, что  произойдет,  если  перекомпилировать   эту
программу  с  моделью  памяти compact (либо large или huge)?
Указатель mystr в main останется ближним (16-битовым). Одна-
ко, указатель s в myputs теперь будет far, поскольку умолча-
нием теперь будет являться far.  Это означает,  что  попытка
создания  дальнего  указателяприведет к снятию со стека двух
слов, и полученный таким образом адрес, безусловно, не будет
являться адресом mystr.

     Как избежать этой проблемы? Решение состоит втом, чтобы
определить myputs в современном стиле С:

    void myputs(char *s)
    (*
       /* тело myputs */
    *)

     Теперь при  компиляции вашей программы Turbo C++ знает,
что myputs ожидает указатель на char; и поскольку компиляция
выполняется с моделью large, то известно, что указатель дол-
жен быть far. Вследствие этого Turbo C++ поместит в стек ре-
гистр сегмента данных (DS) и 16-битовоезначение mystr, обра-
 
                           - 186 -
зуя тем самым дальний указатель.

     Если вы собираетесь явнообъявлять указатели  как farили
near,не забывайте использовать прототипы тех функций,  кото-
рые могут использовать эти указатели.

     Как быть в обратном случае: когда аргументы myputs объ-
явлены  как  far,  а компиляция выполняется с моделью памяти
small? И в этом случае без прототипа функции у вас возникнут
проблемы, поскольку main будет помещать в стек и смещение, и
адрес сегмента, тогда как myputs будет ожидать приема только
одного смещения.  При наличии определений функций в прототи-
пах main будет помещать в стек только смещение.

 Создание указателя данного адреса сегмент:смещение
 ___________________________________________________________
     Как создать дальний указательна конкретный адрес памяти
(конкретный адрессегмент:смещение)? Для этого можно восполь-
зоваться встроеннойбиблиотечной подпрограммой MK_FP, которая
в качестве аргументапринимает сегмент и смещение,  и возвра-
щает дальний указатель. Например,

    MK_FP(segment_value, offset_value)

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

Использование библиотечных файлов

     Turbo C++ предлягает для каждой из шести моделей памяти
собственную версию библиотеки стандартных подпрограмм. Turbo
C++ при этом проявляет достаточно  "интеллекта",  чтобы  при
компоновке брать нужные библиотеки и в нужной последователь-
ности,  в зависимости от выбранной вами модели памяти. Одна-
ко,при непосредственном использовании компоновщика Turbo C++
TLINK (как автономного компоновщика) вы должны  явно  указы-
вать  используемые библиотеки.  Более подробно это описано в
разделе TLINK Главы 5Б "Утилиты",  Руководства пользователя.

 Компоновка смешанных  модулей

     Что произойдет,если вы компилируете один модуль  с  ис-
пользованием модели памяти small,  второй - модели large,  и
затем хотите скомпоновать их? Что при этом произойдет?

     Файлы скомпонуются удовлетворительно,  но при  этом  вы
встретитесь с проблемами, которые будут аналогичны с описан-
ными выше в разделе "Объявление функций как near  или  far".
Если функция модуля с моделью small вызывает функцию в моду-
ле с моделью large,  она будет использовать при этом ближний
вызов,  что даст абсолютно неверные результаты.Кроме того, у
вас возникнут проблемы с указателями,  описанные  в  разделе
"Объявление  указателей как near,  far или huge",  поскольку
функция в модуле small ожидает, что принимаемые и передавае-
мые ейуказатели будут near,  тогдакак функция в модуле large
ожидает рабрту с указателями far.

     И снова решение заключается в  использовании прототипов
функций.  Предположим,  что  вы поместили myputs в отдельный
модуль и скомпилировали его с моделью памяти large. Затем вы
создаете файл заголовка myputs.h (либо с любым другим именем
и расширением .h), который содержит следующий прототип функ-
ции:

 
                           - 187 -
    void far myputs(char far *s);

     Теперь, если поместить main в отдельный модуль (MYMAIN.
C) и выполнить следующие установки:

    #include <stdio.h>
    #include "myputs.h"

    main()
    (*
       char near *mystr;
       mystr = "Hello, wirld\n";
       myputs(mystr);
    *)

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

     Что произойдет,  если помимо этого вам требуется компо-
новка  с  библиотечными подпрограммами?  Лучший подход здесь
заключается в том, чтобы выбрать одну из библиотек с моделью
large и объявить все как far.  Для этого сделайте копии всех
файлов заголовка,  которые вы обычно включаете  (таких,  как
stdio.h) и переименуйте эти копии (например, fstdio.h).

     Затем отредактируйте копии прототипов функций таким об-
разом, чтобы там было явно указано far, например:

    int far cdecl printf(char far* format, ...);

     Тем самым, не только вызовы подпрограмм будут дальними,
но  и  передаваемые указатели также будут дальними.Модифици-
руйте вашу программу таким образом, чтобы она включала новый
файл заголовка:

    #include <fstdio.h>

    main()
    (*
       char near *mystr;
       mystr = "Hello, world\n";
       printf(mystr);
    *)

     Скомпилируйте вашу программу при помощиTCC, затем ском-
понуйте  ее  при помощью TLINK,  указав библиотеки с моделью
памяти large,  напрмер CL.LIB.  Смешиваниемодулей с  разными
моделями - вещь экстравагантная,  но допустимая; будьте, од-
нако,  готовы к тому,  чтолюбые неточности здесь приводят  к
ошибкам,  которые очень трудно найти и исправиь при отладке.

      Опции типа чисел с плавающей точкой
 ------------------------------------------------------------

     С работает с двумя числовыми  типами:интегральным (int,
short,  long  и  т.д.)  и с плавающей точкой (float double и
long double). Процессор вашего компьютер легко справляется с
обработкой чисел интегральных типов,  однако числа с плаваю-
щей точкой отнимают больше времени и усилий.

     Однако, семейство процессоров iAPx86 имеет  сопутствую-
щееему семейство математических сопроцессоров, 8087, 80287 и
80387. Мы будем обозначать все семейство математических соп-
роцессоров 80x87 термином "сопроцессор".
 
                           - 188 -

      В   случае   процессора80487вы   имеете  математический
 сопроцессор уже встроенным в основной.

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

Эмулирование платы 80х87

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

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

Получение кода только для машин с 80х87

     Если вы планируете использовать вашу программу исключи-
тельно на машинах с установленным математическим сопроцессо-
ром  80х87,  то можно сэкономить около 10К памяти программы,
опустив из нее логику автоматического  определения  присутс-
твия 80х87 и эмулятора.  Для этого следует просто выбратьоп-
цию генерации кодаопераций с плавающей точкой при наличии 80
х87 (или опциюкомпилятора командной строки -f87).  Turbo C++
в  этом  случае  скомпонует  вашу  программу  с  библиотекой
FP87.LIB вместо EMU.LIB.

Получение кода без операций с плавающей точкой

     При отсутствии  в программе операций с плавающей точкой
вы можете сэкономить немного времени компиляции,  выбрав оп-
цию  генерации операций с плавающей точкой None ("отсутству-
ют") (или опцию компилятора  командной  строки  -f-).  Тогда
Turbo  C++ не будет выполнять компоновку ни с EMU.LIB,  ни с
FP87.LIB, ни с MATHx.LIB.

Опция быстрых вычислений с плавающей точкой

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

    double x;
    x = (float)(3.5*x);

     Для вычисления по обычным правилам x умножается на 3.5,
давая точность результата double,которая затем  усекается до
точности float, после чего x записывается какdouble. При ис-
пользовании опции быстрых вычислений с плавающей точкой про-
изведение  типа  long  double преобразуетсянепосредственно в
double. Поскольку лишь очень немногие программы чувствитель-
 
                           - 189 -
ны  к  потере  точностипри преобразовании от более точного к
менее точному типу с плавающей точкой, то данная опция явля-
ется умолчанием.
       Переменная операционной среды 87

     При построении программы с эмуляцией 80x87, которая ус-
танавливается по умолчанию,  ваша программа станет автомати-
чески проверять наличие 80х87 и использовать  его,  если  он
установлен в машине.

     Существует ряд  ситуаций,  в  которых вам может понадо-
биться отменить режим  автоматического  определения  наличия
сопроцессора по умолчанию.  Например, вашасобственная испол-
няющая система можетиметь 80х87, но вам требуется проверить,
будет ли программа работать так, как вы предполагали, в сис-
темебез соопроцессора. Либоваша программа предназначена для-
работыв системе, совместимой с PC, но данная конкретная сис-
тема возвращает логике автоматического  определения  наличия
сопроцессора  неверную информацию (либо при оссутствии 80х87
говорит, что он на месте, либо наоборот).

     Turbo C++ имеет опцию для переопределения логики  опре-
деления  наличия сопроцессорапри загрузке программыж эта оп-
ция - соответствующая переменная операционной  среды системы
87.

     Переменная операционной  среды  87  устанавливается  по
приглашению DOS при помощи команды SET:

    C>SET 87=N

 или

    C>SET 87=X

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

     Установка переменной операционной среды в Y ("Да")  оз-
начает,  что сопроцессор на месте и вы желаете,  чтобы прог-
рамма его использовала.Программист должен  знать  следующее:
!!! Если установить 87=Y, а физически 80х87 в системе не ус-
тановлен, то система повиснет.

     Если переменная операционной среды 86  была  определена
(в  любоезначение),  и вы желаете сделать ее неопределенной,
введите на приглашение DOS:

    C>SET=

     Непосредственно после знака равенстванажмите  Enter,  и
переменная 87 станет неопределенной.

       Регистры и 80х87

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

     1. В  режиме  эмуляции  80Х87 циклический переход в ре-
гистрах,  а также ряд других особенностей 80х87 не поддержи-
вается.

     2. Если  вы  смешиваете  операции  с плавающей точкой и
 
                           - 190 -
встроенные коды на языке ассемблера,  то  при  использовании
регистров вы должны принимать некоторые меры предосторожнос-
ти. Это связано с тем, что набор регистров 80х87 перед вызо-
вом  функции  в Turbo C++ очищается.  Вам может понадобиться
извлечь из стека и сохранить регистры 80х87 до вызова  функ-
ции,  использующей сопроцессор, если вы не уверены, что сво-
бодных регистров достаточно.

 Отмена обработки особых ситуаций
 для операций с плавающей точкой   -------------------------

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

    #include <floar.h>
    main() (*
       _control87(MCW_EM,MCW_EM);
       ...
    *)

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

     Определенные математические ошибки могуттакже произойти
в  библиотечных функциях;  например,  при попытке извлечения
квадратного корня из отрицательного числа. Поумолчанию в та-
ких случаях выполняется вывод на экран сообщений об ошибке и
возврат значения NAN (код IEEE "not-a-number -- "не-число").
Использование  NAN скорее всего приведет далее к возникнове-
нию особой ситуации с плавающей точкой,  которая в свою оче-
редь вызовет , если она не замаскирована, аварийное прерыва-
ние программы. Если вы не желаете, чтобысообщение выводилось
на   экран,  вставьте  в  программу  соответствующую  версию
matherr.

    #include <math.h>
    int cdecl matherr(struct exception *e)
    (*
       return 1;     /* ошибка обработана */
    *)

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

     Комплексными называются числа вида x +yi,  где x и yэто
действительные  числа,  а  i это корень квадратный из -1.  В
Turbo C++ всегда существовал тип

    struct complex
    (*
       double x, y;
    *);

     определенный в math.h.  Этот тип удобен для представле-
ния комплексных чисел,  поскольку их можно  рассматривать  в
качестве  пары действительных чисел.  Однако,  ограничения С
делают арифметические операции с комплексными  числами  нес-
колько  громоздкими.  В  С++ операции с комплексными числами
выполняются несколько проще.
 
                           - 191 -

     Для скомплексными числами  в  С++  достаточно  включить
complex.h. В complex.h для обработкикомплексных числе перег-
ружены:

 - все обычные арифметические операции

 - операции потоков >> и <<

 - обычные арифметические функции, такие как sqrt и log

     Дополнительную информацию см. в описании класса complex
в Справочнике по библиотеке.

     Библиотека complex  активизируется  только  при наличии
аргументов типа complex.  Таким образом, для получении комп-
лексного квадратного корня из -1 используйте

    sqrt(complex(-1))

 а не

    sqrt(-1)

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

    #include <complex.h>
    // вычисление дискретного преобразования Аурье для
    // a[0],...,a[n-1]
    void Fourier(int n, complex a[], complex b[])
    (*
       int j,k;
       complex i(0,1);   // корень квадратный из -1
       for (j = 0; j < n; ++j)
       (*
  b[j] = 0;
  for (k = 0; k < n; ++k)
     b[j] += a[k] * exp(2*M_PI*j*k8i/n);
  b[j] /= sqrt(n);
       *)
    *)
       Использование двоично-десятичной (BCD) математики

     Turbo C++, также как и большинство прочик компьютеров и
компиляторов,  выполняет математические вычисления с числами
в двоичном представлении (то есть в системе счисления с  ос-
нованием 2). Это иногда путает людей, привыкших исключитель-
но к десятичной математике (в системе счисления с основанием
10).  Многие числа с точным представлением в десятичной сис-
теме счисления,  такиекак 0.01,в двоичной системе  счисления
могут иметь лишь приближенные представления.

     Двоичныечисла предпочтительныв  большинстве  прикладных
программ,  однако внекоторых ситуациях ошибка  округления  в
преобразованиях  меджу системами счисления с основаниями 2 и
10 нежелательна. Наиболее характерным случаев здесь являются
финансовые  или учетные задачи,  где предполагается сложение
центов.  Рассмотрим программу,  складывающую до 100 центов и
вычитающую доллар:






 
                           - 192 -




    #include <stdio.h>
    int i;
    float x =0.0;
    for (i = 0; i < 100; ++i)
       x += 0.01;
    x -= 1.0;
    print("100*.01 - 1 = %g\1",x);

     Правильным ответом является 0.0, однако ответ, получен-
ный данной программой, будет малой величиной, близкой к 0.0.
При вычислении ошибка округления,  возникающая во время пре-
образования 0.01 в двоичное число,  накапливается. Изменение
типа  x  на double или long double только уменьшаетошибкувы-
числения, но не устраняет ее вообще.

     Для решения этой проблемы Turbo C++ предлягаетспецифич-
ный  для  C++  типbcd  (двоично-десятичный),  объявленный  в
bcd.h. В случае двоично-десятичного представления число 0.01
будет  иметь  точное представление,  а переменная x типа bcd
даст точное исчисление центов.

    #include <bcd.h>
    int i;
    bcd x = 0.0;
    for (i = 0; i < 100; ++i)
       x += 0.01;
    x -= 1.0;
    cout << "100*0.1 - 1 = " << x << "\n";

     При этом необходимо учитывать следующие особенности ти-
па bcd:

     - bcd не уничтожает ошибку округления вообще.  Вычисле-
ние типа 1.0/3.0 все равно будет иметь ошибку округления.

     - Обычные математические функции, такие как sqrt и log,
для аргументов с типом bcd перегружаются.

     - Числа  типа bcd имеют точность представления около 17
разрядов и -125 125 диапазон принимаемых значений от 1x10 до
1x10.


   Преобразования двоично-десятичных чисел
 ___________________________________________________________

     bcd - это определяемый тип,  отличный отfloat,doubleили
long double;десятичная арифметика выполняется  только  когда
хотя бы один операнд имеет тип bcd.

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

     Для преобразования  двоично-десятичногочисла обратно, к
обычной системе счисления с основанием 2 (тип  float, double
или  long  double),  служит  функция-компонент  real  класса
bcd;это преобразование не  выполняется  автоматически.  real
выполняет все необходимые преобразования к long double,  ко-
торый может быть затем преобразован к другим типам при помо-
щи обычных средств С. Например,

    bcd a = 12.1;

     можно вывести при помощи любой из приводимых ниже строк
 
                           - 193 -
кода:

    double x = a; printf("a = %g", x);

    printf("a = %Lg", real(a));

    printf("a = %g", (double)real(a));

    cout << "a =" << a;

     Отметим,что посколькуprintfне выполняет  контроль  типа
аргументов,  спецификатор формата должен иметь L, если пере-
дается значение real(a) типа long double.

    Число десятичных знаков
 ___________________________________________________________

     Вы можете   задать,   сколькодесятичных  знаков  должно
участвовать в преобразовании из двоичного типа  в  bcd.  Это
число является вторым, опциональным аргументом в конструкто-
ре bcd.  Например,  для преобразования $1000.00/7в bcd-пере-
менную, округленную до ближайшего цента, можно записать:

    bcd a = bcd(1000.00/7, 2)

     где 2  обозначает  два  разряда после десятичной точки.
Таким образом,

    1000.00/7=   142.85714
    bcd(1000.00/7, 2)=   142.860
    bcd(1000.00/7, 1)=   142.900
    bcd(1000.00/7, 0)=   142.000
    bcd(1000.00/7, -1)=   140.000
    bcd(1000.00/7, -2)=   100.000

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

    bcd(12.335, 2)=   12.34
    bcd(12.245, 2)=   12.34
    bcd(12.355, 2)=   12.36

Использование оперативной памяти Turbo C++

     При компиляции  Turbo C++ не генерирует каких-либо про-
межуточных структур данных на диске (Turbo C++ пишет на диск
только .OBJ-файлы);  между проходами он помещает промежуточ-
ные структуры данных в оперативную память.  Вследствие этого
вы можете получить сообщение"Out of memory...",  что означа-
ет, что компилятору не хватает памяти.

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

Оверлеи (VROOMM)

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

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

  Работа программ с оверлеями

     Программа управления оверлеями (VROOMM, или Virtual Run
-time Object-Oriented Memory Manager) очень сложна;  она вы-
полняет за вас большую часть работы по организации оверлеев.
В обычных оверлейных системах модули группируются  в базовый
и набор оверлейных блоков.  Подпрограммы в данном оверлейном
блоке могут вызывать подпрограммы из этого жеблока и из  ба-
зового блока,  но не из других блоков.  Оверлейные блоки пе-
рекрываются друг с другом;  т.е. одновременно в памяти может
находиться только один оверлейный блок,  и все они при акти-
визации занимаютодин и тот же участок физической памяти. Об-
щий  объем памяти,  необходимой длязапуска данной программы,
определяется размером базового,  плюс максимального оверлей-
ного блока.

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

     Схема VROOMM совершенно иная.  Она обеспечивает динами-
ческий  свопинг сегментов.  Базовым блоком свопинга является
сегмент. Сегмент может состоять из одного или нескольких мо-
дулей.  И что еще более важно,  любой сегмент может вызывать
любой другой сегмент.

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

     Что происходит,  когдавозникает необходимость поместить
сегмент в область свопинга? Если эта область имеет достаточ-
но  свободного  места,  то данная задача выполняется просто.
Если же нет,  то из области свопинга должно  быть  выгружено
один или более сегментов, чтобы искомая свободнаяобласть ос-
вободилась.Как выбрать сегменты  для  выгрузки?  Действующий
здесь  алгоритм очень сложен.  Упрощенная версия его такова:
если в области свопинга имеется  неактивный  сегмент,то  для
выгрузки выбираетсяон. Неактивными считаются сегменты, в ко-
торых в текущий момент нет выполняемых  функций.В  противном
случае берется активный сегмент. Удаление сегментов из памя-
ти продолжаетсядо тех пор,  пока в области свопинга не обра-
зуется  достаточно свободнойпамятидля размещения там требуе-
могосегмента. Такой метод называется динамическим свопингом.

     Чем больше памяти выделено для  области  свопинга,  тем
лучше работает программа. Область свопинга работает как кэш-
память:  чем больше кэш, тем быстрее работает программа.Наи-
лучшие значения размера области свопинга определяются разме-
рами рабочего множества данной программы.

     После загрузки оверлеяв память он помещается в оверлей-
ный буфер, который расположен в памяти между сегментом стека
 
                           - 195 -
и дальней кучей. По умолчаниюразмероверлейного буфера вычис-
ляется и устанавливается при загрузке программы, но его мож-
но изменить при помощи глобальной переменной _ovrbuffer. Ес-
лидостаточный  размер памяти недоступен,  то появляется либо
сообщение об ошибке DOS ("Program too big to fit  in memory"
- "Программа слишком велика для имеющейся памяти"), либо со-
общение  кода  загрузки  С  ("Not  enough  memmory  to   run
program").

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


     При использовании  оверлеев  память  распределяется,как
показано на следующем рисунке:

    Модель MEDIUM    Модель LARGE
  -----------------  ------------ -----------------
  \! класс CODE   \! Резидентный  \! класс CODE   \!
  \!   \! код  \!   \!
--\!----------------\!  \!----------------\!
 Эти сегменты \!\!класс OVRINFO\!Данные для\!класс OVRINFO\!
 генерируются  \!  \!   \! управления   \!   \!
 компоновщиком \!  \!   \! оверлеями  \!   \!
 автоматически-\!  \!----------------\!  \!----------------\!
   \!  \! класс STUBSEG  \! Один сегмент \! класс STUBSEG  \!
   \!  \!   \! stub для  \!   \!
   \!  \!   \! каждого  \!   \!
   \!  \!   \! оверлейного  \!   \!
   \!  \!   \! сегмента  \!   \!
--\!----------------\! ------------ \!----------------\!
  \!   _DATA   \!  \!   _DATA   \!
 Ближняя куча  \!   класс DATA   \!  \!   класс DATA   \!
 и стек разделяют \!   \!  \!   \!
 сегмент данных   \!ближняя куча\!Отдельный\!--------------\!
  \! ^   \!сегмент   \! ^   \!
  \! |   стек    \!стека  \! |   стек    \!
  \!----------------\! ------------ \!----------------\!
  \!оверлейный буфер\!  \!оверлейный буфер\!
  \!(распределяется \!  \!(распределяется \!
  \! при загрузке)  \!  \! при загрузке)  \!
  \!----------------\! ------------ \!----------------\!
  \! | дальняя   \!  \! | дальняя   \!
  \! v  куча   \!  \! v  куча   \!
  \!   \!  \!   \!
  ------------------  ------------------

















 
                           - 196 -
    Модель HUGE
     ------------ -----------------
     Резидентный  \! класс CODE   \!
     код  \!   \!
    --  \!----------------\!
     Эти сегменты  \!     Данные для   \! класс OVRINFO  \!
     генерируются  \!     управления   \!   \!
     компоновщиком \!     оверлеями  \!   \!
     автоматически-\!  \!----------------\!
   \!     Один сегмент \! класс STUBSEG  \!
   \!     stub для  \!   \!
   \!     каждого  \!   \!
   \!     оверлейного  \!   \!
   \!     сегмента  \!   \!
    --     ------------ \!----------------\!
   . . .     Несколько  \!----------------\!
     сегментов  \!----------------\!
     данных  \!----------------\!
  \!----------------\!
     Отдельный  \!----------------\!
     сегмент  \! ^   \!
     стека  \! |   стек    \!
     ------------ \!----------------\!
  \!оверлейный буфер\!
  \!(распределяется \!
  \! при загрузке)  \!
     ------------ \!----------------\!
  \! | дальняя   \!
  \! v  куча   \!
  \!   \!
  ------------------

      Рис.4.9  Распределение памяти для оверлейных структур


 Использование преимуществ использования оверлеев Turbo C++
 ___________________________________________________________

     Для полного использования преимуществ оверлейных струк-
тур, создаваемых Turbo C,

     - Минимизируйте резидентный код (резидентные библиотеки
исполняющей системы,  обработчики прерываний и драйверы уст-
ройств).

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

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

  Требования

     При создании оверлеев следует помнить несколько простых
правил, а именно:

     - Минимальная часть программы, которая может быть выде-
лена в качестве лверлея, это сегмент.

     - Прикладные программы с оверлейной  структурой  должны
 
                           - 197 -
иметь одну из трех следующих моделей памяти:  medium,  large
или huge;  модели tiny, small и compact оверлеи не поддержи-
вают.

     - Перекрывающиеся сегменты подчиняются обычным правилам
слияния сегментов.  То есть, в одном и том же сегменте может
участвовать несколько .OBJ- файлов.

     Генерирование оверлеев  во  время  компоновки полностью
независимо от  управления  сегментами  во  время  исполнения
программы; компоновщик не включает автоматически каких-либо-
кодовдля управления оверлеями. Действительно, с точки зрения
компоновшика  программа управления оверлеями является просто
одним из подлежащих компоновке участков  кода.  Единственное
предположение, которое делает компоновщик, состоитв том, что
программа управления оверлеями принимает  вектор  прерываний
(обычно INT 3FH), через который происходитуправление динами-
ческой загрузкой. Такой уровень прозрачности упрощает созда-
ние пользовательских программ управления оверлеями,  наилуч-
шим образом управляющих  требованиям  конкретной  прикладной
программы.
    Использование оверлеев

     Для создания программы с оверлейной структурой  все  ее
модули должны компилироваться с включенной опцией компилято-
ра -Y. Для того, чтобы сделать оверлейным конкретный модуль,
его  следует компилировать с опцией -Yo.  (-Yo автоматически
включает опцию -Y).

     Опция -Yo распространяется  навсе  модули  ибиблиотеки,
следующие за ней в командной строке TCC.  Отменить ее можно,
задав -Yo-.Эти две опции являются единственными опциями  ко-
мандной  строки,  которые могут следовать после имен файлов.
Например,для того,чтобы сделатьоверлейным модуль  OVL.C,  но
не библиотеку GRAPHICS.LIB, можно использовать любую из сле-
дующих командных строк:

    TCC -ml - Yo ovl.c -Yo- graphics.lib

 или

    TCC -ml graphics.lib -Yo ovl.c

     Если при запуске TLINK  явно  задана  компоновка  файла
.EXE,  то в командной строке компоновщика должна быть задана
опция/o. Использование опции /o описано в разделе TLINK гла-
вы 5, "Утилиты",
 Руководства пользователя.

       Пример оверлейной структуры
 ___________________________________________________________

     Предположим, вы  хотите  иметь  оверлейную  структуру в
программе,  состоящейиз трех модулей:  MAIN.C,  O1.C и O2.C.
Оверлеями должны являться модули O1.C и O2.C. (MAIN.C содер-
жит зависящие оттекущего времени  подпрограммы  иобработчики
прерываний ипотому должна оставаться резидентной). Предполо-
жим, что данная программа использует модель памяти large.

      Следующая команда позволяет выполнить данную задачу:

    TCC -ml -Y main.c -Yo o1.c o2.c

     В результате получится выполняющийся  файл  MAIN.EXE  с
двумя оверлеями.


 
                           - 198 -
     Организация оверлеев в интегрированной среде разработки
(IDE)
 ___________________________________________________________

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

     1. Войти в диалоговое поле Options \!  Compiler \! Code
Generation и установить опцию Overlays On.

     2. Войти в Options \!  Linker и установит опцию Overlay
On.

     3. В  менеджере проекта используйте элемент Options для
задания каждого модуля, который должен войти в оверлей.

     Первый шаг представляет собойэквивалент интегрированной
среды разработки опции -Y компилятора командной строки. Если
не установить данную опцию в состояние on, то нельзя исполь-
зовать  и две указанных далее опции.  Вторая опция управляет
тем, должна ли данная информация озадании оверлеев использо-
ваться во время компоновки в IDE.  Переведя эту опцию в сос-
тояние off,  вы можете тем самым глобальноотменить  создание
ооверлеев,не  выполняя  перекомпиляцию  и не изменяязначений
индивидуальных установок модулей в менеджере  проекта. Опция
на  третьем шаге управляет тем,  какие модули войдут в овер-
леи,  а какие останутся фиксированными.  Эта опция похожа на
опцию командной строки -Yo.

     Для построения  .EXE-файла с оверлеями скомпилируйтевсе
модули с опцией Code Generation \! Overlays On(сперва убеди-
тесь, что выбрана опция Options \! Full Menus On).

     Ни одиниз  входящих в оверлей модулей никогда не должен
изменять умолчание имени класса кода (Code Class).  Интегри-
рованная  среда  позволяет изменить набор входящих в оверлеи
модулей, не заботясь о рекомпиляции. Это возможно (с текущей
информацией об .OBJ-файлах) только при условии,  что оверлеи
сохраняют имена класса кода по умолчанию.

      Разработка программ с перекрытиями

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

   Требование дальних (far) вызовов
 ___________________________________________________________

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

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

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

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


 
                           - 199 -



      Размер буфера
 ___________________________________________________________

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

     Очевидно, что решение здесь  заключается  в  увеличении
размера оверлейного буфера до таких размеров,  чтобы в любой
момент времени в нем помещались все  часто  вызывающие  друг
друга оверлеи. Это можно сделать, установив через глобальную
переменную _ovrbuffer требуемый размер в параграфах.  Напри-
мер,  для  установки размера оверлейного буфера равным 128К,
включите вваш код следующий оператор:

    unsigned _ovrbuffer = 0x2000;

     Общей формулы для определения идеального размера  овер-
лейного  буфера не существует.  Turbo Prifiler фирмы Borland
поможет определить необходимое значение. Если вы не распола-
гаете  данным продуктом,  то найти размер буфера вам поможет
понимание прикладной задачи и немного зкспериментирования.


  В каких случаях не следует создавать оверлейные структуры
 ___________________________________________________________

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

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

   Отладка оверлеев
 ___________________________________________________________

     Большинство отладчиков  либо  имеет весьма ограниченные
средства отладки программ с оверлейной структурой,  либо во-
обще не имеет таких средств.  Иначе дело обстоит синтегриро-
ванным со средой разработки отладчиком TurboC++ и отладчиком
автономным отладчиком Turbo Debugger.  Обаэти отладчика пол-
ностью поддерживают пошаговую отладку и установку точек пре-
рывания  в  оверлеях  совершенно прозрачным длявас способом.
Благодаря использованию оверлеев вы имеете возможность легко
разрабатывать и отлаживать громоздкие прикладные программы -
как  в  интегрированной  среде,  так  и  при  помощи   Turbo
Debugger.

 Внешние (external) подпрограммы в оверлеях
 ___________________________________________________________

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

     Если подпрограмма  на  языке ассемблера выполняет вызов
любой оверлейной функции, то такая подпрограмма должна иметь
объявление FAR и должна устанавливать стековый фрейм при по-
мощи регистра BP.  Например,  если OtherFunc это  оверлейная
функция в другом модуле, и ее вызывает подпрограмма на языке
ассемблера ExternFunc,  то тогда ExternFunc должна быть FARи
устанавливать стековый фрейм, как показано ниже:

    ExternFunc   PROC   FAR
    push   bp  ;сохранить bp
    mov    bp,sp  ;установить стековый фрейм
    sub    sp,LocalSize   ;распределить локальные переменные
    ...
    call   OtherFunc  ;вызов другого оверлейного модуля
    ...
    mov    sp,bp  ;освобождение локальных переменных
    pop    bp  ;восстановление BP
    RET   ;возврат
    ExternFunc   ENDP

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

     Эти требования   остаются   теми  же  в  случае,  когда
TxternFunc делает  косвенные  ссылки  наоверлейные  функции.
Например, если OtherFunc вызывает оверлейные функции, носама
не является оверлейной,  то ExternFuncдолжнабыть FAR и также
должна устанавливать стековый фрейм.

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

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

Свопинг

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

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

Дополнительная память (EMS)

     Свопинг с дополнительной памятью инициализируется функ-
цией _OvrInitEms. Вот ее прототип:

    extern int far _OvrInitEms
    (*
       unsigned emsHandle,
       unsigned emsFirst,
       unsigned emsPages
    *)

      _OvrInitEms и _OvrinitExt определены в dos.h

     Если параметр emsHandleравен нулю, программа управления
оверлеями проверяет наличие дополнительной памяти и  распре-
деляет ее количество (если может),  достаточное для размеще-
ния там всех оверлеев,  минус  размер  оверлейногобуфера.  В
противном случае emsHandle должен быть допустимым логическим
номером EMS, emsFirst - первой используемой EMS страницей, а
emsPages  -  числом доступных программе управления оверлеями
страниц.  Если дополнительная  память  доступна,  то  данная
функция возвращает 0.
   Расширенная память (Ext)

     Свопингс расширеннойпамятью инициализируется функцией _
OvrinitExt). Вот ее прототип:

    extern int far -OvrininExt
    (*
       unsigned long extStart,
       unsigned long extLength
    *);

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

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

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

    if (_OvrinitExt (1024L * (2048 + 1024), OL))
 
                           - 202 -
     puts ("Доступная расширенная память для свопинга  овер-
леев отсутствует");



Глава 5    Видео функции


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

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

Несколько слов о видео режимах

     Ваш компьютер обязательно имеет некоторый  видео  адап-
тер. Это может быть Монохромный дисплейный адаптер (MDA) для
базового (только текстового) дисплея,  либо это  может  быть
графический  адаптер,  например  Цветной  графическийадаптер
(CGA),  Улучшенный графический адаптер (EGA), либо монохром-
ный  графический адаптер Hercules.  Каждый из этих адаптеров
может работать в нескольких режимах;  режим определяет вели-
чину  экрана - 80 или 40 символов в строке (только в тексто-
вом режиме), разрешающую способность экрана (только в графи-
ческом режиме) и тип дисплея (цветнойили черно-белый).

     Рабочий режим экрана определяется, когда ваша программа
вызывает  одну  из  функций  определения  режима  (textmode,
Initgraph или setgraphmode).

     - В текстовом режиме экран компьютера разделен на ячей-
ки (80 или 40 столбцов в ширину и 25, 42 или 50 строк по вы-
соте). Каждая ячейка состоит из аттрибута и символа . Символ
представляет   собой   имеющий    графическое    отображение
ASCII-символ, а аттрибут задает, каким образом данный символ
будет выведен на экран (его цвет, яркость, и т.д.). Turbo C+
+ предоставляет полный набор подпрограмм для манипулирования
текстовым экраном,  для вывода текста непосредственно на эк-
ран и управления аттрибутами ячеек.

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

     В текстовом режиме позиция верхнего левого  угла экрана
определяетсякоординатами (1,1),где x-координата растет слева
-направо,  а y-координата растет сверху-вниз.  В графическом
режиме позиция верхнего левого угла определяется координата-
ми (0,0), с теми же направления возрастания координат.

Несколько слов о текстовых и графических окнах

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

Что такое окно ?

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

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

Что такое графическое окно ?

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

Координаты

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

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

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

     В Turbo C++ пакет функций прямого  ввода/выводана  кон-
соль (cprintf, cputs и т.д.) обеспечивает высококачественный
вывод текста,  управление окнами, позиционирование курсора и
управление аттрибутами видео изображений. Всеэти функции яв-
ляются частью стандартных библиотек Turbo C++; они имеютпро-
тотипы в файле заголовка conio.h.

       Функции консольного ввода/вывода

     Функции текстового режима TUrbo C++ работают в любом из
шести возможных текстовых видео режимов.Режимы,  доступные в
вашей  системе,  зависят  от  типа видео адаптера и монитора
системы.  Текущий текстовый режим задается вызовом textmode.
Мы  объясним,  как  использовать эту функцию,  ниже в данной
главе,  и кроме того,  она описана в Главе 1 Справочника  по
библиотеке.
 
                           - 204 -

     Функции текстового  режима  делятся на четыре отдельные
группы:

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

 - управления окнами и режимом

 - управления аттрибутами

 - запроса состояния

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

 Вывод и манипулирование текстом
 ___________________________________________________________

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

      Запись и чтение текста:

    cprintfПосылает на экран форматированный вывод.
    cputsПосылает на экран строку.
    getcheСчитывает символ с эхо-отображением его на экране
    putchПосылает на экран отдельный символ.

      Манипулирование текстом (и курсором) на экране:

    clreolСтирание от курсора до конца строки.
    clrscrСтирание текстового окна.
    dellineУдаление текущей строки курсора.
    gotoxyПозиционирование курсора.
    InslineВставка пустой строки под текущей позицией
курсора.
    movetextКопирование текста из одной области экрана на
другую.

      Пересылка блоков текста между памятью и экраном:

    gettextКопирование текста из области экрана в память.
    puttextКопирование текста из памяти в область экрана.

     По умолчанию ваши программы экранного вывода работают с
полноэкранным текстовым окном, поэтому вы можете в них сразу
же начинать писать,  читать и манипулировать текстом без ка-
ких-либо  предварительных установок режима.  Записьтекста на
экран выполняется при помощи консольных функций прямого  вы-
вода cprintf,  cputs и putch, а ввод с консоли с эхо-отобра-
жением символовнаа экране выполняется функцией getche.  Цик-
лический  переход  текста  по экрану определяется глобальной
переменной -wscroll.  Если _wscroll равна1, тотекст при дос-
тиженииконца  строки  переходит  на следующую строку экрана,
причем при необходимостивыполняется  вертикальный скроллинг.
Если  _wscroll  равен  0,  то текст переходит на ту же самую
строку,  и скроллинг невыполняется.  По  умолчанию  _wscroll
равна 1.

     После того,  как ваш текст выведен на экран,  вы можете
стереть активное окно при помощи clrscr,  либо стереть часть
строки  от  текущей позиции курсора при помощи clreol,  либо
удальть целую строку при помощи delline,  либо вставить пус-
тую строку при помощи insline. Трипоследние функции работают
относительно текущей позиции курсора;  перемещение курсора в
желаемую  позицию  выполняется при помощи gotoxy.  Вы можете
также скопировать целый блок текста из  одной  прямоугольной
 
                           - 205 -
области в другую при помощи movetext.

     Имеется также функция копирования прямоугольной области
текста с экрана в память gettext и обратная функция  копиро-
вания текста из памяти на экран (в любую позицию) puttext.

 Управление окнами и режимом
 ___________________________________________________________

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

    textmode   Устанавливает текстовый режим экрана.
    window     Определяет окно текстового режима.

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

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

 Управление аттрибутами
 ___________________________________________________________

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

      Установка аттрибутов переднего плана и фона:

    textattr    Одновременная установка (аттрибутов) цветоа
    переднего плана и фона.
    textbackground  Установка (аттрибута) цвета фона.
    textcolor    Установка (аттрибута) цвета переднего плана.

      Модификация яркости:

    highvideo    Установка повышенной яркости текста.
    lowvideo    Установка низкой яркости текста.
    normvideo    Установка обычной яркости текста.

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

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

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

 Запрос состояния
 ___________________________________________________________

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

    gettextinfo  Заполняет структуру text_info информацией о
    текущем текстовом окне.
    wherex    Дает x-координату ячейки экрана, в которой в
    текущий момент находится курсор.
    wherey    Дает y-координату ячейки экрана, в которой в
    текущий момент находится курсор.

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

     Функция gettextinfoзаполняет структуру text_info (опре-
деленную  в  conio.h)  подробными  даннымио  текстовом окне,
включая:

 - текущий видео режим

 - позиция окна в абсолютных экранных координатах

 - размеры окна

 - текущие цвета переднего плана и фона

 - текущая позиция курсора

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

 Форма курсора
 ___________________________________________________________

     Для изменения  способа представления курсора служит но-
вая   функция   _setcursortype.   Она   принимает   значения
_NOCURSOR, которая вообще отменяет курсор, _SOLIDCURSOR, ко-
торая  дает  курсор  в  виде  сплошного  прямоугольничка   и
_NORMALCURSOR, которое соответствует обычному курсору в фор-
ме подчеркивания.
Текстовые окна

     По умолчанию  текстовое  окно  имеет  размервсего экра-
на;это умолчание можно изменить,  создав  окно,  по  размеру
меньше экрана,  с помощью функции window. Текстовые окна мо-
гут содержать до 50 строк и от 40 до 80 столбцов.

     Начало координат окна(точка,  от которой  отсчитываются
координаты  окна) в Turbo C++ лежит вверхнем девомуглу окна.
Координаты верхнего левого угла окна равны (1,1); координаты
 
                           - 207 -
правого  нижнего окна полноэкранного окна равны при 80столб-
цах и 25 строках (80,25).

 Пример
 ___________________________________________________________

     Предположим, у вас имеется 100%-совместимая с PC систе-
ма, находящаяся в текстовом режиме с 80 столбцами, ивы жела-
ете  создать  окно.  Верхний  левый  угол  окна должен иметь
экранные координаты (10.8),  а нижний правый - (50,21).  Для
этого требуется вызвать функцию window:

    window(10, 8, 50, 21);

     Теперь, создав текстовое окно, вы можете поместить кур-
сор в позицию окна (5,8) и вывести,  начиная с  этойпозиции,
некоторый текст с использованием gotoxy и cputs.

    gotoxy(5, 8);
    cputs("С днем рождения, Френк Борланд");

      Следующий рисунок иллюстрирует сказанное.




   Столбец 1 экрана
   \!
 Строка 1--xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  экрана   x   x
   x   x
   x   x
   x   x
   x   x
   x   x
 Строка 1--x ---  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx   x
  окна   x  x   x   x
   x  x   x   x
   x  x С днем рождения, Френк Борланд x   x
   x  x   x   x
 Строка 14-x----  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx   x
  окна   x   x
   x  \!   \!   x
   x  \!   \!   x
   xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-Строка
  \!   \!   \!  25
  \!   \!   \! экрана
  \!   \!   \!
  Столбец 1  Столбец 41   Столбец 80
   окна    окна экрана

      Рис.5.1  Окно в текстовом режиме 80x25
 Тип text_mode

     Вы можете перевести свой монитор в один из семи тексто-
вых режимов PC, вызвав для этогофункцию textmode. Определяе-
мый в conio.h перечислимый тип text_mode позволяет использо-
вать в качестве аргумента  режима,  задаваемого  при  вызове
text_mode, любое символическое имя, определенное для данного
перечислимого типа,  вместо "сырых" номеров режимов. Однако,
чтобы  воспользоваться  данными  симвоическими  константами,
следует ввести:

    #include <conio.h>

 в исходный код программы.

 
                           - 208 -
     Числовые и символические значения, определенные в text_
mode, приводятся ниже:

 -----------------------------------------------------------
 СимволическаяЧисловое
 константазначение  Текстовый видео режим
 -----------------------------------------------------------
 LASTMODE   -1  Предыдущий текстовый режим
 BW40    0  Черно-белый, 40 столбцов
 C40    1  16 цветов, 40 столбцов
 BW80    2  Черно-белый, 80 столбцов
 C80    3  16 цветов, 80 столбцов
 MONO    7  Монохромный, 80 столбцов
 C4350   64  EGA, 80x43;  VGA, 80x50 строк
 -----------------------------------------------------------

     Например, следующие вызовы textmode помещаютваш цветной
монитор в указанный рабочий режим:

    textmode(0)      Черно-белый, 40 столбцов
    textmode(BW80)   Черно-белый, 80 столбцов
    textmode(c40)    16 цветов, 40 столбцов
    textmode(3)      16 цветов, 80 столбцов
    textmode(7)      Монохромный, 80 столбцов
    textmode(C4350)  EGA, 80x43;  VGA, 80x50 строк

     После вызова textmode с режимомC4350  следуетпри помощи
функции settextinfo задать число строк экрана.
 Цвета текста

     Подробную информацию о том, как хранятся аттрибуты яче-
ек, см. в описании textattr в Главе 1 Справочника по библио-
теке.

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

     Включаемый файл  conio.h  определяет  вместо  различных
цветов символические имена.  При использовании этих символи-
ческих констант вы должны включить в исходный код conio.h.

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

 -----------------------------------------------------------
 Символическая       Числовое   Передний план
 константа       значение   или фон?
 -----------------------------------------------------------
 BLACK  0  Оба
 BLUE  1  Оба
 GREEN  2  Оба
 CYAN  3  Оба
 RED  4  Оба
 MAGENTA  5  Оба
 BROWN  6  Оба
 LIGHTGRAY  7  Оба
 DARKGRAY  8  Только передний план
 LIGHTBLUE  9  Только передний план
 LIGHTGREEN 10  Только передний план
 
                           - 209 -
 LIGHTCYAN 11  Только передний план
 LIGHTRED 12  Только передний план
 LIGHTMAGENTA 13  Только передний план
 YELLOW  14  Только передний план
 WHITE 15  Только передний план
 BLINK128  Только передний план
 -----------------------------------------------------------

     Если выжелаете, чтобы символ мигал, добавьте к аргумен-
ту переднего плана  символическую  константу  BLINK(числовое
значение 128).

Высокоскоростной вывод: переменная directvideo

     Пакет консольных  функциий ввода/вывода Turbo C++ вклю-
чает  переменную  directvideo.  Эта   переменная   управляет
тем,выполняется  ли  вывод на консоль из вашей программы не-
посредственно в оперативную память  дисплея  (directvideo  =
1),  либо направлфется туда через вызовы BIOS (directvideo =
0).

     По умолчанию значение directvideo = 1 (консольный вывод
направляется непосредственно в память дисплея). В целом, ра-
бота непосредственно с дисплейной памятью ускоряет вывод, но
для  этого  требуется 100%  совместимость вашей машины с IBM
PC.Ваша видео  аппаратура  должна  быть  идентичнадисплейным
адаптерам IBM. Установка directvideo=0 позволит вам работать
на любой машине с IBM-совместимым BIOS,  но вывод на консоль
будет несколько замедлен.
     Программирование в графическом режиме


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

     Turbo C++ имеет отдельную библиотекус более чем 70 гра-
фическими  функциями,  начиная  от  функций  высокого уровня
(таких как setviewport,  bar3d и drawpoly) и кончая бит-ори-
ентированными функциями (ипа getimage иputimage).  Графичес-
кая библиотека поддерживает многочисленные стили линий и за-
полнителей,   а  также  предоставляютвам  различныетекстовые
шрифты,  которые вы можете изменять по размерам, способу вы-
равнивания,  а  также  ориентировать их либо по горизонтали,
либо по вертикали.

     Эти функции    находятся    в    библиотечном     файле
GRAPHICS.LIB, а их прототипы - в файле заголовка graphics.h.
Кроме этих двух файлов,  в состав графического пакета входят
драйверы  графических  устройств  (файлы *.BGI) и символьные
шрифты (*.CHR-файлы).;эти дополнительные файлы  рассматрива-
ются в следующих разделах.

      Для использования графических функций:

     - При  работе  в интегрированной среде переключите Full
menus в  состояние  on  и  выберите  Options  \!  Linker  \!
Graphics Library. При создании программы компоновщик автома-
тически выполнит  компоновку  графической  библиотеки  Turbo
C++.

     - Если  вы  используете TCC.EXE,  вы должны в командной
строке указать GRAPHICS.LIB.  Например, если ваша программа,
MYPROG.C, использует графику, то командная строка TCC должна
иметь вид:

 
                           - 210 -
    tcc myprog graphics.lib

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

     Поскольку графические функциииспользуют  указатели far,
графика в случае модели рамяти tiny не поддерживается.

     Графическая библиотека только одна и не имеет версий по
моделям памяти (по сравнению  со  стандартными  библиотеками
CS.LIB, CC.LIB, CM.LIB и т.д., которые зависят от используе-
мой модели памяти).  Каждая функция в GRAPHICS.LIB  является
far (дальней) функцией,  а графические функции, использующие
указатели работают с дальними  указателями.  Для  правильной
работы  графических  функций  требуется  директива  #include
graphics.h вкаждом использующем graphics модуле.

  Функции библиотеки graphics

     Графические функции Turbo C++ делятся на несколько  ка-
тегорий:

 - управления графической системой

 - черчения и заполнения

 - манипулирования экранами и графическими окнами

 - вывода текстов

 - управления цветами

 - обработки ошибок

 - запроса состояния

 Управление графической системой
 ____________________________________________________________

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

    closegraph     Закрывает графическую систему.

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

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

  _graphfreemem  Отменяет распределенную графике память; испо-
                 льзуется для определения собственной подпро-
                 граммы.

  _graphgetmem   Распределяет память графике; используется
                 для определения собственной подпрограммы.

  getgraphmode   Возвращает текущий графический режим.

  getmoderange   Допускает младший и старший допустимые режимы
                 для заданного драйвера.

  initgraph     Инициализирует графическую систему и переводит
                аппаратное обеспечение в графический режим.

 installuserdriver  Инсталлирует дополнительный драйвер устрой
                    ства в таблицу драйверов устройста BGI.
 
                           - 211 -

 installuserfont  Загружает поставляемый файл штрихового шриф
                  та в таблицу символьных файлов BGI.

 registerbgldriver  Регистрирует внешний или загруженный
                    пользователем файл драйвера для включения
                    во время компоновки.

 restorecrtmode Восстанавливает  первоначальный (существовав
                ший до Initgraph) режим экрана.

 setgraphbufsize  Задает размер внутреннего графического буфе
                  ра.

 setgraphmode   Выбирает заданный графический режим, очищает
                зкран и восстанавливает все умолчания.

     Графический пакет Turbo  C++  обеспечивает  графические
драйверы  для  следующих  графических адаптеров (и полностью
совместимых с ними):

 - Цветной/графический адаптер (CGA)

 - Многоцветная графическая матрица (MCGA)

 - Улучшенный графический адаптер (EGA)

 - Видео графическая матрица (VGA)

 - Графический адаптер Hercules

 - Графический адаптер серии AT&T 400

 - Графический адаптер 3270 PC

 - Графический адаптер IBM 8514

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

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

     После того, как графический драйвер загружен, вы можете
определить его имя при помощи функции getdrivename,  а число
поддерживаемых  драйвером   режимов   при   помощи   функции
getmaxmode.  getgraphmode  сообщит вам,  в каком графическом
режиме вы находитесь в текущий момент. Имея номер режима, вы
можете  определить егоимя при помощи функции getmodename. Вы
такжеимеетевозможность изменить графический режим при помощи
функции  setgraphmode  и  вернуть исходный видео режим (тот,
который был установлен до инициализации графики)  с  помощью
restorecrtmode.  restorecrtmode вернет экран в текстовый ре-
жим, но не закроет при этом графическую систему (загруженные
шрифты и драйверы останутся в памяти).

     graphdefaults сбрасывает установки состоянияграфической
 
                           - 212 -
системы (размеры графического окна,  цвет линий, цвет и шаб-
лон заполнителя и т.д.) в исходное состояние.

     installuserdriver и  installuserfont  позволяют устано-
вить в графической системе новые драйверы устройства и шриф-
ты.

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

 Более подробное рассмотрение графики
 ____________________________________________________________

     Выше кратко рассматривалась работа функции initgraph. В
следующих параграфах initgraph, _graphgetmem и _graphfreemem
будут описаны подробно.

     Обычно подпрограмма  initgraph  загружает   графический
драйвер,  распределяя для этого драйвера память и затем заг-
ружая туда с диска соответствующий  файл  .BGI.  В  качестве
альтернативы   данной  схемединамической  загрузкивы  можете
скомпоновать нужный файл графического драйвера (или несколь-
ко  таких файлов) непосредственно с файлом выполняемой прог-
раммы.  Для этого .BGI-файл сначала преобразуетсяв .OBJ-файл
(при   помощи  утилиты  BGIOBJ  -см.  документацию  в  файле
UTIL.DOC,  который поставляется на одном  из  дистрибутивных
дисков),   после   чего  в  исходный  код  помещается  вызов
registerbgidriver (до вызова initgraph), чтобы зарегистриро-
вать графический драйвер(ы) в системе.  При построении прог-
раммы вы должны выполнить компоновку.OBJ-файлов  всех  заре-
гистрированных  драйверов.  После определения того,  который
графический драйвер  должен  быть  использован  (посредством
detectgraph)  initgraph  проверяет,  был ли желаемый драйвер
зарегистрирован.  Если был,  то initgraph обращается к заре-
гистрированному драйверу непосредственно в памяти. В против-
ном случае initgraph  распределяет  память  для  драйвера  и
зпгружает нужный .BGI-файл с диска.

 Примечание

     Использование функции  registerbgidriverотносится к бо-
лее сложным методам программирования,  не рекомендуемым  для
начинающих программистов. Более подробно данная функция опи-
санав Главе 1
 Справочника по библиотеке.

     Во время  выполнения  графической системе может понадо-
биться выполнить распределение памяти для драйверов, шрифтов
и    внутренних    буферов.   Принеобходимостиона   вызывает
_graphgetmemдля распределенияпамятии  _graphfreemem  для  ее
освобождения.  По умолчанию данные подпрограммы просто вызы-
вают malloc и free, соответственно.

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

     Определив собственные    функции     _graphgetmem     и
_graphfreemem,  вы можете получить предупреждение "duplicate
symbols" ("повторение символических имен"). Это предупрежде-
 
                           - 213 -
ние можно игнорировать.

 Черчение и заполнение
 ___________________________________________________________

     Ниже приводится краткий обзор функций черчения и запол-
нения:

      Черчение:

    arc     Чертит дугу окружности.

    circle    Чертит окружность.

    drawpoly    Чертит контур многоугольника.

    ellipse    Чертит эллиптическую дугу.

    getarccoords    Возврашает координаты последнего вызова
                    arc или ellipse.

    getaspectratio  Возвращает коэффициент сжатия для текуще
                    го графического режима.

    getlinesettings Возвращает текущий стиль линии, шаблон
                    линии и толщину линии.

    line    Чертит линию из (x0,y0) в (x1,y1).

    linerel    Чертит линию в точку, задаваемую относитель
               ным расстоянием от текущей позиции (CP).

    lineto    Чертит линию из текущей позиции (CP) в (x,y).

    moveto    Перемещает текущую позицию (CP) в (x,y).

    moverel    Перемещает текущую позицию (CP) на относитель
               ное расстояние.

    rectangle    Рисует прямоугольник.

    setaspectratio  Изменяет коэффициент сжатия по умолчанию.

    setlinestyle    Устанавливает ширину и стиль текущей ли
                    нии.


      Заполнение:

    bar     Чертит и заполняет столбик.

    bar3d    Чертит и заполняет трехмерный столбик.

    fillellipse     Чертит и заполняет эллипс.

    fillpoly    Чертит и заполняет многоугольник.

    getfillpattern  Возвращает определяемый пользователем
                    шаблон заполнения.

     getfillsettings Возвращает информацию о текущкм шаблоне
                     и цвете заполнения.

    pieslice    Чертит и заполняет сектор окружности.

    sector    Чертит и заполняет эллиптический сектор.

 
                           - 214 -
    setfillpattern  Выбирает шаблон заполнения, определяемый
    пользвателем.

    setfillstyle    Устанавливает шаблон и цвет заполнения.

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

     Линии и незаполненные формы  вычерчиваются  при  помощи
функций  arc,  circle,  drawpoly,  ellipse,  line,  linerel,
lineto и rectangle. Затемможно заполнить эти формы с помощью
floodfil,  либо  можно  объединить вычерчивание/заполнение в
одном шаге  при  помощи  функций  bar,  bar3d,  fillellipse,
fillpoly,  piesliceи sector.  Функция setlinestyle позволяет
задать стиль линий (играничных линий форм): толстая или тон-
кая,  сплошная,  пунктир и т.д.,  либоможно задать ваш собс-
твенный шаблондля вычерчивания линии.  Можно выбрать предоп-
ределенный    шаблон    заполнения    при   помощи   функции
setfillstyle,либо определить собственный шаблон заполнения в
setfill  psttern.  Функция  moveto позволяетпереместить CP в
желаемую позицию,  афункция moverel позволяет сдвинуть ее на
желаемую величину смещения.

     Выяснить текущий  стиль и толщину линии позволяет функ-
ция getlinesettings. Информацию о текущем шаблоне заполнения
и   цвете   заполнителя   можно   получить   через   функцию
getfillsettings;  определяемый пользователем шаблон заполне-
ния можно получить при помощи getfillpattern.

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

    Манипулирование экраном и графическими окнами
 ___________________________________________________________

     Ниже приводитсякраткий обзорфункций  манипулирования  с
экраном, графическими окнами, битовыми образами и пикселями:

      Манипуляции с экраном:

    cleardevice        Очищает экран (активную страницу).

    setactivepage      Устанавливает активную страницу для
       графического вывода

    setvisualpage      Устанавливает номер визуальной графи
                       ческой cтраницы.

      Манипуляции с графическими окнами:

    clearviewport      Очищает текущее графическое окно.

    getviewsettings    Возвращает информацию о текущем графи
                       ческом окне.
 
                           - 215 -

    setviewport        Устанавливает текущее графическое окно
                       для направления на него графического
                       вывода.

      Манипуляции с битовыми образами:

    getimage       Записывает битовый образ в заданный участок
                   памяти.

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

    putimage     Помещает на экран ранее записанный в память
                 битовый образ.

      Манипуляции с пикселями:

    getpixel       Принимает цвет пикселя в (x,y).

    putpixel       Помещает пиксель на экран в (x,y).

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

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

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

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

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

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




 
                           - 216 -


 Текстовый вывод в графическом режиме
 ___________________________________________________________

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

    gettextsettings   Возвращает текущий текстовый шрифт,
      направление, размер и выравнивание.

    outtext      Посылает строку на экран в текущую позицию
      (CP).

    outtextxy    Посылает текст на экран в заданную позицию.

     registerbgifont Регистрирует     прикомпонуемый     или
определяемый пользователем шрифт.

     settextjustify Устанавливает   значения    выравнивания
текста, используемые outtext и outtextxy.

     settextstyle Устанавливает  шрифт,  стиль и коэффициент
увеличения текущего текста.

     setusercharsize Устанавливает соотношение между высотой
и шириной штриховых шрифтов.

    textheight      Возвращает высоту строки в пикселях.

    textwidth      Возвращает ширину строки в пикселях.

     Графическая библиотека  включает в себя матричный шрифт
8х8 и несколько штриховых шрифтовдля вывода  текста  вграфи-
ческом режиме.

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

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

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

     В графике  текст  выводится   функциями   outtext   или
outtextxy,  а управление его выравниванием (относительно CP)
выполняет функция settextjustify. Вы должны выбрать символь-
ныйшрифт,направление его размещения (горизонтальное или вер-
тикальное)   и   размер   (масштаб)   при   помощи   функции
settextstyle.  Узнать текущие установки вывода текстов можно
при помощи функции gettextsettings,  которая возвращаеттеку-
щий текстовый шрифыт, выравнивание, увеличение и направление
в структуре textsettings.  setusercharsize позволяет модифи-
цировать ширину и высоту штриховых шрифтов.


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

     По умолчанию  битовый  8х8  матричный  шрифт   является
встроенным  в графический пакет и поэтому всегда доступен во
время выполнения.  Штриховые шрифты все хранятся в отдельных
.CHR-файлах;  они  могут загружаться во время выполнения или
преобразовываться в .OBJ-файлы (при помощи утилиты BGIOBJ) и
затем компоноваться с вашим .EXE-файлом.

     Обычно подпрограмма settextstyle загружает файл шрифта,
распределяя память для него и затем загружая с  диска  соот-
ветствующий .CHR-файл.  В качестве альтернативы данной схеме
динамической загрузки вы  можете  скомпоновать  файл  шрифта
(или  несколько  таких  файлов) непосредственно свыполняемым
файлом программы.  Для этого сначала требуется преобразовать
.CHR-файл в .OBJ-файл (спомощью утилиты BGIOBJ - прочтите об
этом в файле документации  UTIL.DOC,  который  находится  на
дистрибутивном диске), а затем поместить в исходную програм-
му вызовы registerbgifont (перед вызовом  settextstyle)  для
того, чтобы зарегистрировать данный символьный шрифт(ы). При
построении программы необходимо скомпоновать полученные .OBJ
-файлы для всех зарегистрированных вами штриховых шрифтов.

 Примечание

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

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

      Функции получения информации о цвете:

    getbcolor      Возврашает текущий цвет фона.

    getcolor      Возвращает текущий цвет вычерчивания.

  getdefaultpalette Возвращает структуру определения палитры.

   getmaxcolor       Возвращает максимальное значение цвета,
                     доступное в текущем графическом режиме.

    getpalette      Возвращает текущую палитру и ее размер.

    getpalettesize    Возвращает размер просмотровой таблицы
                      палитры.

      Функции установки одного или более цветов:

    setallpalette     Изменяет все цвета палитры, как задано.

    setbkcolor      Устанавливает текущий цвет фона

    setcolor      Устанавливает текущий цвет вычерчивания.

    setpalette      Изменяет один из цветов палитры, как
                    указано ее аргументами.
 
                           - 218 -

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

 Пиксели и палитры
 ___________________________________________________________

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

     Такая схема  косвенных обращений имеет множество следс-
твий.  Хотя аппаратное обеспечение может позволять отображе-
ние множества цветов, одновременно на экране может находить-
ся только некоторое их  подмножество. Количествоодновременно
находящихся  на  экране цветов равно числу элементов палитры
(размеру палитры).  Например,  EGA позволяет наличие 64 цве-
тов, но лишь 16 из них может находиться на экране сразу; та-
ким образом, размер палитрыEGA равен 16.

     Размер палитры определяет  диапазон  значений,  которые
может   принимать   пиксель,   от  0  до  (размер-1).  Функ-
цияgetmaxcolor возвращает максимальное  допустимое  значение
пикселя  (размер-1) для текущего графического драйвера и ре-
жима.

     При обсужденииграфических функций Turbo  C++  мы  часто
используем  термин "цвет",  например текущий цвет вычерчива-
ния,  цвет заполнения  и  цвет  пикселя.  Фактическицветоммы
здесь называем значение пикселя:  это некоторый индекс в па-
литре. Только палитра реально определяет фактический цвет на
экране.Манипулируя  палитрой,  выможетеизменять  фактические
цвета,  выводимые на дисплей,  даже хотя  значения  пикселей
(цвета вычерчивания, заполнения и т.д.) могут не изменяться.

 Цвет фона и вычерчивания
 ___________________________________________________________

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

     Цветомвычерчивания называется  значение,в которое уста-
навливаются пиксели при вычерчивании линий.  Цвет вычерчива-
ния устанавливается функцией setcolor(n),  где n есть допус-
тимое для текущей палитры значение пикселя.

   Управление цветом на CGA

     Вследствие различий в графическом аппаратномобеспечении
фактическое  управление  цветами  различно для CGA и EGA,что
заставляет нас рассмотреть  их  по  отдельности.  Управление
цветом  для драйвераAT&T,  а также режимы низкой разрешающей
способности драйвера MCGA аналогичны управлению  цветом CGA.

     В случае CGA вы можете выбрать либо режим  низкойразре-
шающей способности (320х200),  который допускает использова-
ние четырех цветов,  либо режим высокой разрешающей  способ-
ностей (640х200), где допускается использование двух цветов.


 
                           - 219 -


 CGA в режиме низкой разрешающей способности
 ___________________________________________________________

     В режиме низкой разрешающей способности вы имеете  воз-
можность  выбрать  одну из четырех четырехцветных палитр.  В
каждой из этих четырех  палитр  вы  можете  сами  установить
только первый (цвет 0) элемент; цвета 1, 2 и 3 являются фик-
сированными.  Первый элемент палитры (цвет 0) - это цвет фо-
на.  Этот  цвет  может являться одним из 16 имеющихся цветов
(см. таблицу цветов фона, приводимую ниже).

     Вы выбираете желаемую палитру,  выбирая соответствующий
режим (CGAC0,  CGAC1,  CGAC2,  CGAC3); эти режимы используют
палитры цветов от 0 до 3,  соответственно,  как  показано  в
следующей таблице.  Цвета вычерчивания в CGA и эквивалентные
им константы определяются в graphics.h.


 -----------------------------------------------------------
    Константа, присвоенная номеру цвета (значению пикселя)
 Номер    --------------------------------------------------
 палитры  1     2 3
 -----------------------------------------------------------
   0    CGA_LIGHTGREENCGA_LIGHTRED     CGA_YELLOW
   1    CGA_LIGHTCYANCGA_LIGHTMAGENTA     CGA_WHITE
   2    CGA_GREENCGA_RED      CGA_BROWN
   3    CGA_CYANCGA_MAGENTA     CGA_LIGHTGRAY
 -----------------------------------------------------------

     Для того,  чтобы назначить один из этих  цветов  цветом
вычерчивания  CGA,  нужно вызвать функцию setcolor,  задав в
ней в качестве аргумента либо номер цвета,  либо  имя  соот-
ветствующей константы; например,если вы используете палитру3
и желаете назначить цветом вычерчивания cyan, то можно запи-
сать:

    setcolor(1);

 или

    setcolor(CGA_CYAN);

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

 -----------------------------------------------------------
 Числовое Символическое    Числовое    Символическое
 значение имя    значение    имя
 -----------------------------------------------------------
    0 BLACK8    DARKGRAY
    1 BLUE9    LIGHTBLUE
    2 GREEN       10    LIGHTGREEN
    3 CYAN       11    LIGTHCYAN
    4 RED       12    LIGHTRED
    5 MAGENTA       13    LIGHTMAGENTA
    6 BROWN       14    YELLOW
    7 LIGHTGRAY       15    WHITE
 -----------------------------------------------------------

     Цвета CGA для переднего плана те же,  что  находятся  в
данной таблице.

     Для назначения одного изэтих цветов в качестве фонового
цветаслужитфункция setbkcolor(цвет),  где цвет - это один из
элементов  приведенной  выше таблицы.  Отметим,  что для CGA
 
                           - 220 -
цвет не является значением пикселя (индексом в  палитре); он
прямо  задает фактический цвет,  помещаемый в первый элемент
палитры.

 CGA в режиме высокой разрешающей способности
 ___________________________________________________________

     В режиме  высокой  разрешающей способности (640x200)CGA
работает с двумя цветами - черным цветом фона и цветным  пе-
редним  планом.  Пиксели  могут  принимать при этом значения
только 0 или 1. В связи с особенностями CGA цветом переднего
плана фактически является тот цвет, который аппаратное обес-
печение считает цветом фона;  таким образом,  цвет переднего
плана устанавливается подпрограммой setbkcolor. (Странно, но
факт).

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

     Следующие режимы работают аналогичным  оюразрм:  CGAHI,
MCGAMED, MCGAHI, ATT400MED и ATT400HI.

 Подпрограммы управления палитрой в случае CGA

     Поскольку палитра CGA является предопределенной,  подп-
рограмму setallpaletteиспользовать в данном  случае  нельзя.
Также не следует использовать setpalette(индекс, фактический
_цвет),  за исключением индекс=0.  (Этоальтернативный способ
установки фонового цвета CGA равным фактическому_цвету).

 Управление цветом для EGA и VGA
 ___________________________________________________________

     В случае EGA палитрасодержит 16 элементов из общего ко-
личества 64 возможных цветов, причем каждый из элементов па-
литры можетбыть задан пользователем. Доступ к текущей палит-
ре   выполняется   через   getpalette,   которая   заполняет
структуру,  включающую всебя размер палитры (16) имассивфак-
тических элементов палитры ("аппаратные номера цветов", хра-
нимые в палитре). Элементы палитры можно изменять как по от-
дельностипри   помощи   setpalette,  либо  все  сразу  через
setallpalette.

     Палитра EGA по умолчанию соответствует 16  цветам  CGA,
которые  были даны впредыдущей таблице цветов:  черный равен
элементу 0, голубой равен элементу 1, ... , белый равен эле-
менту 15.  В graphics.h определены константы, которые содер-
жат  соответствующие   цветам   аппаратные   значения:   это
EGA_BLACK, EGA_WHITE и т.д.Эти значения могут быть также по-
лучены через getpalette.

     Подпрограмма setbkcolor(цвет) на  EGAработает несколько
иначе,  чем  на CGA.  На EGA setbkcolor копирует фактическое
значение цвета, хранящееся в элементе #цвет, в элемент #0.

     Что касается цветов,  то драйвер VGA работает фактичес-
китак же,  как и драйвер EGA;  он просто имеет более высокое
разрешение (и меньшие по размеру пиксели).

 Обработка ошибок в графическом режиме
 ___________________________________________________________

     Ниже приводитсякраткий  обзорфункций обработки ошибок в
графическом режиме:

     grapherrormsg Возвращает строку с сообщением  об ошибке
 
                           - 221 -
для заданного кода ошибки.

     graphresult Возвращает  код ошибки для последней графи-
ческой операции, в которой встретилась ошибка.

     Если ошибка произошла при вызове графической библиотеч-
ной функции (например,не найден шрифт,  запрошенный функцией
settextstyle), устанавливается внутренний код ошибки. Доступ
к коду ошибки для последней графической операции, сообщившей
об ошибке,  выполняется при помощи  graphresult.  Определены
следующие коды возврата ошибки:

 -----------------------------------------------------------
 Код  Константа  Соответствующая строка
 ошибки   графической_ошибки  с сообщением об ошибке
 -----------------------------------------------------------
   0  grOk  No error
  Нет ошибки

   -1  grNoInitGraph   (BGI) graphics not installed (use
  initgraph)
  (BGI) графика не инсталирована
  (используйте initgraph)

   -2  grNotDetected   Graphics hardware not detected
  Графическое аппаратное обеспечение
  не обнаружено

   -3  grFileNotFound  Device driver file not found
  Не найден файл драйвера устройства

   -4  grInvalidDriver  Invalid device driver file
  Неверный файл драйвера устройства

   -5  grNoLoadMem  Not enough memory to load driver
  Не хватает памяти для загрузки
  драйвера

   -6  grNoScanMem  Out of memory in scan fill
  Кончилась память при сканирующем
  заполнении

   -7  grNofloodMem  Out of memory in flood fill
  Кончилась память при лавинном
  заполнении

   -8  grFontNotFound  Font file not found
  Файл шрифта не найден

   -9  grNoFontMem  Not enough memory to load font
  Не хватает памяти для загрузки
  шрифта

   -10  grInvalidMode   Invalid graphics mode for selrcted
  driver
  Недопустимый графический режим
  для выбранного драйвера

   -11  grError  Graphics error
  Графическая ошибка

   -12  grIOerror  Graphics I/O error
  Графическая ошибка ввода/вывода

   -13  grInvalidFont   Invalid font file
  Неверный файл шрифта

 
                           - 222 -
   -14  grInvalidFontNum  Invalid font number
  Неверный номер шрифта

   -15  grInvalidDeviceNum  Invalid device number
  Неверный номер устройства

   -16  grInvalidVersion  Invalid version of file
  Неправильная версия файла
 -----------------------------------------------------------

     Вызов grapherrormsg(graphresult())  возвращает   строку
сообщения об ошибке из вышеприведенной таблицы.

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

 Функции запроса состояния
 ___________________________________________________________

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

     getarccoords Возвращает   информацию   о   координатах,
заданных в последнем вызове arc или ellipse.

     getaspectratio Возвращает коэффициент сжатия для графи-
ческого экрана.

    getbkcolor     Возвращает текущий цвет фона.

    getcolor     Возвращает текущий цвет вычерчивания.

     getdrivername Возвращает   имя   текущего  графического
драйвера.

     getfillpattern Возвращает шаблон заполнения, определяе-
мый пользователем.

    getfillsettings  Возвращает информацию о текущем шаблоне
     и цвете заполнения.

    getgraphmode     Возвращает текущий графический режим.

     getlinesettings Возвращает текущие стиль, шаблон и тол-
щину линии

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

     getmaxmode Возвращает  максимально   допустимый   номер
режима для текущего драйвера.

    getmaxx     Возвращает текущее разрешение по оси x.

    getmaxy     Возвращает текущее разрешение по оси y.

    getmodename      Возвращает имя данного режима драйвера.

    getmoderange     Возвращает диапазон режимов для данного
     драйвера.

    getpalette     Возвращает текущую палитру и ее размер.
 
                           - 223 -

    getpixel     Возвращает цвет пикселя в (x,y).

     gettextsettings Возвращает текущий  шрифт, направление,
размер и способ выравнивания текста.

     getviewsettings Возвращает  информацию о текущем графи-
ческом окне.

    getx     Возвращает координату x текущей позиции (CP).

    gety     Возвращает координату y текущей позиции (CP).

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

     Функциями запроса состояния категории  управленияграфи-
ческой   системы   являются   getgraphmode,   getmaxmode   и
getmoderange.  Первая из них возвращает целое число, опреде-
ляющее текущийграфический драйвер и режим, вторая возвращает
максимальный номер режима для этого драйвера, а третья возв-
ращает  диапазон режимов,  поддерживаемых данным графическим
драйвером.  getmaxx и getmaxy возвращают соответственно мак-
симальные экранные координаты x и y для текущего графическо-
го режима.

     Функциями запроса состояниякатегории вычерчивания и за-
полнения      являются     getarccoords,     getaspectratio,
getfillpattern  и  getlinesettings.  getarccoords  заполняет
структуру, содержащуу координаты, которые использовались при
последнем вызове функций arc или ellipse; getaspectratio со-
общает текущийкоэффициент сжатия,  используемый графической-
системой для  того,  чтобы  окружности  выглядели  круглыми.
getfillpatternвозвращает  текущий  определяемыйпользователем
шаблон заполнения. getfillsettings заполняет некоторуюструк-
туру  текущим шаюлоном и цветом заполнения.  getlinesettings
заполняет структуру текущим стилем линии(сплошная, пунктир и
т.д.),  толщиной (обычнаяили увеличенная),  а также шаблоном
линии.

     Функциями запроса  состояниякатегории   манипулирования
графическим  окном  являются getviewsettings,  getx,  gety и
getpixel.  После того,  как графическое окно определено,  вы
можете  найтиего  абсолютные  экранные  координаты ивыяснить
состояние режима отсечки,  вызвав  getwiewsettings,  которая
заполняет  соответствующей  информацией некоторую структуру.
getx и gety возвращают (относительно графическогоокна)  x- и
y-координаты текущей позиции.  getpixel возвращает цвет ука-
занного пикселя.

     Функция запросасостояния категориивывода текста в  гра-
фическом  режиме имеется только одна - gettextsettings.  Эта
функция заполняет структуруинформацией  отекущем  символьном
шрифте,  направлении  вывода  текста  (по горизонтали или по
вертикали)6 коэффициенте увеличениясимволов,  а  также  виде
выравнивания  (как  для  горизонтально,  так идля вертикаль-
но-ориентированных текстов).

 
                           - 224 -
     Функциями запроса состоянии  категорииуправления цветом
являются    getbkcolor,возвращающая   текущий   цвет   фона,
getcolor,   возвращающая   текущий   цвет   вычерчивания   и
getpalette,заполняющая  структуру,  которая  включаетв  себя
размер текущей палитры и ее содержимое.  getmaxcolor возвра-
щает  максимально  допустимое  значение пикселя для текущего
графического драйвера и режима (размер палитры -1).

     И наконец,  getmodename и  getdrivername  возвращаютимя
заданного  режима драйвера и имя текущего графического драй-
вера, соответственно.



Глава 6

 Интерфейс с языком ассемблера
 ___________________________________________________________

     В данной главе рассказывается,  как написать ассемблер-
ный код,  который будет хорошо работать с Turbo C++. Предпо-
лагается,  что  вы знаете,  как пишутсяподпрограммы на языке
ассемблера икак определяются сегменты, константы данных и т.
д.  Если вы не знакомы с этими концепциями,  почитайте руко-
водство по Turbo Assembler,  особенно главу "Интерфейс Turbo
Assembler   с   Turbo   C"   в   Руководстве   пользователя.
TurboAssembler версии 2.0включает несколько средств,  делаю-
щих  интерфейс  с  Turbo  C++ более простым и прозрачным для
программиста.

Смешанное программирование

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

    Последовательности передачи параметров

     Turbo C++ поддерживаетдва  метода  передачи  параметров
функции. Один из них является стандартным методом С, который
мы рассмотрим первым; второй метод заимствован из Паскаля.


 Последовательность передачи параметров в С
 ___________________________________________________________

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

    void funca(int p1, int p2, long p3);

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








 
                           - 225 -




    main()
    (*
       int  i,j;
       long k;
       ...
       i = 5; j = 7; k = 0x1407AA;
       funca(i,j,k);
       ...
    *)

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

    sp + 06: 0014
    sp + 04: 07AA k = p3
    sp + 02: 0007 j = p2
    sp:      0005 i = p1

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

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

    mov  WORD PTR [bp-8],5;установка i =5
    mov  WORD PTR [bp-6],7;установка j = 7
    mov  WORD PTR [bp-2],0014h;установка k = 0x1407AA
    mov  WORD PTR [bp-4],07AAh
    push WORD PTR [bp-2];помещение в стек старшего слова k
    push WORD PTR [bp-4];помещение в стек младшего слова k
    push WORD PTR [bp-6];помещение в стек j
    push WORD PTR [bp-8];помещение в стек i
    call NEAR PTR funca ;вызов funca (помещение в стек
;адреса возврата)
    add  sp,8;настройка стека

     Обратите внимание на последнюю  команду,  add  sp,8.  К
этому моменту компилятору известно,  сколько параметров было
помещено в стек;  компилятор также знает, что адрес возврата
был  помещен  в  стек при вызове funca и уже был снят оттуда
командой ret в конце funca.

   Последовательность передачи параметров Паскаля
 ___________________________________________________________

     Другим методом передачи параметров является стандартный
метод передачи параметров Паскаля (называемый также соглаше-
нием о связях Паскаля).  Это не значит,  что вы можете вызы-
вать из TurboC++ функции Turbo Pascal.  Это невозможно. Если
funca объявлена как

    void pascal funca(int p1, int p2, long p3);

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



 
                           - 226 -



    main()
    (*
       int  i,j;
       long k;
       ...
       i = 5; j = 7; k = 0x1407AA;
       funca(i,j,k);
       ...
    *)

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

    sp + 06: 0005 i = p1
    sp + 04: 0007 j = p2
    sp + 02: 0014
    sp:      07AA k = p3


     Итак, в чем здесь различие?  Дело в том, что помимо из-
менения очередности помещения параметров в стек,  последова-
тельность передачи параметров Паскаля предполагает,  что вы-
зываемая функция (funca) знает,  сколько параметров будет ей
передано и соответственно  настраиваетстек.  Другимисловами,
теперь  в  ассемблированном  виде данная функция будет иметь
вид:

    push WORD PTR [bp-8];помещение в стек i
    push WORD PTR [bp-6];помещение в стек j
    push WORD PTR [bp-2];помещение в стек старшего слова k
    push WORD PTR [bp-4];помещение в стек младшего слова k
    call NEAR PTR funca ;вызов funca (помещение в стек
;адреса возврата)

     Отметим, что теперь после вызова отсутствует командаadd
sp,8.  Вместо нее funca использует при окончании команду ret
8, при помощи которой очищает стек перед возвратом к main.

     По умолчанию все функции,  создаваемые в Turbo C++, ис-
пользуют способ передачи параметров С.  Исключение  делается
при  наличии опциикомпилятора -p (опция Pascal в диалоговом-
поле Code Generation);  в этом случае все функции используют
метод передачи параметров Паскаля.  Тем не менее,  вы можете
задать для любой функции метод передачи параметров С при по-
мощи модификатора cdecl:

    void cdeclfunca(int p1, int p2, long p3);

     Данное объявление переопределит директиву компилятора -
p.

     И однако, почему может возникнуть необходимость исполь-
зовать  соглашение  о связях Паскаля вообще?  Для этого есть
две главных причины.

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

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

     Однако, использование соглашения о связях Паскаля может
 
                           - 227 -
вызвать некоторые проблемы.

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

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

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

     Резюме:если вы  собираетесь  использовать  вС-программе
соглашение освязяхПаскаля,  не забывайте о необходимости ис-
пользовать прототипы функций везде, где это возможно, а каж-
дую функцию явно объявляйте pascal или cdecl.  Полезно также
разрешить  выдачу   сообщения   "Function   call   with   no
prototype" ("вызов функции без прототипа"), чтобы гарантиро-
вать наличие прототипов всех функций.

Подготовка к вызову .ASM из Turbo C++

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

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

     Turbo Assembler  (TASM)  предлагает  вам три упрощенных
сегментных директивы (.CODE,  .DATA и .DATA?),  которыемогут
быть использованыпри определении этих сегментов. Они говорят
компилятору о необходимостииспользовать имена  сегментов  по
умолчанию для модели памяти, заданной вами при помощи дирек-
тивы .MODEL.  Например,  если ваша программа на С использует
модель  памяти small,  вы можете организовать каждый ассемб-
лерный модуль с упрощенными сегментными директивами,как  по-
казано в следующей таблице:

 -----------------------------------------------------------
    .MODEL SMALL

    .CODE
    ...кодовый сегмент...

    .DATA
    ...сегмент инициализированных данных...

    .DATA?
    ...сегмент неинициализированных данных...
 
                           - 228 -
 -----------------------------------------------------------
       Стандартные сегментные директивы

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

 Формат файла языка ассемблера       Таблица 6.1
 -----------------------------------------------------------
    code    SEGMENT BYTE PUBLIC 'CODE'
    ASSUME CS:code, DS:dseg
    ...........кодовый сегмент.............
    code    ENDS

    dseg    GROUP _DATA,_BSS
    data    SEGMENT WORD PUBLIC 'DATA'
    ...инициализированный сегмент данных...
    data    ENDS

    _BSS    SEGMENT   WORD PUBLIC 'BSS'
    ...неинициализированный сегмент данных...
    _BSS    ENDS
    END
 -----------------------------------------------------------

Идентификаторы code,  data и dseg в данном макете имеют спе-
циальные заменители,  зависящие от используемой модели памя-
ти;  в таблице 6.2 показано, какое имя должно использоваться
для  тойили  иной модели.  имя_файла в Таблице 6.2 - это имя
модуля:  оно должно быть тем же в директиве NAME и при заме-
нах идентификаторов.

Отметим,что вслучаемоделипамятиhuge  сегмент _BSS отсутству-
ет,  а определение GROUP опускается полностью. В целом, _BSS
представляет собой опцию; определение ее необходимо только в
случае использования.

Лучший способ создания "заготовки" для  будущей ассемблерной
программы  состоит в том,  чтобы скомпилировать пустую прог-
рамму в .ASM-файл (при помощи опции TCC -S) и  затем изучить
сгенерированный таким образом ассемблерный код.

 Замены идентификаторов и модели памяти        Таблица 6.2
 -----------------------------------------------------------
 Модель        Замены идентификатораУказатели кода и данных
 -----------------------------------------------------------
 Tiny,Small    code = _TEXTКод:DW _TEXT:xxx
       data = _DATAДанные: DW DGROUP:xxx
       dseg = DGROUP

 Compact       code = _TEXTКод:DW _TEXT:xxx
       data = _DATAДанные: DD DGROUP:xxx
       dseg = DGROUP

 Medium        code = имя_файла_TEXTКод:DD:xxx
       data = _DATAДанные: DW DGROUP:xxx
       dseg = DGROUP

 Large       code = имя_файла_TEXTКод:DD:xxx
       data = _DATAДанные: DD DGROUP:xxx
       dseg = DGROUP

 Huge       code = имя_файла_TEXTКод:DD:xxx
       data = имя_файла_DATAДанные: DD:xxx
 -----------------------------------------------------------

 
                           - 229 -
Определение данных - констант и переменных

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

Некоторые определения  используют DW (определение слова),  а
некоторые - DD (определение двойного  слова),  что  означает
размер результирующего указателя. Числовые и текстовые конс-
танты определяются нормальным образом.

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

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

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

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

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

Turbo Assembler (TASM) не учитываетрегистры, которыми набра-
ны символы идентификаторов;  другими словами,при ассемблиро-
вании  программы все идентификаторы записываются только заг-
лавными буквами.  Опция TASM /mx устанавливает учет регистра
для общих и внешних имен.  Компоновщик Turbo C++ также запи-
сывает идентификаторы extern заглавными буквами, поэтому тут
все  должно работать.  В наших примерах ключевые слова и ди-
рективы записываются заглавными буквами,  а все прочие иден-
тификаторы и коды операций строчными; это соответствует сти-
лю имен в справочном руководстве по TASM.Вам предоставляется
свобода любых комбинаций заглавных и строчных букв в иденти-
фикаторах, по вашему усмотрению.

Для того,  чтобы идентификаторы были видимыми извне  ассемб-
лерного модуля, вы должны объявить их как PUBLIC.
 
                           - 230 -

Например, если высобираетесь написать модуль с целочисленны-
ми функциями max и min,  а также целочисленными  переменными
MAXINT,  lastmaxи  lastmin,  вам следует поместить в кодовый
сегмент оператор

    PUBLIC  _max, _min

 и операторы

    PUBLIC   _MAXINT, _lastmax, _lastmin
    _MAXINT   DW  32767
    _lastmin  DW  0
    _lastmax  DW  0

 в сегмент данных.

 TASM 2.0

Turbo Assemblrt  2.0  расширяет  синтаксис  многих директив,
позволяя задавать опциональный спецификатор языка. Например,
если вы укажете С в вашем модуля в директиве .MODEL,  то все
имена идентификаторов будут записываться в  объектный модуль
с  ведущим  символом подчеркивания.  Это средство такжеможет
работать на уровне директив. При помощи спецификатора языкаC
в Turbo Assembler 2.0 вышеприведенные объявления можно пере-
писать в виде:

    PUBLIC C max, min
    PUBLIC C MAXINT, lastmax, lastmin
    MAXINT    DW  32767
    lastmin   DW  0
    lastmax   DW  0

Подготовка к вызову Turbo C++ из .ASM

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

Ссылки к функциям

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

    EXTRN fname : fdist

где fname - это имя функции,  а fdist - это либо near,  либо
far,  в зависимости от того,  является ли функция С near или
far. Поэтому в кодовом сегменте может находиться оператор

    EXTRN _myCfunc1:near, _myCfunc2:far

что позволяет вызывать myCfunc1 и myCfunc2 из подпрограмм на
языке ассемблера.

 TASM 2.0

Используя спецификатор языкаС в Turbo Assembler2.0 последний
оператор можно переписать как:

    EXTRN C mCfunc1:near, myCfunc2:far
Ссылки к данным

Для обращения  к переменным следует поместить в сегмент дан-
ных соответствующий оператор(ы) EXTRN в формате:
 
                           - 231 -

    EXTRN vname : size

где vname - это имя переменной,  а size указывает размер пе-
ременной.

      Размер переменной может быть следующим:

    BYTE(1 байт)  QWORD(8 байтов)
    WORD(2 байта)  TBYTE(10 байтов)
    DWORD(4 байта)

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

    int  i,jarray[10];
    char ch;
    long result;

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

    EXTRN _i:WORD,_jarray:WORD,_ch:BYTE,_result:DWORD

либо при  помощи спецификатора языка С в Turbo Assembler 2.0
(TASM 2.0):

    EXTRN C i:WORD,jarray:WORD,ch:BYTE,result:DWORD

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

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

  Определение подпрограмм на языке ассемблера

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

Предположим, что вы хотите написать функцию min, для которой
предполагается наличие соответствующего прототипа С:

    extern int min(int v1, int v2);

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

  PUBLIC  _min
    _min  PROC  NEAR
  ...
    _min  ENDP

Разумеется, это  предполагает,что min является ближней функ-
цией;  если бы эта функция была дальней, вы бы подставилиFAR
вместо NEAR.  Отметим,  что мы добавили передmin символ под-
черкивания,  благодаря чему компоновщик Turbo C++ может пра-
вильно разрешить ссылки. Если бы мы использовали в операторе
PUBLIC спецификатор языка С Turbo Assembler  2.0,  ассемблер
позаботился бы об этом сам.

      Передача параметров

Прежде всего,  вы должны решить,  какое соглашениео передаче
 
                           - 232 -
параметров использовать;  при  отсутствииадекватной  причины
избегайте соглашения о передаче параметров Паскаля, соглаше-
ние С является предпочтительным. Это означает, что когда min
вызвана, стек будет выглядеть следующим образом:

    sp + 04:   v2
    sp + 02:   v1
    sp:        адрес возврата

Вам требуется получить доступ к параметрам, не выполняя сня-
тие со стека,  поэтому вам следует сохранить указатель  базы
(BP), переслать указатель стека (SP) в указатель базы, а за-
тем использовать последний для прямой индексации  стека, что
позволит получить необходимые значения. Отметим, что при по-
мещении BP в стек относительные смещения  параметров  увели-
чатся на 2, поскольку стек теперь увеличится на два.

 TASM 2.0

Turbo Assembler  2.0 обеспечивает простой способ обращения к
параметрам функции и работы со стеком.  Прочтите  следующее;
для вас важно понять, как работает адресация стека.



  Обработка значений возврата

Ваша функция возвращает целочисленное значение;  куда же оно
помещается?  Для  16-битовых  (2-байтовых)  значений  (char,
short,  int,  enum иближних указателей) используется регистр
AX;  для 32-битовых (4-байтовых) значений (включая указатели
far  и  huge) используется также регистр DX,  причем старшее
слово (в случае указателей это адрес сегмента)  помещается в
DX, а младшее слово помещается в AX.

Значениятипа float,  double и long double возвращаются через
регистр "вершины стека" (TOS), ST(0); если используется эму-
лятор 80x87, то значение возвращается через регистр TOS эму-
лятора.  Вызывающая функция должна скопировать это значение-
туда, куда требуется.

Структуры длиной  в 1 байт возвращаются через AL.  Структуры
длиной в 2 байта возвращаются через AX.  Структуры длиной  4
байта возвращаются через DX:AX. Для возврата структур, имею-
щих размер 3 байта или более 5 байтов,  онипомещаются в  об-
ластьстатических данных и затем возвращается указатель на их
адрес (через AX для моделей данных small и через  DX:AX  для
моделей данных large). вызываемая подпрограмма должна скопи-
ровать значение возврата по адресу,  задаваемому указателем.

В примере с функцией min выимеетедело с 16-битовым  значени-
ем, поэтому ответ можно поместить непосредственно в AX.

      Так будет выглядеть этот код теперь:

   PUBLIC  _min
    _min   PROC    NEAR
   push    bp;записать bp в стек
   mov   bp,sp;скопировать sp в bp
   mov   ax,[bp+4];переслать v1 в ax
   cmp   ax,[bp+6];сравнить с v2
   jle   exit ;если v1 > v2
   mov   ax,[bp+6];то загрузить v2 в ax
    exit:  pop   bp;восстановить bp
   ret;и выполнить возврат в С
    _min   ENDP

 
                           - 233 -
Что, если вы объявитеmin как дальнюю (far) функцию - что из-
менится в результате этого?  Главноеотличие будетсостоять  в
том, что стек на входе в подпрограмму будет выглядеть следу-
ющим образом:

    sp + 06:  v2
    sp + 04:  v1
    sp + 02:  сегмент возврата
    sp:       смещение возврата

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

   PUBLIC  _min
    _min   PROC    FAR
   push    bp;записать bp в стек
   mov   bp,sp;скопировать sp в bp
   mov   ax,[bp+6];переслать v1 в ax
   cmp   ax,[bp+8];сравнить с v2
   jle   exit ;если v1 > v2
   mov   ax,[bp+6];то загрузить v2 в ax
    exit:  pop   bp;восстановить bp
   ret;и выполнить возврат в С
    _min   ENDP

Отметим,что все смещения для v1 и v2 увеличились на  2,  что
отражает дополнительно помещенные в стек два байта.

Что будет,  если  вы  решите использовать последовательность
передачи параметров Паскаля?

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

    SP + 04:  v1
    SP + 02:  v2
    SP:       адрес возврата

Кроме того,   вам  придетсясоблюдать  соглашения  Паскалядля
идентификатора min: он должен быть записан заглавными буква-
ми и не иметь символа подчеркивания в начале.

Помимо того, что должны поменяться местами v1 и v2, это сог-
лашение также подразумевает,  что min должна очищать стекпри
выходе,  задавая в команде RET число байтов,  которые должны
сниматься со стека.  В данном случае требуетсяснять со стека
4  дополнительных байтадля v1 и v2 (адрес возврата снимается
со стека автоматически командой RET).

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

   PUBLIC  MIN
    MIN    PROC    NEAR ;версия с соглашениями Паскаля
   push    bp;записать bp в стек
   mov   bp,sp;скопировать sp в bp
   mov   ax,[bp+6];переслать v1 в ax
   cmp   ax,[bp+4];сравнить с v2
   jle   exit ;если v1 > v2
   mov   ax,[bp+4];то загрузить v2 в ax
    exit:  pop   bp;восстановить bp
   ret   4;очистить стек и выполнить
;возврат в С
    MIN    ENDP

Приведем последний  пример того,почемуможет понадобиться ис-
 
                           - 234 -
пользование последовательностьпередачи параметров С. Предпо-
ложим, вы переопределили min следующим образом:

    int min (int count,...);

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

    i = min(5, j, limit, indx, lcount, 0);

предполагая, что i,  j,  limit,  indx и lcount имеют тип int
(или любой совместимый с ним тип).  Стек после входа в подп-
рограмму будет иметь вид:

    sp + 08:  (и т.д.)
    sp + 06:  v2
    sp + 04:  v1
    sp + 02:  count
    sp:       адрес возврата

      Модифицированная версия min будет иметь теперь вид:

   PUBLIC  MIN
    _min   PROC    NEAR
   push    bp;записать bp в стек
   mov   bp,sp;скопировать sp в bp
   mov   cx,[bp+4];переслать count в cx
   cmp   cx,0 ;сравнить с 0
   jle   exit ;если <= 0 то выход из подпрограммы
   lea   bx,[bp+6];установить bx
   mov   ax,[bx];переслать первое значение
   imp   ltest;проверить цикл
  compare: cmp   ax,[bx];сравнение
   jle   ltest;если след. значение
   mov   ax,[bx];то загрузить в ax...
    ltest: add   bx,2 ;переход к новому значению
   loop    compare;продолжение цикла
    exit:  pop   bp;восстановить bp
   ret;возврат в С
    _min   ENDP

Данная версии  подпрограммы  будет правильно обрабатыватьвсе
возможные значения счетчика count:

- Если count <= 0, то min возвращает 0.

- Если count = 1,  то min возвращает первое значение в спис-
ке.

- Если count >= 2, то min выполняет последовательность срав-
нений до последнего переданного ей в списке значения.

     Теперь, когда вы понимаете,  как  нужно  манипулировать
стеком  и  умеете  писать свои собственные функции,вы можете
оценить некоторые новые расширения  версии  Turbo  Assembler
2.0.  Некоторые из них позволяют вам автоматически создавать
имена переменных,  устанавливать и очищать стек из  PROC,  а
также  легко  выполнять  доступ к параметрам,  используя при
этом соглашения того языка,  на котором написана  вызывающая
процедура.

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

  PUBLIC C MIN
    min   PROC C NEAR v1: WORD, v2: WORD
  mov ax,v1
  cmp ax,v2
  jle exit
  mov ax,v2
    exit: ret
    min   ENDP

     Версия с соглашениями Паскаля (стр.259 оригинала) может
быть переписана в виде:

  PUBLIC PASCAL MIN
    min   PROC PASCAL NEAR v1: WORD, v2: WORD
  mov ax,v1
  cmp ax,v2
  jle exit
  mov ax,v2
    exit: ret
    min   ENDP

     Отметим, что код в обоих случаях отличается только клю-
чевым словом PASCAL вместо С,  а в остальных  он  идентичен.
Однако,  код, фактически генерируемый ассемблером, соответс-
твует исходным примерам. Полное описание этих новых средств,
учитывающих  конкретные языки при смешанномпрограммировании,
см. в руководствах по Turbo Assembler.

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

     Если подпрограмма  на  языке ассемблера выполняет вызов
любой оверлейной процедуры или функции,  то эта подпрограмма
должна  быть  дальней  (far) и устанавливать стековый фрейм,
используя для этого регистр BP.  Более подробную  информацию
см. на стр.217 оригинала.

    Соглашения о регистрах

     В min  было  использовано несколько регистров (BP,  SP,
AX,BX, CX); было ли это использование безопасным? Как обсто-
ит дело с регистрами,  которые может использовать ваша прог-
рамма на Turbo C++?

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

     Два остальных регистра,  на которые также следует обра-
щать внимание, это SI и DI; Turbo C++ использует эти два ре-
гистрадля любыхрегистровых переменных. Есливы используете их
в вашей ассемблерной подпрограмме, то при входе в нее следу-
ет сохранить эти регистры(возможно, в стеке),и затем восста-
новить их при выходе. Однако, при компиляции программы Turbo
C++ сопцией-r(или при  выключенной  опцииRegister  Variables
диалогового  поля Code Generation) вы можете не беспокоиться
о сохранении SI и DI.

 Примечание

     При использовании опции -r- следует принимать меры пре-
досторожности.  См.  Главу4, "Компилятор командной строки" в
 
                           - 236 -
Руководстве пользователя, где данная опция описана подробно.

     Регистры CS,  DS, SS и ESпринимают конкретные значения,
в  зависимости от используемоймоделипамяти.  Ниже приводится
эта взаимозависимость:

    Tiny       CS = DS = SS
       ES = рабочий

    Small, Medium      CS != DS, DS = SS
       ES = рабочий

    Compact, Large     CS != DS != SS
       ES = рабочий
       (один CS на модуль)

    Huge       CS != DS != SS
       ES = рабочий
       (один CS и один DS на модуль)

     Вы можете установить DS не равным SS для  моделей tiny,
small и medium, задавая опции компилятора командной строки -
mtl,  -msl и -mml.  См. Главу 4, "Компилятор командной стро-
ки"  в Руководстве пользователя,  где эти опции описаны под-
робно.

 TASM 2.0

     Turbo Assembler2.0 позволяетзадавать это (DS != SS) при
использовании  упрощенных сегментных директив и модификатора
модели в директиве .MODEL.

Вызов функций С из модулей .ASM

     Вы можете поступитьи следующимобразом:  вызывать  подп-
рограммы на С из модулей на языке ассемблера.  Прежде всего,
для этого вы должны сделать функцию С видимой для  модуля на
языке ассемблера. Мы уже кратко рассматривали, как это дела-
ется:  функция должна быть объявлена как EXTRN и иметь моди-
фикатор либо near, либо far. Например, вы написали следующую
функцию С:

    long docalc(int *fact1, int fact2, int fact3);

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

    EXTRN _docalc:near

     Аналогичным образом, если функция использует модели па-
мяти medium, large или huge, то онадолжна иметь объявление
 _docalc:far.

 TASM 2.0

     Используя в  Turbo  Assembler 2.0 спецификатор языка С,
эти объявления можно переписать в виде

    EXTRN C docalc:near

 и

    EXTRN C docalc:far

      docalc должна вызываться с тремя параметрами:
 
                           - 237 -

 - адресом памяти с именем xval

 - значением, хранимым в адресе памяти с именем imax

 - третьим значением - константой 421 (десятичной)

     Предположим также, что вы собираетесь сохранить резуль-
тат  в  32-битовом адресе памяти сименем ans.  Эквивалентный
вызов в С имеет вид:

    ans = docalc(&xval,imax,421);

     Сначала вы должны поместить в стек константу 421, затем
imax и наконец, адрес xval, после чего вызвать docalc. После
возврата вы должны очистить стек, в котором будет находиться
лишних шесть байтов, а потом переслать ответ по адресу ans и
ans+2.

      Код будет иметь следующий вид:

    mov   ax,421    ;взять 421 и поместить в стек
    push  ax
    push  imax    ;взять imax и поместить в стек
    lea   ax,xval    ;взять &xval и поместить в стек
    push  ax
    call  _docalc    ;вызвать docalc
    add   sp,6    ;очистить стек
    mov   ans,ax    ;переслать в ans 32-битовый результат
    mov   ans+2,dx    ;включая старшее слово

 TASM 2.0

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

    EXTRN  C docalc:near

    mov    bx,421
    lea    ax,xval
    calc   docalc C ax,imax,bx
    mov    ans,ax
    mov    ans+2,dx

     Полное описаниеэтих новых средств см. в руководствах по
Turbo Assembler 2.0.

     Как быть, еслиdocalcиспользует соглашениео передаче па-
раметров Паскаля?  В этом случае вамнужно изменить на проти-
воположный порядок передачи параметров и не выполнять очист-
ку стека после возврата,  поскольку подпрограмма сделает это
завас сама.Кроме того, имя docalc должно быть записано в ис-
ходном ассемблерном коде по правилам Паскаля (т.е. заглавны-
ми буквами и без ведущего символа подчеркивания).

      Оператор EXTRN будет иметь следующий вид:

    EXTRN  DOCALC:near

 а сам код, вызывающий docalc:

    lea   ax,xval    ;взять &xval и поместить в стек
 
                           - 238 -
    push  ax
    push  imax    ;взять imax и поместить в стек
    mov   ax,421    ;взять 421 и поместить в стек
    push  ax
    call  DOCALC    ;вызвать docalc
    mov   ans,ax    ;переслать в ans 32-битовый результат
    mov   ans+2,dx    ;включая старшее слово

     Turbo Assembler версии 2.0 включает  в  себя  несколько
расширений,  которыеупрощают интерфейс между модулями с сог-
лашениями Паскаля и на языке ассемблера,  включая  автомати-
ческое создание имен в стиле,  свойственном Паскалю, и поме-
щение  параметров  в  стек  в  той  последовательности,  что
принята в Паскале. Например, подпрограмму docalc можно пере-
писать в виде:

    EXTRN  PASCAL docalc:near

    lea    ax,xval
    mov    bx,421
    calc   docalc PASCAL ax,imax,bx
    mov    ans,ax
    mov    ans+2,dx

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

                         - 239 -
     Псевдопеременные, встраиваемые   ассемблерные   коды  и
функции прерывания

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

     Блок центрального  процессора  вашей  системы  (8088или
80х86) имеет несколько  регистров,или  специальных  областей
памяти,  используемых для манипулирования значениями. Каждый
регистр имеет длину16 битов (2 байта);  большинство  из  них
имеет  специальное назначение,  а некоторые также могут быть
использованыв качестве регистров общего назначения. См. раз-
дел  "Модели  памяти" на стр.187 оригинала Главы 4,  где ре-
гистры центрального процессора описаны более подробно.

     Иногда при программировании на нижнем уровне  вам может
понадобиться доступ из программы на С непосредственно к этим
регистрам.

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

     - Вам может понадобиться узнать,  какие значения содер-
жатся там в текущий момент.

     Например, вы можете вызвать конкретные подпрограммы из-
ПЗУ вашего компьютера,  выполнив для этого команду INT (пре-
рывания),  но сначала вам требуется поместить  в  конкретные
регистры определенную информацию:

     void reaches(unsigned  char  page,  unsigned  char *ch,
unsigned char *attr);
    (*
       _AH = 8;  /* Служебный код: читает символ, атрибут*/
       _BH = page; /* Задает страницу дисплея */
       geninterrupt(0x10);  /* Вызов прерывания INT 10h */
       *ch = _AL; /* Прием ASCII-кода считанного символа */
       *attr = _AH /* Прием атрибута считанного символа */
    *)

     Как выможетевидеть,  подпрограмме  INT  10h  передается
служебный код и номер страницы;  возвращаемые значения копи-
руются в ch и attr.

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

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

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

     - Присвоение псевдопеременным констант также не ведет к
разрушению данных в прочих регистрах, за исключением присво-
ений сегментным регистрам (_CS,_DS,_SS,_ES), которые исполь-
зуют регистр _AX.

 
                           - 240 -
     - Простое  обращение  по  ссылке  через переменную типа
указателя обычно влечет разрушение данных в одном из следую-
щих регистров: _BX, _SI или _DI, а также, возможно, _ES.

     - Если вам требуется выполнить установку нескольких ре-
гистров (например,  при обращении к ПЗУ-резидентным подпрог-
раммам),  безопаснее  использовать _AX последним,  поскольку
другие операторы могут привести к случайному  его изменению.

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

 Псевдопеременные       Таблица 6.3
 ----------------------------------------------------------
 Псевдо-
 переменная  Тип    Регистр  Назначение
 ----------------------------------------------------------
 _AX     unsigned int    AX      Общего назначения/сумматор
 _AL     unsigned char   AL      Младший байт AX
 _AH     unsigned char   AH      Старший байт AX

 _BX     unsigned int    BX      Общего назначения/индексный
 _BL     unsigned char   BL      Младший байт BX
 _BH     unsigned char   BH      Старший байт BX

 _CX     unsigned int    CX      Общего назн./счетчик циклов
 _CL     unsigned char   CL      Младший байт CX
 _CH     unsigned char   CH      Старший байт CX

 _DX     unsigned int    DX      Общего назн./хранение данных
 _DL     unsigned char   DL      Младший байт DX
 _DH     unsigned char   DH      Старший байт DX

 _CS     unsigned int    CS      Адрес кодового сегмента
 _DS     unsigned int    DS      Адрес сегмента данных
 _SS     unsigned int    SS      Адрес стекового сегмента
 _ES     unsigned int    ES      Адрес вспомогат. сегмента

 _SP     unsigned int    SP   Указатель стека (смещение в SS)
 _BP     unsigned int    BP   Указатель базы (смещение в SS)
 _DI     unsigned int    DI   Используется для регистровых
     переменных
 _SI     unsigned int    SI      Используется для регистровых
     переменных
 _FLAGS      unsigned int    флагов  Состояние процессора
 -----------------------------------------------------------

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

     - С  псевдопеременными нельзя использовать операцию ад-
ресации (&), поскольку псевдопеременные не имеют адреса.

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

     Это означает, что присваивать значения псевдопеременным
нужно непосредственно перед тем,  как эти значения будут ис-
 
                           - 241 -
пользованы, а считывать значения - сразу же после их получе-
ния,  как в предыдущем примере. Это особеннокасается регист-
ров  общего  назначения  (AX,  AH,  AL  и  т.д.),  так   как
компилятор  свободно  использует  эти  регистры для хранения
промежуточных значений. Таким образом, процессор может изме-
нять  значения этих регистров неожиданно для вас;  например,
CX может использоваться в циклах и операциях сдвига,а  в  DX
может помещаться старшее слово 16-битового умножения.

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

    _CX = 18;
    myFunc();
    i = _CX;

     Привызовефункции сохраняются не все значения регистров,
тем самым нет никаких гарантий, что i будет присвоено значе-
ние18.  Единственными регистрами,которые наверняка сохраняют
свое значение после вызова функции, являются _DS,_BP,_SI и _
DI.

     - Следует быть очень осторожным при модификации некото-
рых регистров,  поскольку это может иметь весьма неожиданный
и нежелательный эффект. Например, прямое присвоение значений
псевдопеременным CS,_DS,_SS,_SP или _BP может  (и  наверное,
так  и  произойдет)  привести  к  ошибочному поведению вашей
программы,  так как машинный код,  создаваемый  компилятором
Turbo C++,  использует эти регистры самыми различными спосо-
бами.

Встраиваемые ассемблерные коды

     Вы уже знаете,  как писать  отдельные  подпрограммы  на
языке  ассемблера икомпоновать их с программой на Turbo C++.
Turbo C++ позволяет также  встраивать  ассемблерные  коды  в
С-программу.Это средство называется встроенным ассемблирова-
нием.

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

     По умолчанию -B запускает TASM. Это умолчание можно пе-
реопределить  опцией -Exxx,  где xxx - это другой ассемблер.
Подробную информацию см.  в Главе 4,  "Компилятор  командной
строки", Руководства пользователя.

     Для использования  данного средства вы должны иметь ко-
пию Turbo Assembler (TASM).  Сначала  компилятор  генерирует
ассемблерный  файл,  а затем запускает для этого файла TASM,
который создает .OBJ-файл.

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

     Если все эти условия  выполнены,  то  для  включения  в
С-программу встроенных команд на языке ассемблера достаточно
использовать ключевое слово asm. Формат этой команды:
 
                           - 242 -

    asm код-операции операнды;или новая-строка

 где

     - код-операции это одна из допустимых команд  8086 (все
коды-операций 8086 приводятся ниже в таблице 6.4.

     - операнды  -  это  допустимый (допустимые) для данного
кода-операции операнд(ы); это могут быть константы, перемен-
ные и метки С.

     - ;или  новая-строка  - это либо точка с запятой,  либо
символ новой строки, обозначающие конец оператора asm.

     Новый оператор asm может находиться в той же строке че-
рез  точку  с  запятой,  однако никакой оператор asm неможет
быть продолжен в новой строке.

     Если вы хотите включить в программу несколько  операто-
ров asm, возьмите их в фигурные скобки:

    asm (*
       pop ax; pop ds
       iret
    *)

     Точки сзапятой в данном случае не могут служить призна-
ком начала комментария (как в TASM). Длякомментирования опе-
раторов asm следует использовать комментарии С, например:

    asm mov ax,ds;/* Этот комментарий допустим */
    asm (*pop ax; pop ds; iret;*) /* Этот тоже допустим */
    asm push ds ;ЭТОТ КОММЕНТАРИЙ НЕВЕРЕН !!

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

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

      Каждый оператор asm считается оператором С. Например,

    myfunc()
    (*
       int i;
       int x;
       if (i>0)
  asm mov x,4
       else
  i = 7;
    *)

     Данная конструкция представляет собой допустимый опера-
тор С.  Отметим, чтоточка с запятой после команды mov x,4 не
требуется.  Операторы asm являются единственными операторами
С,  зависящими от наличия символа новой строки.  Этоне соот-
ветствует практике, принятой для остальной части языкаС, но-
зато соответствует соглашению, принятому в нескольких компи-
ляторах на базе UNIX.
 
                           - 243 -

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

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

    int min (int V1, int V2)
    (*
       asm (*
  mov ax,V1
  cmp ax,V2
  jle minexit
  mov ax,V2
       *)
       minexit:
       return (_AX);
    *)

     Отметим схожесть  данного кода с кодом настр.260 ориги-
нала,  который используетрасширение Turbo Assembler, связан-
ное с заданием конкретного языка.

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

 - обычные команды - стандартный набор кодов операций 8086

 - строковые команды - специальные коды обработки строк

 - команды перехода - различные коды операций перехода

     - директивы  ассемблирования - размещения и определения
данных

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


     Коды операций
 ___________________________________________________________
 ___________________________________________________________

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

 Мнемонические имена кодов операций       Таблица 6.4
 -----------------------------------------------------------
      aaafdvtr  fpatan    lsl
      aadfeni  fprem     mov
      aamffroe**   fplan     mul
      aasfiadd  frndint   neg
      adcficom  frstor    nop
      addficomp  fsave     not
      andfidiv  fscale    or
      boundfidifr  fsqrt     out
      callfild  fst    pop
      cbwfimul  fstcw     popa
      clcfincstp** fslenv    popi
 
                           - 244 -
      cldfinit  fstp    push
      clifist  fstsw     pusha
      cmcfistp  fsub    pushf
      cmpfisub  fsubp     rcl
      cwdfisubr  fsubr     rcr
      daafld  fsubrp    ret
      dasfld1  ftst    rol
      decfldcw  fweit     ror
      divfldenv  fxam    sahf
      enterfldl2e  fxch    sal
      f2xm1fldl2t  fxtract   sar
      fabsfldlg2  fyl2x     sbb
      faddfldln2  fyl2xp1   shl
      faddpfldpi  hlt    shr
      foldfldz  idiv    smsw
      fbstpfmul  imul    stc
      fchsfmulp  in    std
      fclexfnclex  inc    sti
      fcomfndisi  int    sub
      fcompfneni  into    test
      fcomppfninit  iret    verr
      fdecstp** fnop  lahf    verw
      fdisifnsave  lds    wait
      fdivfnstcw  lea    xchg
      fdivpfnstenv   leave     xlat
      fdivrfnstsw  les    xor
 -----------------------------------------------------------

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

     При использовании в операторах встроенного  ассемблиро-
вания  мнемонических  команд 80186 необходимо включать опцию
командной строки -1. Тогда компилятор включит в генерируемый
им ассемблерный код соответствующие операторы,  в результате
чего Turbo Assembler будет ожидать появление  данныхмнемони-
ческихимен.При  использовании  предыдущих  версий ассемблера
эти мнемонические имена могут не поддерживаться.

 Строковые команды
 ___________________________________________________________

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

 Строковые команды       Таблица 6.5
 -----------------------------------------------------------
      capslasw  movsb     outswstos
      capsblods  movsw     scasstosb
      capswlodsb  outs    scasbstosw
      laslodsw  outsb     scasw
      lasbmovs
 -----------------------------------------------------------


 Префиксы
 ___________________________________________________________

      Допустимы следующие префиксы:

    lock   rep reperepnerepnzrepz


   Команды перехода
 
                           - 245 -
 ___________________________________________________________

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

 Команды перехода       Таблица 6.6
------------------------------------------------------------
      jajge  jnc    jnp        js
      jaejl  jne    jns        jz
      jbjle  jng    jnx        loop
      jbejmp  jnge    jo       loope
      jcjna  jnl    jp       loopae
      jcxzjnae  jnle    jpe        loopnz
      jejnb  jno    jpo        loopz
      jgjnbe
 -----------------------------------------------------------

  Директивы ассемблирования
 ___________________________________________________________

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

    db   dd  dw extra

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

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

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

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


Встроенное ассемблирование и регистровые переменные
 ___________________________________________________________

     Встроенный ассемблерный код может свободно использовать
рабочие  регистры SIи DI.При использовании во встроенном ас-
семблерномкоде регистров SI и DI компилятор не станет  расп-
ределять их для регистровых переменных.


     Встроенное ассемблирование,  смещения и переопределение
размера
 ___________________________________________________________

     Во времяпрограммирования вам не требуется  знать точные
смещения локальных переменных.  При использовании имени пра-
вильное значение смещения будет включено автоматически.
 
                           - 246 -

     Однако, может оказаться необходимым включение в ассемб-
лерную команду соответствующего WORD PTR,  BYTE PTR, или лю-
бого другого переопределения размера.  Переопределение DWORD
PTR требуется задавать в командах LES или косвенного дальне-
го вызова.

      Использование компонентов структур С
 ___________________________________________________________
 ___________________________________________________________

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

    struct myStruct (*
       int a_a;
       int a_b;
       int a_c;
    *) myA;

    myfunc ()
    (*
       ...
       asm (*mov  ax, myA.a_b
    mov  bx, [di].a_b
   *)
       ...
    *)

     Мы объявили  тип  структуры  с  именем myStruct с тремя
компонентами,a_a,  a_b и a_c;  мы также объявили  переменную
myA типа myStruct. Первый оператор встроенного ассемблирова-
ния пересылает значение из myA.a_b в регистрAX.  Второй опе-
ратор пересылает значение по адресу [di]+смещение(a_c) в ре-
гистр  BX(он  беретадрес,хранимый  в  DI,  и  складывает  со
смещениемa_c относительно начала myStruct.) В такой последо-
вательностиэти ассемблерные операторы образуют следующий ас-
семблерный код:

    mov  ax, DGROUP : myA+2
    mov  bx, [di+4]

     Для чего это может понадобиться? Загрузив регистр (нап-
ример,    DI)    адресом    структуры   типа   myStruct   вы
можетеиспользовать имена  компонентов  для  непосредственных
ссылок  к этим компонентам.  Фактически имя компонента может
быть использовано везде,  где в качестве операнда ассемблер-
ного операторадопустима числовая константа.

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

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

    asm  mov  bx,[di].(struct tm)tm_hour


      Использование команд перехода и меток
 ___________________________________________________________
 ___________________________________________________________

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

     В следующем примере кода переход выполняется к  метке C
goto a.

    int     x()
    (*
    a:    /* это метка команды C goto  "a"  */
       ...
       asm  jmp  a  /* переход к метке"a" */
       ...
    *)

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

      Функции прерывания

     8086 резервирует первые 1024 байта памяти для набора из
256 дальних указателей,  называемых также  векторамипрерыва-
ния,  указывающих на специальные системные подпрограммы, ко-
торые называются обработчиками прерываний.  Эти подпрограммы
вызываются при выполнении следующей команды:

    int int#

     где int# это число от 0h до FFh. Когда встречается дан-
ная команда,  компьютер сохраняет кодовый сегмент (CS), ука-
затель команд (IP) и состояния флагов,  затем запрещает пре-
рывания и выполняет дальний переход по  адресу,  на  который
указывает соответствующий вектор прерывания. Например, часто
встречается прерывание

    int 21h

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

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

    void interrupt myhandler(bp, di, si, ds, es, dx,
     cx, bx, ax, ip, cs, flags, ...);

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

     Функция типа interrupt автоматически  сохраняет (помимо
SI,  DI и BP) регистры от AX до DX и DS. Эти же регистры при
выходе из обработчика прерывания восстанавливаются.

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

     Функция прерывания может модифицировать передаваемые ей
параметры. Изменение объявленных параметров приведет к моди-
фикации соответствующего регистра при выходе  из обработчика
прерывания. Это свойство может оказаться полезным, когда об-
работчик прерывания используется как служебнаяфункция  поль-
зователя, какэто происходит вслучаеслужебной функции DOS INT
21.  Кроме того, обратите внимание на то, что функция преры-
вания  выполняет  выход  с помощью команды IRET (возврата из
прерывания).

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

 Практические примеры программ низкого уровня

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

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

    #include   <dos.h>

     void interrupt   mybeep(unsigned   bp,   unsigned   di,
unsigned si, unsigned ds, unsigned es, unsigned dx, unsigned
cx, unsigned bx, unsigned ax)
    (*
       int    i, j;
       char   originalbits, bits;
       unsigned char  bcount = ax >> 8;

       /* прием текущих установок управляющего порта */
       bits = originalbits = inportb(0x61);

       for (i = 0; i <= bcount; i++) (
  /* временное выключение динамика */
  outportb(0x61, bits & 0xfc);
  for (j = 0; j <= 100; j++)
     ; /* пустой оператор */
 
                           - 249 -

  /* теперь динамик на некоторое время включается */

  outportb(0x61, bits \! 2);
  for (j = 0; j <= 100; j++)
     ; /* еще один пустой оператор */
  )
       /* восстановление установок управляющего порта */
       outportb(0x61, originalbits);
    *)

     Затем напишем  функцию,  которая  будет  инсталлировать
данный обработчик прерываний.  Ей передается адрес  и  номер
обработчика (от 0 до 255 или от 0x00 до 0xFF).

    void install(void interrupt (*faddr)(), int inum)
    (*
       setvect(inum, faddr);
    *)

     И наконец,  вызовем для проверки написанную вами сигна-
лящую подпрограмму. Это сделает следующая функция:

    void testbeep(unsigned char bcount, int inum)
    (*
       _AH = bcount;
       geninterrupt(inum);
    *)

      Функция main может иметь вид:

    main()
    (*
       char ch;

       install(mybeep,10);
       testbeep(3,10);
       ch = getch();
    *)

     Вы можете также сохранить исходный вектор  прерывания и
восстановить его при выходе из главной программы.  Для этого
служат функции getvect и setvect.

   Глава 7Сообщения об ошибках


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

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

 ------------------------------------------------------------
 Обобщенное имя Фактическое имя или значение
 в данном руководствевыводимое на экран
 ------------------------------------------------------------
 аргументАргумент командной строки или иной аргумент
 классИмя класса
 полеСсылка на поле
 
                           - 250 -
 имя_файлаИмя файла (с расширением или без)
 группа Имя группы
 идентификаторИдентификатор (имя переменной или другой)
 языкНазвание языка программирования
 компонентИмя компонента данных или функции компонента
 сообщениеСтрока сообщения
 модуль Имя модуля
 числоФактическое число
 опцияОпция командной строки или другая опция
 параметрИмя параметры
 сегментИмя сегмента
 спецификаторСпецификатор типа
 символическое_имяСимволическое имя
 XXXXh4-значное шестнадцатиричное число,
за которым следует h
 ------------------------------------------------------------

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

     Например, если у вас имеется функция  С++  goforit,  то
фактически вы можете получить сообщение об ошибке

    goforit must be declared with no arguments

     Для того, чтобы найти описание данного сообщения в этой
главе, искать следует сообщение

    функция must be declared with no arguments

 в начале списка сообщений об ошибках.

     Если же некоторая переменная включается в текст сообще-
ния позже (например, "Incorrect command-line argument: аргу-
мент"), то такое сообщение можно найти по алфавиту, в данном
случае на букву I.

    Сообщения об ошибках времени выполнения
 -----------------------------------------------------------

     Количество ошибок времени выполнения в Turbo C++  неве-
лико. Эти ошибки могут быть обнаружены в уже откомпилирован-
ной и выполняющейся программе. В данном разделе они перечис-
лены в алфавитном порядке и приводятся с объяснениями.


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

 Abnormal program termination
 Аварийное завершение программы

     Данное сообщение может появляться,  если для выполнения
программы не может быть выделено достаточного количества па-
мяти.  Более подробно оно рассматривается в  конце  раздела,
касающегося ошибок операций с плавающей точкой.  Вызов abort
также приводит к появлению данного сообщения.

 Divide by 0
 Деление на ноль

     Данное сообщение  выдается при целочисленном делении на
 
                           - 251 -
ноль, например

    int n = 0;
    n = 2 / n;

     Эту ошибку можно отследить при помощи функции signal. В
противном случае вызывается abort,  и программа завершается.

 Floating point error:Divide by 0.
 Ошибка операции с плавающей точкой:Деление на 0.
 Floating point error:Domain.
     Ошибка операции с плавающей точкой:Выход из области оп-
ределения.
 Floating point error:Overflow.
 Ошибка операции с плавающей точкой:Переполнение.

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

     - "Деление  на 0" означает,  что результат равен в точ-
ности +INF или -INF (плюс или минус неопределенность),  нап-
ример для операции 1.0/0.0.

     - "Выход из области определения" означает,  что резуль-
тат равен NAN (not  a  number  -  не  число),  например  для
0.0/0.0.

     - "Переполнение"  означает,  что  результат  равен +INF
(неопределенность) или -INF при полной потере точности, нап-
ример  в случае присвоения 1e20*1e20 переменной типа double.

 Floating point error:Partial loss of precision.
     Ошибка операции  с  плавающей  точкой:Частичная  потеря
точности.
 Floating point error:Underflow.
     Ошибка операции  с плавающей точкой:Отрицательное пере-
полнение.

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

 Floating polnt error:Stack fault.
 Ошибка операции с плавающей точкой:Сбой в стеке.

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

     Этой ошибки можно избежать,  маскируя  особые  ситуации
таким образом, чтобы они не появлялись, либо перехватывая их
функцией signal. См. подробное описание функций _control87 и
signal в Справочнике по библиотеке.

     В каждом  из приведенных случаев программа печатает со-
общение об ошибке и затем вызывает  abort,  которая  в  свою
очередь выдает сообщение

    Abnormal program termination

     и вызывает  _exit(3).  См.  подробные  описания функций
abort и _exit.

 Null pointer assignment.
 
                           - 252 -
 Присвоение пустому указателю

     При выходе  из  программы  с  моделью  памяти small или
medium выполняется проверка, чтобы определить, были ли изме-
нены  значения двух первых байтов в сегменте данных програм-
мы.  Эти байты никогда не должны изменяться работающей прог-
раммой.  Если  же  они были изменены,  то выдается сообщение
"Null pointer asignment",  говорящее о том,  что  (вероятно)
некоторое значение было записано в неинициализированный ука-
затель.  Во всех прочих отношениях программа может  работать
правильно;  однако данная ошибка является серьезной ошибкой,
и ей следует заняться немедленно.  Если вы не сможете испра-
вить неинициализированный указатель, это приведет к непредс-
казуемому поведению компьютера (вплоть  до  его  "блокирова-
ния" в  случае  моделей  памяти large,  compact и huge.) Для
отслеживания таких ошибок может служить интегрированный  от-
ладчик, входящий в среду разработки.

 Stack overflow
 Переполнение стека

     По умолчанию размер стека для программ Turbo  C++ равен
4,096 байт. Для большинства программ этого достаточно, одна-
ко программы с рекурсивными функциями или  большими объемами
локальных  данных  могут переполнить стек.  Данное сообщение
выдается только в том случае,  когда включено средство конт-
роля стека.  При получении этого сообщения вы можете перейти
к большей модели памяти, увеличить размер стека, либо умень-
шить использование стека вашей программой. Информацию о том,
как изменить размер стека с  помощью  глобальной  переменной
_stklen, см. в Главе 2, "Глобальные переменные" в Справочни-
ке по библиотеке.  Для  уменьшения  количества  используемых
функцией локальных данных можно поступить так,  как показано
в приводимом ниже примере.  Переменная buffer объявлена  как
static и потому, в отличие от list, не расходует стек.

    void anyfunction( void )
       (*
     static int buffer[ 2000 ];/*размещается в сегменте дан-
ных*/
       int list[ 2000 ]; /*размещается в стеке*/
       *)

     Объявление локальных переменных как  static  имеет  два
недостатка.

     1. Теперь  такая  переменная занимает место,  отводимое
обычно глобальным переменным и куче. (Чтобы заплатить Павлу,
приходится  грабить Петра).  Однако этот недостаток не самый
главный.

     2. Функция не может более являться реентерабельной. Это
означает,  что если функция должна вызываться рекурсивно или
асинхронно, и при этом важно, чтобы каждый вызов функции ра-
ботал со своей собственной копией переменной, то такая пере-
менная не может являться статической. Это связано с тем, что
при  каждом  вызове функции данная переменная будет занимать
ту же самую область памяти,  вместо того, чтобы ей распреде-
лялась всякий раз новая. Также возникает проблема с разделе-
нием доступа к переменной, если функция содержит вызов самой
себя (рекурсивно), либо должна выполняться одновременно сама
с собой (асинхронно).  Для большинства программ DOS  это  не
проблема. Если вы не пишете рекурсивных функций и не работа-
ете в мультизадачном режиме, то вам не о чем беспокоиться. В
противном случае приведенные выше объяснения помогут вам ра-
зобраться, что к чему.
       Сообщения об ошибках компилятора
 
                           - 253 -
 -----------------------------------------------------------

     Диагностические сообщения компилятора Turbo C++ делятся
на три категории: фатальные ошибки, ошибки и предупреждения.

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

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

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

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

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

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

       Фатальные ошибки
 -----------------------------------------------------------

 Bad call of inline function
 Неправильный вызов встроенной функции

     Вы вызвали  встроенную функцию из макро определения, но
сделали это неправильно. Встроенная функция в С должна начи-
наться двумя символами подчеркивания (__).

 Irreducible expression tree
 Неупрощаемое дерево выражения

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

 Out of memory
 Недостаточно памяти

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

 Register allocation error
 Ошибка распределения регистров

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

    Ошибки
 -----------------------------------------------------------

 конструктор cannot return a value
 конструктор не может возвращать значение

     Конструктор С++ не может иметь  выражения  в  операторе
возврата.


 конструктор is not a base class of класс
 конструктор не относится к базовому классу класс

     Конструктор класса  С++  "класс" пытается вызвать конс-
труктор базового класса "конструктор", либо вы пытаетесь из-
менить права доступа класс::конструктор.  Конструктор не от-
носится к базовому классу "класс". Проверьте объявления.


 функция1 cannot be distingulshed from функция2
 функция1 не отличается от функции2

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


 функция is ambiguous
 функция неоднозначна

     Данному вызову  "функции" соответствует по списку аргу-
ментов более одной перегруженной функции (с учетом  преобра-
зования аргументов по умолчанию). Используйте явное приведе-
ние типов одного или более аргументов  для  разрешения  этой
неоднозначности.


 функция must be declared with no arguments
 функция должна быть объявлена без аргументов

     Данная функция-операция  С++ была неправильно объявлена
с аргументами.


 функция must be declared with one argument
 функция должна быть объявлена с одним аргументом

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


 функция must be declared with two arguments
 функция должна быть объявлена с двумя аргументами
 
                           - 255 -

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


 функция was previously declared without static
 функция была ранее объявлена без атрибута static

     Здесь функция объявлена как static,  тогда как выше она
была объявлена как extern (или global).  ANSI C не позволяет
смешивать эти объявления.


 функция was previously declared with the language язык
 функция была выше объявлена с модификатором языка "язык"

     Функция может  иметь  только  один  модификатор   языка
(cdecl,  pascal или interrupt). Данная функция в двух местах
была объявлена с двумя разными модификаторами языка.


 идентификатор cannot be declared in an anonymous union
 идентификатор не может быть объявлен в анонимном объединении

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


 идентификатор cannot be used in a static member function
 идентификатор не может использоваться в статической функции-
 компоненте

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


 идентификатор is inaccessible because also in класс
     идентификатор недоступен поскольку также используется в
классе

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


     идентификатор is  not  a  data  member  and  can't   be
initlallzed here
     идентификатор не является компонентом данных и не может
быть здесь инициализирован

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


 идентификатор is not a member of struct
 идентификатор не является компонентом структуры

     Вы пытаетесь сослатьсяна идентификатор,  как на  компо-
нент  структуры,  в  то время как он не является компонентом
структуры.
 
                           - 256 -

 Проверьте объявления.


 идентификатор is not a parameter
 идентификатор не является параметром

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


 идентификатор is not legal here
 идентификатор здесь недопустим

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


     идентификатор is  virtual  and  cannot  be   explicitly
initialized
     идентификатор виртуальный и не может быть явно  инициа-
лизирован

     Конструктор класса  С++  пытается  вызвать "идентифика-
тор" конструктора базового класса, однако этот идентификатор
относится к виртуальному базовому классу.  Виртуальные базо-
вые классы не могут быть инициализированы  явно.  Компилятор
неявно  вызывает  конструктор  базового  класса по умолчанию
base::base().


 идентификатор must be a member function
 идентификатор должен быть функцией-компонентом

     Большинство функций-операций С++ может являться  компо-
нентами  классов  или  обычными  функциями,  не  входящими в
класс,  однако некоторые из них обязательно должны быть ком-
понентами  класса.  Это  функции  operator  =,  operator ->,
operator() и преобразования типов. Данная функция не являет-
ся функцией-компонентом, но должна являться таковой.



 идентификатор must be a member function or have an argument
 of class type
     идентификатор должен   быть   функцией-компонентом  или
иметь аргумент типа класса

     Большинство функций-операций С++ должно  иметь  неявный
или явный аргумент типа класса. Данная функция-операция была
объявлена вне класса и не имеет явного аргумента типа  клас-
са.



 идентификатор must be a previously defined class or struct
     идентификатор должен быть ранее объявленным классом или
структурой

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



 идентификатор must be a previoustly defined enumeration tag
     идентификатор должен  быть ранее определенным тегом пе-
речислимого типа

     Данное объявление пытается обратиться к идентификатору,
как к тегу типа enum, тогда как он не был объявлен в качест-
ве такового. Исправьте имя или реорганизуйте объявления.



 идентификатор must be a previoustly defined structuretag
 идентификатор должен быть ранее определенным тегом структуры

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



 идентификатор specifies multiple or duplicate access
 идентификатор задает многократный или дублирующийся доступ

     Базовый класс  должен  быть  объявлен  как  public  или
private, но не то и другое одновременно. Спецификатор досту-
па не должен задаваться для базового класса более одного ра-
за.



 компонент is not accessible
 компонент недоступен

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



 спецификатор has already been included
 спецификатор уже был включен

     Данный спецификатор типа встречается в объявлении более
одного раза. Удалите или измените один из них



 = expected
 = ожидается

     Ожидалась операция  присвоения  для инициализации пере-
менной.




 
                           - 258 -


 , expected
 , ожидается

     В списке объявлений,  инициализации или параметров ожи-
дается запятая.



 (* epected
 (* ожидается

     В качестве начала блока или инициализации ожидается ле-
вая фигурная скобка.



 ( expected
 ( ожидается

     Перед списком   параметров   ожидается   левая  круглая
скобка.



 *) expected
 *) ожидается

     В конце блока или инициализации ожидается правая фигур-
ная скобка.



 ) expected
 ) ожидается

     В конце  списка  параметров  ожидается  правая  круглая
скобка.



 : expected alter private/protected/publlc
 : ожидается после private/protected/public

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



 ::requires a preceding identifier in this context
 в данном контексте перед :: требуется идентификатор

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


 .* operands do not match
 операнды .* не соответствуют

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

 
                           - 259 -


 # operator not followed by macro argument name
 за операцией # не следует имя аргумента макроса

     В макро определении символ # может быть использован для
обозначения преобразования аргумента макроса в строку.  За #
должно следовать имя аргумента макроса.



 Access can only be changed to public or procted
     Тип доступа  может  быть  изменен  только на public или
protected

     Производный класс С++ может модифицировать права досту-
па членабазового класса,  но только на public или protected.
 Компонент базового класса нельзя сделать private.



 Access declarations cannot grant or reduce access
     Объявления доступа не могут повысить или понизить права
доступа

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



 Access specifier спецификатор found in a union
 Спецификатор доступа встретился в объединении

     Спецификаторы доступа   С++   (public,   private    или
protected) не могут находиться в объединениях.



 Ambiquity between функция1 and функция2
 Неоднозначность между функцией1 и функцией2

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



 Ambiquous conversion functions: тип1 and тип2
 Неоднозначность функций преобразования: тип1 и тип2

     Компилятор нашел  более  одного  способа преобразования
данного типа в желаемый.  Такая неоднозначность недопустима.



 Array bounds missing ]
 В задании границ массива отсутствует ]

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



 Array must have at least one element
 Массив должен иметь хотя бы один элемент
 
                           - 260 -

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

      Например,

     char ray[]  /* определение массива неизвестного размера
недопустимо */
     char ray[0] /* определение массива нулевого размера не-
допустимо */



 Array of references is not allowed
 Массив ссылок недопустим

     Массив ссылок недопустим, поскольку указатели на ссылки
недопустимы, а имена массивов встраиваются в указатели.



 Array size too lange
 Размер массива слишком велик

      Объявленный массив превышает 64К.



 Assembler statement too long
 Слишком длинный ассемблерный оператор

     Операторы встроенного ассемблирования не  могут  превы-
шать по длине 480 байтов.



 Attempting to return a reference to local name идентификатор
 Попытка вернуть ссылку на локальное имя  идентификатор

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



 Bad file name format in inciude directive
 Неправильный формат имени файла в директиве включения

     Имена включаемых  файлов  должны  заключаться в кавычки
("имя_файла.h") или в угловые скобки  (<имя_файла.h>). Перед
именем  файла  отсутствовала открывающая кавычка или угловая
скобка.  Если использовался макрос,  то результирующий текст
расширения неверен; т.е., он не взят в кавычки.



 
                           - 261 -
 Bad ifdef directive synfax
 Неверный синтаксис директивы ifdef

     Директива ifdef должна содержать в качестве тела дирек-
тивы единственный идентификатор (и ничего более).


 Bad ifndef directive synfax
 Неверный синтаксис директивы ifndef

     Директива ifndef  должна  содержать в качестве тела ди-
рективы единственный идентификатор (и ничего более).



 Bad return type for a type conversion operator
 Неверный тип возврата в операции преобразования типа

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


 Bad syntax for pure function definition
 Неверный синтаксис определения "чистой" функции

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



 Bad undef directive syntax
 Неверный синтаксис директивы undef

     Директива #undef  должна  содержать в качестве тела ди-
рективы единственный идентификатор (и ничего более).



 Base class класс is included more than once
 Базовый класс включен более одного раза

     Класс С++  может быть производным от любого числа базо-
вых классов,  но непосредственно от одного и того же  класса
он может быть производным только один раз.



 Base class класс is initialized more than once
 Базовый класс инициализируется более одного раза

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



 Base class cannot be declared protected
 Базовый класс не может быть объявлен как protected

      Базовый класс С++ может быть public или private, но не
 protected.



 
                           - 262 -
 Bit field cannot be static
 Битовое поле не может быть static

     Только обычные  данные-компоненты классов С++могут быть
объявлены как static, но не битовые поля.


 Bit fields must be signed or unsigned int
 Битовые поля должны быть signed или unsigned int

      Битовое поле должно быть объявлено с интегральным типом
 signed или unsigned. В ANSI C битовые поля могут быть только
 signed или unsigned int (но не char или long).


 Bit fields must contain at least one bit
 Битовые поля должны содержать как минимум один бит

     Вы не можете объявить именованное битовое поле длиной 0
(или менее 0) битов.  Можно объявить  битовое  поле  нулевой
длины без имени, по соглашению используемое для принудитель-
ной установки выравнивания битового поля  по  границе  байта
(или по границе слова,  если выбрана опция выравнивания -a).


 Bit field too large
 Битовое поле слишком велико

     Данная ошибка возникает при попытке определения битово-
го поля длиной свыше 16 битов.



 Body already defined for this function
 Тело этой функции уже определено

     Тело функции с этим же именем и типом  уже  встречалось
выше.  Тело  функции  может  входить в программу только один
раз.



 Call of non-function
 Вызов не-функции

     Вызываемое имя не было объявлено как функция. Эта ошиб-
ка обычно возникает при неправильном объявлении или опечатке
в имени функции.



 Cannot assign идентификатор1 to идентификатор2
 Присвоение идентификатора1 идентификатору2 невозможно

     Обе стороны операции присвоения (=) (или составной опе-
рации  присвоения  типа  +=)  должны  быть совместимыми и не
должны являться массивами.  Правая сторона  данной  операции
присвоения имеет тип идентификатор1 и не может быть присвое-
на объекту слева, имеющему тип идентификатор2.



 Cannot call 'main' from within the program
 Вызвать 'main' из программы невозможно

      С++ не позволяет рекурсивные вызовы 'main'.

 
                           - 263 -


 Cannot cast from идентификатор1 to идентификатор2
 Приведение между идентификатор1 и идентификатор2 невозможно

     Приведение типа идентификатор1  к  типу  идентификатор2
здесь запрещено.  В С указатель может быть приведен к интег-
ральному типу или к другому типу указателя. Интегральный тип
может быть приведен к любому интегральному типу, типу с пла-
вающей точкой и указателю. Тип с плавающей точкой может быть
приведен  к  интегральному или другому типу с плавающей точ-
кой.  Структуры и массивы не позволяют  выполнение  для  них
приведений типа.  Также невозможны приведения для типа void.

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



 Cannot create a varlable for abstract class класс
     Создание переменной для абстрактного класса "класс" не-
возможно

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



 Cannot define a pointer or reference to a reference
 Определение указателя или ссылки на ссылку невозможно

      Иметь указатель на ссылку или ссылку на ссылку нельзя.



 Cannot find класс::класс (класс&) to copy a vector
 Не найден класс::класс (класс&) для копирования вектора

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

     Обычно компилятор создает конструктор ссылки  автомати-
чески.  Однако,  если  вы  определили конструктор для класса
класс2,  имеющего параметр типа класс2. и дополнительные па-
раметры  со  значениями по умолчанию,  то данный конструктор
ссылки не может существовать и не может быть создан компиля-
тором.   (Вследствие  того,  что  класс2::класс2(класс2&)  и
класс2::класс2(класс2&,  int = 1) не различаются  компилято-
ром). Вы обязаны переопределить данный конструктор таким об-
разом,  чтобы не все параметры имели значения по  умолчанию.
Затем  вы можете определить конструктор ссылки или позволить
 
                           - 264 -
компилятору создать собственный.

     Cannot find      идентификатор::идентификатор()      to
initialize a vector
     Не найден идентификатор::идентификатор() для  инициали-
зации вектора

     Если класс  С++  класс1 содержит вектор (массив) класса
класс2 и вы желаете сконструировать объект типа  класс1,  но
при этом не из другого объекта типа класс1, то для конструи-
рования элементов вектора должен быть использован  конструк-
тор класс2::класс2(). Такой конструктор без параметров назы-
вается конструктором по умолчанию. Если вы не определили для
класса2 никакого конструктора,  компилятор создаст конструк-
тор по умолчанию автоматически; в противном случае будет ис-
пользован ваш конструктор.



 Cannot find класс::класс() to initialize base class
 Не найден класс::класс для инициализации базового класса

     При конструировании производного класса С++ класс2 сна-
чала должен быть сконструирован каждый базовый класс класс1.
Если конструктор для класса2 не задает конструктор для клас-
са1  (как  часть заголовка класса2),  то для базового класса
должен быть задан конструктор класс1::класс1().  Такой конс-
труктор  без  параметров называется конструктором по умолча-
нию.  Если вы не определили для класса1 никакого конструкто-
ра,    компилятор    создаст    конструктор   по   умолчанию
автоматически;  в противном  случае  будет  использован  ваш
конструктор.



 Cannot find класс::класс() to initialize field идентификатор
     Не найден класс::класс() для инициализации поля иденти-
фикатор

     Если класс С++ класс1 содержит компонент  класса класс2
и вы желаете сконструировать объект типа класс1, но при этом
не из другого объекта типа класс1,  то  для  конструирования
этого   компонента   должен   быть  использован  конструктор
класс2::класс2(). Такойконструктор без параметров называется
конструктором по умолчанию.  Если вы не определили для клас-
са2 никакого конструктора,компилятор создаст  конструктор по
умолчанию автоматически;  в противном случае будет использо-
ванваш конструктор.



 Cannot find класс::operator=(класс&) to 2  0copy a vector
 Не найден класс::operator=(класс&) для копирования вектора

     Если класс  С++  класс1 содержит вектор (массив) класса
класс2 и вы желаете скопировать класс типа класс1, то должна
использоваться              операция              присвоения
класс2::operator=(класс2&),  позволяющая копирование элемен-
тов вектора.  Обычно компилятор вставляет такую операцию ав-
томатически.  Однако, если вы определили operator= для клас-
са2,  но  эта  операция  не  принимает параметр класс2&,  то
компилятор не будет генерировать операцию автоматически - вы
должны написать свою.




 
                           - 265 -


 Cannot have a near membar in a far class
 Компонент near в классе far недопустим

     Все компоненты класса С++ far должны быть  far.  Данный
компонент должен принадлежать классу, объявленному (или име-
ющему по умолчанию) атрибут near.



 Cannot initialize a fieid
 Инициализация поля невозможна

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



 Cannot initialize тип1 with тип2
 Тип1 не может быть инициализирован типом2

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



 Cannot modify a const object
 Модификация объекта - константы невозможна

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



 Cannot overioad 'main'
 Перегрузка 'main' невозможна

     main - это единственная функция,  которая не может быть
перегружена.



 Cannot specify base classes except when defining the class
     Задавать базовые  классы допустимо только при определе-
нии класса


     При задании  класса С++ базовые классы,  производным от
которых является данный класс,  могут  задаваться  только  в
точке определения класса. При объявлении тега класса, напри-
мер class c; задавать базовые классы недопустимо.



 Case outside of switch
 Оператор case вне оператора switch

     Компилятор встретил оператор case вне оператора switch.
Это часто случается при несоответствии числа правых  и левых
фигурных скобок.


 
                           - 266 -

 Case statement missing :
 В операторе case отсутствует :

     Оператор case должен содержать выражение типа  констан-
ты, за которым следует двоеточие. Либо в выражении оператора
case отсутствует двоеточие,  либо перед двоеточием находится
лишнее символическое имя.



 Character constant too long
 Слишком длинная символьная константа

     Символьные константы могут иметь длину  только  в  один
или два символа.



 Class класс has a constructor and cannot be hidden
 Класс имеет конструктор и не может быть скрытым

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



 Classes cannot be initialized with (**)
 Классы не могут быть инициализированы при помощи (**)

     Обычные структуры  С  могут  инициализироваться набором
значений в фигурных скобках. Классы С++ могут быть инициали-
зированы только конструкторами, если класс имеет конструкто-
ры, компоненты private, функции или базовые классы, являющи-
еся виртуальными.


     Class member компонент declared outside its class
 Компонент класса объявлен вне своего класса

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


 Compound statement missing *)
 В составном операторе отсутствует *)

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


 Conflicting type modifiers
 Противоречащие друг другу модификаторы типа

     Это случается,  когда в объявлении встречается,  напри-
мер, два ключевых слова - far и near, относящихся к одному и
тому  же  указателю.  Одному указателю может соответствовать
только один модификатор адресации,  а  функция  может  иметь
только один модификатор языка (cdecl, pascal или interrupt).


 
                           - 267 -
 Constant expression required
 Требуется выражение типа константы

     Массивы должны объявляться с заданным константой  выра-
жением.  Данная  ошибка  обычно  бывает  вызвана опечаткой в
константе в #define.


 Constructor cannot have a return type specification
 Конструктор не может иметь спецификации типа возврата

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

 Conversion of near pointer not allowed
 Преобразование ближнего указателя недопустимо

     Ближний указатель  не может быть преобразован в дальний
при вычислении выражения, если программа в текущий момент не
выполняется. Причина этого состоит в том, что для преобразо-
вания требуется знать текущее значение DS программы  пользо-
вателя, которое в данный момент просто не существует.



 Could not find a match for аргумент(ы)
 Не найдено соответствие аргументу (аргументам)

     Не найдена функция С++ с  параметрами, соответствующими
заданным аргументам.



 Could not find file имя_файла

     Компилятор не  может  найти файл,  заданный в командной
строке.



 Declaration does not specify a tag or an identifier
 В объявлении не указан тег или идентификатор

     Данное объявление  ничего не объявляет.  Это может быть
структура или объединение без тега или переменная в объявле-
нии. С++ требует, чтобы что-то было объявлено.



 Declaration is not allowed here
 Объявление здесь недопустимо

     Объявления не могут использоваться в управляющих опера-
торах для операторов while, for, do. if или switch.



 Declaration missing ;
 В объявлении отсутствует ;

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




 
                           - 268 -


 Declaration syntax error
 Синтаксическая ошибка в объявлении

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


 Declaration terminated incorrectiy
 Неправильное окончание объявления

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



 Declaration was expected
 Ожидается объявление

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



 Declare operator delete (void*) or (void*,size_t)
 Операция delete объявляется (void*) или (void*,size_t)

     Операция delete  объявляется  с одним параметром void*,
либо с двумя,  где вторым является size_t. При использовании
второй  версии она будет использована с большим приоритетом,
нежели первая. Глобальная операция delete всегда объявляется
с двумя параметрами,  поэтому будьте осторожны при переопре-
делении этого объявления.



 Default outside of swich
 Оператор default вне оператора switch

     Компилятор встретил   оператор  default  вне  оператора
switch.  Это чаще всего бывает при несовпадении числа правых
и левых фигурных скобок.



 Default value missing
 Отсутствует значение по умолчанию

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



 Define directive needs an identifier
 директиве define необходим идентификатор

     Первый не-пробельный символ после  #define  должен  яв-
ляться  идентификатором.  Компилятор  встретил на этом месте
какие-либо другие символы.

 
                           - 269 -


 Destructor cannot have a return type specification
 Деструктор не может иметь спецификации типа возврата

     Деструкторы С++ не имеют типа возврата,  и вы не можете
объявить тип или значение возврата.



 Destructor for класс is not accessibie
 Деструктор для класса недоступен

     Деструктор для  данного  класса  является protected или
private и недоступен из данной точки для  разрушения класса.
Если деструктор класса является private, класс не может быть
разрушен и потому не может никогда быть использован. Это на-
верняка ошибка. Деструктор protected позволяет доступ только
из производных классов. Это полезно для того, чтобы предотв-
ратить  создание базовых классов,  обеспечив создание из них
производных классов.



 Destructor name must match the class name
 Имя деструктора должно соответствовать имени класса

     В классах  С++  объявление  деструктора класса вводится
символом тильда.  Имя деструктора должно совпадать с  именем
класса. В вашем исходном файле тильда предшествует какому-то
другому имени.

 Division by zero
 Деление на ноль

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

 do statement must have while
 оператор do должен иметь while

     В исходном  файле  встретился оператор do без соответс-
твующего ограничивающего ключевого слова while.

 do-whlle statement missing (
 В операторе do-while отсутствует (

     В операторе do компилятор не нашел левой круглой скобки
после ключевого слова while.


 do-whlle statement missing )
 В операторе do-while отсутствует )

     В операторе do компилятор не нашел правой круглой скоб-
ки после условного выражения.


 do-whlle statement missing ;
 В операторе do-while отсутствует ;

     В условном  выражении  оператора do компилятор не нашел
точки с запятой после правой круглой скобки.




 
                           - 270 -


 Dulicate case
 Повторение case

     Каждое ключевое  слово  case  оператора  switch  должно
иметь уникальное значение выражения типа константы.



 Enum syntax error
 Синтаксическая ошибка в enum

     Объявление enum   не  содержит  правильно  оформленного
списка идентификаторов.



 Error directive: сообщение
 Директива error: сообщение

     Данное сообщение  появляется  при  обработке  директивы
#error из исходного файла.  Текст этой директивы выводится в
"сообщении".



 Error writing output file
 Ошибка при записи выходного файла

     Ошибка DOS при попытке Turbo C++ вывести .OBJ, .EXE или
временный файл.  Проверьте опцию командной строки -n или ус-
тановку меню Options \!  Directiries \! Output directory ин-
тегрированной среды,  правильно ли задана директория для вы-
вода. Также убедитесь, что на диске достаточно места.



 Expression expected
 Ожидается выражение

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



 Expression is too complicated
 Слишком сложное выражение

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


 Expression of arithmetic type expected
 Ожидается выражение арифметического типа

     Унарные операции плюс (+) и минус (-) требуют выражений
арифметического  типа  -  допустимыми  являются  только типы
char, short, int long, enum, float, double и long double.


 
                           - 271 -

 Expression of integral type expected
 Ожидается выражение интегрального типа

     Операция дополнения  (тильда ) требует выражения интег-
рального типа  -  допустимыми  являются  только  типы  char,
short, int, long или enum.



 Expression of scalar type expected
 Ожидается выражение скалярного типа

     Операции "не" (!),  инкремента (++) и  декремента  (--)
требуют  выражений  скалярного  типа  - допустимыми являются
только типы char,  short,  int,  long,  enum, float, double,
long double и типы указателей.



 Expression syntax
 Синтаксис выражения

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



 Expression type does not match the return type
 Тип выражения не соответствует типу возврата

     Тип выражения return не может быть преобразован  к типу
возврата функции.


 extern variable cannot be initialized
 Переменная extern не может быть инициализирована

     Класс памяти  extern применительно к переменной означа-
ет,  что переменная здесь объявляется,  но не определяется -
распределение  памяти для нее не происходит.  Следовательно,
инициализация переменной в объявлении невозможно.



 Extra parameter in call
 Лишние параметры в вызове

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



 Extra parameter in call to функция
 Лишние параметры в вызове функции

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



 Field поле cannot be used without an object
 Поле не может быть использовано без объекта
 
                           - 272 -

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



 Field поле is ambiguous in class
 Неоднозначно задан класс поля

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



 Field identifier expected
 Ожидается идентификатор поля

     Ожидалось, но  не найдено имя поля структуры или класса
С++.  Справа от операции (.) или (->) должно находиться  имя
поля структуры или класса, указанных слева от операции.



 File must contaln at least one external declaration
 Файл должен содержать хотя бы одно объявление external

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



 File name too long
 Слишком длинное имя файла

     Имя файла в директиве #include было слишком длинным для
обработки его компилятором. Имена файлов в DOS не могут быть
длиннее чем 79 символов.



 For statement missing (
 В операторе for отсутствует (

     В операторе for компилятор не нашел левой круглой скоб-
ки после ключевого слова for.



 For statement missing )
 В операторе for отсутствует )

     В операторе for  компилятор  не  нашел  правой  круглой
скобки после управляющего выражения.

 
                           - 273 -


 For statement missing ;
 В операторе for отсутствует ;

     В операторе for компилятор не  нашел  точки  с  запятой
после одного из выражений.



 Found : instead of ::
 : вместо ::

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



 Friend declarations need a function signature
 Объявления friend требуют точного указания функции

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



 Friends must be functions or classes, not fields
 Друзья должны быть функциями или классами, но не полями

     Друг (friend) класса С++ должен быть функцией или  дру-
гим классом; поле не может быть другом.


 Function call missing )
 В вызове функции отсутствует )

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


 Function calls not supported
 Вызовы функции не поддерживаются

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


 Function defined inline after use as extern
 Функция определена как встраиваемая после объявления extern

     Функцию нельзя  определить  как  встраиваемую  (inline)
после того,  как она была уже была использована.  Или перес-
тавьте определение inline выше в  тексте,  или  удалите  его
совсем.


 Function definition cannot be a typedef'ed declaration
 Определение функции не может быть объявлением typedef

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

     Например, тип F - это функция без параметров, возвраща-
ющая int:

    typedef int F(void);

      Определение g в качестве такой функции недопустимо:

    F g (* /* ... */ *)

     Однако можно определить  g  как  функцию,  возвращающую
указатель на тип F.

    F *g(...) (* /* ... */ *)


 Function функция cannot be static
 Функция не может быть статической

     Только обычные  функции-компоненты  и  операции  new  и
delete могут быть объявлены static. Конструкторы, деструкто-
ры и прочие операции не могут быть статическими.



 Function cannot return arrays or functions
 Функции не могут возвращать массивы или функции

     Функция объявлена как возвращающая функцию  или массив,
вместо указателя функции или элемента массива.


 Function should return a value
 Функция должна возвращать значение

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


 Functions may not be part of a struct or union
 Функции не могут быть частью структуры или объединения

Данное поле структуры или объединения С было объявлено с ти-
пом функции,  а не указателя функции. Функции в качестве по-
лей разрешены только в С++.


 Global anonimous union not static
 Глобальное анонимное объединение не static

В С++ глобальное анонимное объединение на уровне файла долж-
но быть static.


 Goto statement missing label
 Отсутствует метка в операторе goto

      За ключевым словом goto должен следовать идентификатор.


 Group overflowed maximum size: имя
 Группа превысила максимальный размер: имя

     Общий размер сегментов в группе  (например,  в  DGROUP)
 
                           - 275 -
превысил 64К.


 Identifier идентификатор cannot have a type qualifier
 Идентификатор идентификатор не может иметь квалификатор типа

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


 Identifier expected
 Ожидается идентификатор

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



 If statement missing (
 В операторе if отсутствует (

     В операторе if компилятор не нашел левой круглой скобки
после ключевого слова if.



 If statement missing )
 В операторе if отсутствует )

     В операторе if компилятор не нашел правой круглой скоб-
ки после выражения проверки.


 Illegal character символ (0xзначение)
 Недопустимый символ символ (0хзначение)

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


 Illegal initialization
 Недопустимая инициализация

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


 Illegal octal digit
 Недопустимая восьмеричная цифра

     Компилятор встретил восьмеричную константу с недопусти-
мой в восьмеричных числах цифрой (8 или 9).

 
                           - 276 -

 Illegal parameter to __emit__
 Недопустимый параметр __emit__

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



 Illegal pointer substraction
 Недопустимое вычитание указателя

     Выдается при попытке вычитания указателя из не-указате-
ля.


 Illegal structure operation
 Недопустимая операция со структурой

     Структуры могут использоваться только в операциях точки
(.),  адреса (&) или присвоения  (=),  либо  передаваться  в
функции  и  из  функций  в  качестве параметров.  Компилятор
встретил структуру,  используемую с какой-либо другой опера-
цией.


 Illegal to take address of bit field
 Недопустимо брать адрес битового поля

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


 Illegal use of floating point
 Недопустимое использование плавающей точки

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


 Illegal use of pointer
 Недопустимое использование указателя

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


 Improper use of a typedef идентификатор
 Неправильное использования typedef идентификатор

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


 Improper use of a typedef symbol
 Неправильное использования символического имени typedef

     В исходном  файле символическое имя typedef встретилось
в выражении на месте переменной.  Проверьте объявление этого
 
                           - 277 -
имени и возможные опечатки.



 Incompatible type conversion
 Несовместимое преобразование типа

      Требуемое приведение типа невозможно.


 Incorrect command-line option: опция
 Неправильная опция командной строки: опция

     Компилятор не распознает параметр командной  строки как
допустимую опцию.


 Incorrect configuration file option: опция
 Неправильная опция файла конфигурации: опция

     Компилятор не  распознает  параметр  файла конфигурации
как допустимую опцию; проверьте наличие ведущего дефиса (-).


 Incorrect number format
 Неправильный формат числа

     Компилятор встретил в шестнадцатиричном числе  десятич-
ную точку.


 Incorrect use of default
 Неправильное использование слова default

     Компилятор не нашел после ключевого слова default двое-
точия.



 Inline assembly not allowed in an inline function
 Встроенное ассемблирование во встраиваемой функции запрещено

     Компилятор не  может обрабатывать операторы встроенного
ассемблирования внутри встраиваемых  (inline)  функций  С++.
Ассемблерные  операторы могут быть оформлены в макрос,  либо
вы можете убрать  класс  памяти  inline,  или  можно  убрать
встроенные ассемблерные коды.


 Invalid indirection
 Недопустимое обращение по ссылке

     Операция обращения  по  ссылке  (*)  требует в качестве
операнда не-пустого (не void) указателя.


 Invalid macro argument separator
 Недопустимый разделитель аргументов макроса

     В определении макроса аргументы должны разделяться  за-
пятыми. Компилятор встретил после последнего имени аргумента
другой символ разделителя.


 Invalid point addition
 Недопустимое сложение указателя

 
                           - 278 -
     В исходном файле встретилась попытка сложения двух ука-
зателей.


 Invalid use of dot
 Недопустимо использование точки

     За операцией  "точка" (.) должен непосредственно следо-
вать операнд.


     Items of type тип need constructors and can't be passed
with ...  Элементы типа тип требуют конструкторов и не могут
быть переданы с ...

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


 Left side must be a structure
 Левая часть должна быть структурой

     Левая часть  операции  "точка"  (.)  (или  операции С++
"точка со звездочкой") должна иметь тип структуры.  В данном
случае это не так.


 Linkage specification not allowed
 Спецификация компоновки не разрешается

     Спецификации типа компоновки,  например extern "C", до-
пустимы только на уровне файла. Перенесите данное объявление
функции на уровень файла.


 Lvalue required
 Требуется именующее значение

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




 Macro argument syntax error
 Синтаксическая ошибка в аргументе макроса

     Аргумент в  определении макроса должен являться иденти-
фикатором.  Компилятор встретил некоторый символ, который не
может являться частью идентификатора,  там, где ожидался ар-
гумент.




 Macro expansion too long
 Слишком длинное макрорасширение

     Размер макрорасширения не может превышать 4,096  симво-
лов.



 
                           - 279 -
 main must have a return type of int
 main должна иметь тип возврата int

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


 May compile only one file when an output file name is given
     При заданном имени выходного файла  возможна компиляция
только одного файла

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


 Member компонент initialized more than once
 Компонент инициализируется более одного раза

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


 Member functions can only have static storage class
 Функции-компоненты могут иметь только класс памяти static

     Единственным классом   памяти,   допустимым  для  функ-
ции-компонента, является static.


 Misplaced break
 Неправильно расположенный break

     Компилятор обнаружил  оператор  break  вне  конструкции
оператора switch или цикла.


 Misplaced continue
 Неправильно расположенный continue

     Компилятор обнаружил  оператор continue вне конструкции
цикла.



 Misplaced decimal point
 Неправильно расположенная десятичная точка

     Компилятор обнаружил   десятичную  точку  в  экспоненте
константы с плавающей точкой.


 Misplaced elif directive
 Неправильно расположенная директива elif

     Компилятор обнаружил директиву #elif без  соответствую-
щей ей директивы #if, #ifdef или #ifndef.


 Misplaced else
 Неправильно расположенный else

     Компилятор обнаружил оператор else без соответствующего
ему оператора if.  Это сообщение может быть вызвано как лиш-
ним оператором else,  так и лишней точкой с запятой, отсутс-
 
                           - 280 -
твием фигурной скобкой,  либо некоторой синтаксической ошиб-
кой в предыдущем операторе if.


 Misplaced else directive
 Неправильно расположенная директива else

     Компилятор встретил директиву #else без соответствующей
ей директивы #if, #ifdef или #ifndef.


 Misplaced endif directive
 Неправильно расположенная директива endif

     Компилятор встретил директиву #endif без  соответствую-
щей ей директивы #if, #ifdef или #ifndef.


 Multiple base classes require explicit class names
 Множественные базовые классы требуют явных имен классов

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


 Multiple declaration for идентификатор
 Множественное объявление идентификатора

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


 Multiple scope qualifiers
 Множественные квалификаторы контекста

     Данный идентификатор  С++  был квалифицирован более чем
одним именем класса; идентификатор может быть квалифицирован
максимум одним классом.


 Must take address of a memory location
 Должен существовать адрес памяти

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


 Need an identifier to declare
 Для объявления требуется идентификатор

     В данном контексте для завершения объявления требовался
идентификатор.  Причиной этого сообщения мог  стать  typedef
без имени или лишняя точка с запятой на уровне файла.  В С++
это могло быть имя класса,  неправильно использованное в ка-
честве другого рода идентификатора.


 'new' and 'delete' not supported
 `new' и 'delete' не поддерживаются

 
                           - 281 -
     При вычислении   выражения  интегрированным  отладчиком
операции new и delete не поддерживаются.


 No : following ?
 Нет : после ?

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


 No base class to initialize
 Отсутствует базовый класс для инициализации

     Конструктор класса  С++  пытается  неявно вызвать конс-
труктор базового класса,  тогда как этот класс был  объявлен
без базовых классов. Проверьте объявления.


 No body defined for this inline function
 Не определено тело данной встраиваемой функции

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


 No constructor parameters allowed for array of class
 Параметры конструктора для массива класса запрещены

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


 No file name ending
 Неправильное окончание в имени файла

     В имени  файла оператора #include отсутствует необходи-
мая завершающая имя кавычка или угловая скобка.


 No file names given
 Не заданы имена файлов

     Командная строка компилятора Turbo C++ командной строки
(TCC) не содержит имен файлов.  Вы обязаны задать имя исход-
ного файла.


 No matching )
 Нет соответствующей )

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


 Nonportable pointer conversion
 Немобильное преобразование указателя

     Требуется выполнение неявного преобразования между ука-
 
                           - 282 -
зателем и интегральным типом,  но эти типы имеют разный раз-
мер. Такое преобразование не может быть выполнено без явного
приведения типов.  Такое преобразование  может  и  не  иметь
смысла,  поэтому проверьте, действительно ли оно вам необхо-
димо.



 Non-virtual function функция declared pure
 Не-виртуальная функция объявлена чистой

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


 Not an allowed type
 Неразрешенный тип

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


 Not a valid expression format type
 Недопустимый тип формата выражения

     Недопустимый спецификатор формата после выражения в ок-
не вычислений или наблюдения. Допустимым спецификатором фор-
мата является опциональное число повторений, за которым сле-
дует символ формата (c, d, f[n], h, x, m, p, r или s).


 No type information
 Нет информации о типе

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


 Numeric constant too large
 Числовая константа слишком велика

     Строковые и символьные  управляющие  последовательности
больше шестнадцатиричного \xFF или восьмеричного \377 сгене-
рированы быть не могут.  Двухбайтовые  символьные  константы
могут  быть заданы при помощи второй обратной наклонной чер-
ты. Например, \x0D\x0A представляет собой двухбайтовую конс-
танту. Числовой литерал после управляющей последовательности
следует разбит:

    printf("\x0D" "12345");

      Тем самым будет выведен возврат каретки и затем 12345.


 Object must be initialized
 Объект должен быть инициализирован

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


 Only one of a set of overloaded functions can be функция
     Только один из набора перегруженных функций  может быть
 
                           - 283 -
функция

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


 Operand expected
 Ожидался операнд

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


 Operands are of different or incompatible type
 Операнды имеют разные или несовместимые типы

     Левая и правая части бинарной  операции  (+,  /,  ==  и
т.д.) не могут быть объединены в подобном выражении.


 Operator [] missing ]
 В operator[] отсутствует ]

     В С++ operator[] была объявлена как operator[. Вы долж-
ны добавить недостающую квадратную скобку или  еще  как-либо
исправить объявление.


 operator -> must return a pointer or a class
 operator -> должна возвращать указатель или класс

     Функция С++  operator-> должна быть объявлена как возв-
ращающая класс или указатель на  класс  (или  структуру  или
объединение).  В любом случае это должно быть нечто такое, к
чему применима операция ->.


 Operator cannot be applied to these operand types
 Операция не применима к этим типам операндов

     Левая или правая сторона бинарной операции (+,-,== и т.
д.) не имеет типа,  допустимого для данной операции;  напри-
мер, вы пытались сложить два массива.


 Operator delete must have a single parameter of type void
 Операция delete должна иметь один параметр типа void

      Перегруженная операция С++ delete была объявлена иначе.


 Operator delete must return void
 Операция delete должна возвращать тип void

      Перегруженная операция С++ delete была объявлена иначе.



 Operator new must have an initial parameter of type size_t
 Операция new должна иметь параметр инициализации типа size_t
 
                           - 284 -

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


 Operator new must have an single parameter of type size_t
 Операция new должна иметь один параметр типа size_t

      Перегруженная операция С++ new была объявлена иначе.



 Operator new must return an object of type void
 Операция new должна возвращать объект типа void

      Перегруженная операция С++ new была объявлена иначе.



 Other objects cannot be declared in a function definition
 Другие объекты не могут быть объявлены в определении функции

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

      Например,

    int f(), j     /* объявление f, запятая допустима,
j также объявляется как int */
     int f() (*return 0;*), j; /* здесь f определяется. поэ-
тому запятая недопустима */


     Overlays only  supported  in  medium,  large  and  huge
memory models
     Оверлеи допустимы  только  для  моделей  памяти medium,
large и huge

     Как объяснялось в Главе 4,  Оверлеи допустимы только  в
программах с моделями памяти medium, large и huge.



 Overloadable operator expected
 Ожидается перегружаемая операция

     Почти все операции С++ могут быть  перегружены.  Единс-
твенными  исключениями  являются  операции  выбора поля (.),
точка со звездочкой (.*),  двойное двоеточие (::) и условное
выражение  (?:).  Операции  препроцессора # и ## не являются
операциями языка С или С++ и потому перегружены быть не  мо-
гут.  Прочие знаки пунктуации, не входящие в число операций,
например,  точка с запятой,  разумеется, также не могут быть
перегружены.


 Overloaded function is not allowed here
 Перегруженная функция здесь не разрешена

     При изменении защиты доступа компонента базового класса
С++ в производном классе этот компонент  не  может  являться
перегруженной функцией.


 Overloaded function resolution not supported
 
                           - 285 -
 Разрешение перегруженной функции не поддерживается

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


 Parameter параметр missing name
 Отсутствует имя параметра

     В заголовке  определения  функции этот параметр состоит
только из спецификатора типа,  без имени параметра.  В С это
недопустимо.  (В С++ это разрешено, но тогда невозможно сос-
латься на параметр функции.)


 Parameter names are used only with a function body
 Имена параметров используются только в теле функции

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

      Примеры объявлений:

int func();   /* объявление без прототипа допустимо */
int func(int, int);   /* объявление с прототипом допустимо */
int func(int i, int j); /* имена параметров в прототипе допус
тимы */
int func(i, j);  /* только имена параметров недопустимы*/


 Pointer required on left side of ->
 В левой части -> требуется указатель

     В левой части операции (->) не допустимо  ничего, кроме
указателя.


 Pointer to a static member cannot be created
 Указатель на статический компонент не может быть создан

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


     Previously specified default argument value  cannot  be
changed
     Ранее заданное значение умолчания  аргумента  не  может
изменяться

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


 Pure function функция not overriden in класс
 Чистая функция не переопределена в классе

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


 Reference member компонент is not initialized
 Компонент - ссылка не инициализирован
 
                           - 286 -

     Ссылки всегда  должны быть инициализированными.  Компо-
нент класса типассылки должен иметь  инициализатор  во  всех
конструкторахдля данного класса. Это означает, что вы не мо-
жете зависеть от компилятора в вопросе генерации конструкто-
ровдля такого класса,  поскольку неизвестно, как инициализи-
ровать ссылки.


     Reference member   компонент   needs  a  temporary  for
initialization
     Компонент - ссылка требует временную память для инициа-
лизации

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


 register is the only storage class allowed
 единственным разрешенным классом памяти является register

     Единственным разрешенным классом памяти  для параметров
функции является register.


 Repeat count neads an lvalue
 Для счетчика циклов требуется именующее значение

     Выражение перед  запятой  (,) в окне Watch или Evaluate
должно являться допускающим манипуляции с ним участком памя-
ти. Например, следующие выражения недопустимы:

    i++,10d
    x = y, 10m


 Right side of .* is not a member pointer
 Правая часть .* не является указателем компонента

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


 Side effects are not allowed
 Побочные эффекты не разрешены

     В окне  наблюдения (Watch) побочные эффекты,  такие как
присвоения,  ++ или --, не разрешены. Общая ошибка состоит в
использовании x=y (недопустимо) вместо x==y при проверке ра-
венства x и y.


 Size of идентификатор unknown or zero
 Размер идентификатора неизвестен или нулевой

     Данный идентификатор был использован в  контексте,  где
нужен  размер.  Без  размера могут быть объявлены только тег
структуры (структура еще не определена) ,  либо extern  мас-
сив.  Ссылки к таким элементам (например,  операцией sizeof)
или обращение к ним через указатель к этому  типу запрещены.
Реорганизуйте объявление таким образом, чтобы размер данного
идентификатора был известен.
 
                           - 287 -


 sizeof may not be applied to a bit field
 Операция sizeof неприменима к битовому полю

     sizeof возвращает размер объекта данных в байтах и неп-
рименим к битовым полям.


 sizeof may not be applied to a function
 Операция sizeof неприменима к функции

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


 Size of the type is unknown or zero
 Размер типа неизвестен или нулевой

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


 Size of this expression is unknown or zero
 Размер этого выражения неизвестен или нулевой

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


 Statement is required here
 Здесь требуется оператор

     Некоторые части  программ  С  и С++ обязательно требуют
наличие оператора (это может быть просто точка  с  запятой);
он  помещается между меткой и концом блока,  в котором нахо-
дится метка, а также после if, do, while или for.


 Statement missing ;
 В операторе отсутствует ;

     Компилятор встретил выражение с оператором без заверша-
ющей точки с запятой.


 Static and union members cannot require initialization
     Статические компоненты  и компоненты объединений не мо-
гут требовать инициализации

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



 
                           - 288 -



 Storage class класс памяти not allowed for a field
 Класс памяти не разрешен для функции

     В С и С++ функция может быть extern или static.  В  С++
функция может также иметь класс памяти inline.  Никакие дру-
гие классы памяти недопустимы,  и кроме того,  класс  памяти
может быть задан только один.


 Storage class класс памяти is not allowed here
 Класс памяти здесь не разрешен

     Указанный класс памяти здесь не разрешен. Возможно, бы-
ло задано два класса памяти, тогда как разрешен только один.


 Structure size too large
 Слишком большой размер структуры

      В исходном файле объявлена структура размером более 64К.



 Subscripting missing ]
 В индексе отсутствует ]

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


 Switch selection expression must be of integral type
     Выражение выбора  в операторе switch должно быть интег-
рального типа

     Задаваемое в круглых скобках выражение выбора оператора
switch  должно  давать  значение  интегрального  типа (char,
short,  int,  long,  enum). Для того, чтобы выполнить данное
требование, можно воспользоваться явным приведением типов.



 Switch statement missing (
 В операторе switch отсутствует (

     В операторе switch компилятор не смог  обнаружить левой
круглой скобки после ключевого слова switch.



 Switch statement missing )
 В операторе switch отсутствует )

     В операторе switch компилятор не смог  обнаружить левой
круглой скобки после выражения проверки.


 'this' can only be used within a member function
 'this' можно использовать только в функции-компоненте

     В С++  this - это резервированное слово,  которое можно
использовать только в пределах функций-компонентов класса.
 
                           - 289 -


 Too few parameters in call
 Слишком мало параметров в вызове

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


 Too few parameters in call to функция
 Слишком мало параметров в вызове функции

     Вызов названной функции (объявленной  с  использованием
прототипа) имеет слишком мало аргументов.


 Too many decimal points
 Слишком много десятичных точек

     Компилятор встретил константу с плавающей точкой, в ко-
торой находится более одной десятичной точки.


 Too many default cases
 Слишком много операторов слов default

     Компилятор встретил более одного  оператора  default  в
одной конструкции switch.


 Too many error or warning messages
 Слишком много сообщений об ошибке или предупреждений

     Компилятор может  зарегистрировать  до  255  ошибок или
предупреждений, а потом остановит свою работу.


 Too many exponents
 Слишком много экспонент

     Компилятор встретил в константе с плавающей точкой  бо-
лее одной экспоненты.


 Too many initializers
 Слишком много инициализаторов

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


 Too many storage classes in declaration
 В объявлении слишком много классов памяти

     В объявлении должно быть указано не больше одного клас-
са памяти.


 Too many types in declaration
 В объявлении слишком много типов

     Объявление может содержать не более одного из следующих
базовых типов: char, int, float, double, struct, union, enum
или typedef-имя.


 
                           - 290 -
 Too much global data defined in file
 В файле объявлено слишком много глобальных данных

     Сумма объявленных глобальных данных превысила по разме-
ру 64К.  Проверьте объявления массивов,  которые могут  быть
причиной выхода за пределы памяти.  Либо реорганизуйте прог-
рамму,  либо, если все объявления вам нужны, используйте пе-
ременные far.


 Trying to derive a far class from a near base
     Попытка объявить  производный  класс  far  от  базового
класса near

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



 Trying to derive a near class from a far base
     Попытка объявить производный  класс  near  от  базового
класса far

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


 Two consecutive dots
 Две последовательно расположенные точки

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


 Two operands must evaluate to the same type
 Два операнда должны давать один и тот же тип

     Типы выражений  по  обеим сторонам двоеточия в условной
операции (?:) должны быть одинаковыми,  за исключением  тех,
что  позволяют  взаимные  преобразования  типа - char в int,
float в double или void* в конкретный  указатель.  В  данном
выражении  по  обеим  сторонам операции имели различные,  не
преобразуемые автоматически типы.  Это может быть либо ошиб-
кой, либо вам нужно просто выполнить приведение типов участ-
вующих в выражении операндов.


 Type mismatch in parameter номер
 Несоответствие типа параметра номер

     Вызванная через указатель функция была объявлена с про-
тотипом;   данный   же   параметр   с   указанным  "номером"
(отсчитывая слева-направо от 1) не может быть преобразован к
объявленному типу параметра.


 Type mismatch in parameter номер in call to функция
 Несоответствие типа параметра номер в вызове функции

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


 
                           - 291 -
 Type mismatch in parameter параметр
 Несоответствие типа параметра

     Вызванная через указатель функция была объявлена с про-
тотипом;  указанный же параметр не может быть преобразован к
объявленному типу параметра.



 Type mismatch in parameter параметр in call to функция
 Несоответствие типа параметра в вызове функции

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



 Type mismatch in redeclaration of идентификатор
 Несоответствие типа в переобъявлении идентификатора

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


 Type name expected
 Ожидается имя типа

      Произошла одна из следующих ошибок:

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

     - В объявлении typedef не задано имя типа.

     - В объявлении деструктора класса С++  имя  деструктора
не равно имени типа (имя деструктора класса должно совпадать
с именем класса).

     - В задании имени базового класса С++ имя  не  является
именем класса.


 Type qualifier идентификатор must be a struct or class name
     Квалификатор типа   идентификатор  должен  быть  именем
структуры или класса

     Квалификатор С++ в конструкции  квалификатор::идентифи-
катор не является именем структуры или класса.


 Unable to create output file имя_файла
 Невозможно создать выходной файл  имя_файла

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


 
                           - 292 -
 Unable to create turboc.$ln
 Невозможно создать turboc.$ln

     Компилятор не  может создать временный файл TURBOC.$LN,
поскольку отсутствует доступ к диску или диск полон.


 Unable to execute command команда
 Невозможно выполнить команду

      Не найден TLINK или TASM, либо поврежден диск.


 Unable to open include file имя_файла
 Невозможно открыть включаемый файл имя_файла

     Компилятор не может найти  указанный  файл.  Это  может
быть   также  в  том  случае,  когда  включаемый  директивой
#include файл включает сам себя, либо если не задан параметр
FILES  в  файле CONFIG.SYS в корневой директории (попробуйте
установить FILES=20). Проверьте, существует ли действительно
не найденный файл.



 Unable to open input file имя_файла
 Невозможно открыть входной файл  имя_файла

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


 Undefined label идентификатор
 Неопределенная метка  идентификатор

     Указанная метка задана в функции в операторе  goto,  но
определение метки отсутствует.


 Undefined structure структура
 Неопределенная  структура

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


 Undefined symbol идентификатор
 Неопределенное символическое имя  идентификатор

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


 Unexpected *)
 Неожиданное появление *)

     Лишняя фигурная правая скобка встречена там, где она не
ожидалась. Проверьте отсутствие (*.


 
                           - 293 -
 Unexpected ) - check for matching parenthesis
     Неожиданное появление ) - проверьте наличие открывающей
скобки

     Лишняя круглая правая скобка встречена там,  где она не
ожидалась. Проверьте отсутствие (.


 Unexpected : found
 Неожиданное появление :

     Лишнее двоеточие встречено там,  где оно не  ожидалось.
Проверьте отсутствующий или неверно набитый знак ?.


 Unexpected end of file in comment started on номер строки
 Неожиданно встречен конец файла в комментарииномер строки

     Исходный файл  кончился  в  середине  комментария.  Это
обычно происходит при отсутствии признака  конца комментария
(*/).


     Unexpected end of file in conditional started  on номер
строки
     Неожиданно встречен конец файла  в  условной  директиве
номер строки

      Исходный файл кончился раньше, чем компилятор встретил
 #endif. Либо это отсутствие, либо опечатка в #endif.


 Union cannot have a base type
 Объединение не может иметь базовый тип

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



 Union members cannot require initialization
 Компоненты объединения не могут требовать инициализации

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


 Unknown language, must be C or C++
 Неизвестный язык, здесь должен быть С или С++

      В конструкции С++

    extern имя тип func( /*...*/);

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

    extern "C" int pascal func( /*...*/ );

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

    extern int pascal func( /*...*/ );
 
                           - 294 -


 Unknown preprocessor directive: идентификатор
 Неизвестная директива препроцессора: идентификатор

     Компилятор встретил   в   начале  строки  символ  #,  а
следующее за ним имя директивы не являлось допустимым именем
директивы:  define, undef, line, if, ifdef, ifndef, include,
else или endif.


 Unterminated string or character constant
 Незавершенная строка или символьная константа

     Компилятор не нашел завершающей кавычки в  конце строки
или символьной константы.


 Use . or -> to call функция
 Используйте для вызова функции . или ->

      Попытка вызвать функцию-компонент, не задав объект.


 Use :: to take the address of a member function
 Используйте :: для обращения к адресу функции-компонента

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


 Use ; to terminate declarations
 В конце объявлений должна находиться ;

     Данное объявление не заканчивается запятой или точкой с
запятой.


 User break
 Прерывание пользователя

     Вы ввели  во время компиляции или компоновки в интегри-
рованной среде Ctrl-Break.  (Это не ошибка,  а просто  подт-
верждение приема вашей команды прерывания выполнения).


 Value of type void is not allowed
 Значение типа void не разрешено

     Значение типа  void  практически  не является значением
вообще и не может появляться в контексте,  требующем  факти-
чески существующего значения. Таким контекстом может являть-
ся правая часть операции присвоения,  аргумент  функции  или
управляющее выражение условных операторов if, for или while.


 Variable идентификатор is initialized twice
 Переменная  идентификатор  инициализирована дважды

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


 Variable name expected
 Ожидается имя переменной

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


 Vectors of classes must use the default constructor
     Векторы классов   должны  использовать  конструктор  по
умолчанию

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


 Virtual function функция1 conflicts with функция2
 Виртуальная  функция1противоречит  функции2

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


 virtual specified more than once
 virtual задано более одного раза

     Резервированное слово С++ virtual может появиться в оп-
ределении функции-компонента только один раз.


 void & is not a valid type
 void & не является допустимым типом

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


 While statement missing (
 В операторе while отсутствует (

     В операторе while компилятор не обнаружил левой круглой
скобки после ключевого слова while.


 While statement missing )
 В операторе while отсутствует )

     В операторе  while компилятор не обнаружил правой круг-
лой скобки после выражения проверки.


 Wrong number of arguments in call of макрос
 Неверное число параметров при вызове макроса

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


 
                           - 296 -

Предупреждения
 -----------------------------------------------------------

 функция1 hides virtual function функция2
 функция1 скрывает виртуальную функцию2

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


 идентификатор is declared as both external and static
 идентификатор объявлен сразу как external и static

     Данный идентификатор появился в объявлении,  неявно или
явно обозначающем его как global или external, и кроме того,
в объявлении,  обозначающем его как static.  Идентификатор в
таком случае считается static. Проверьте все объявления дан-
ного идентификатора.



 идентификатор declared but never used
 идентификатор объявлен, но нигде не использован

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


 идентификатор is assigned a value that is never used
 идентификатору присвоено значение, нигде не используемое

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


     идентификатор is both a structure tag and a  name,  now
obsolete
 идентификатор одновременно является тегом структуры и имя,
 устаревшая возможность

     В С  допустимо использовать идентификатор сразу как тег
структуры и имя переменной или typedef, как в следующем при-
мере:

    struct s (* int i, j; *) s;

 или

    typedef struct s (* int i, j; *) s;

      В С++ это недопустимо.


 Ambiguous operators need parentheses
 Неоднозначные операции требуют круглых скобок

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


 Assigning тип to перечислимый тип
 Присвоение   типа   перечислимому типу

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


 Assignment to this is obsolete, use X::operator new instead
     Такое присвоение  устарело,  используйте  вместо   него
X::operator new

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


 Base initialization without a class name is now obsolete
 Инициализация базового класса без имени устарела

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

     Это делает  создаваемый код более понятным и необходимо
в случае множественных базовых классов.

      Старый способ:

    derived::derived(int i) : (i, 10) (* ... *)

      Новый способ:

    derived::derived(int i) : base(i, 10) (* ... *)


 Bit fields must be signed or unsigned int
 Битовые поля должны иметь тип signed или unsigned int

     Битовое поле должно быть объявлено имеющее интегральный
тип  со  знаком  или  без.  В ANSI C битовое поле может быть
только signed или unsigned int (а не char или  long,  напри-
мер).


 Both return and return with a value
     Одновременно присутствуют  операторы  return и return с
заданным значением

     Текущая функция имеет операторы return с заданным  зна-
чением  возврата  и без значения возврата одновременно.  В С
это допустимо, но практически всегда является ошибкой. Веро-
ятно, оператор return просто был опущен в конце функции.


 Call to function with no prototype
 Вызов функции без прототипа
 
                           - 298 -

     Это сообщение  выдается в тех случаях,  когда разрешено
сообщение "Prototype required" и вы  вызываете  функцию  без
первоначального задания прототипа этой функции.


 Call to function функция with no prototype
 Вызов функциифункция  без прототипа

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


 Code has no effect
 Код не вызывает никаких действий

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

    a + b;

     не оказывает   воздействия   ни  на  какую  переменную.
Операция не нужна и наверняка записана по ошибке.


 Constant is long
 Длинная константа

     Компилятор встретил либо десятичную константу, превыша-
ющую значение 32767,  либо восьмеричную (или шестнадцатирич-
ную) константу,  превышающую значение 65535 без следующей за
ней буквы l или L. Такая константа будет рассматриваться как
имеющая тип long.


 Constant member компонент is not initialized
 Константа-компонент не инициализирована

     Класс С++ имеет константу-компонент,  которая  не  была
инициализирована. Отметим, что допустимым является инициали-
зировать константу-компонент,  а не присваивать ей значение.



 Constant out of range in comparison
 Участвующая в сравнении константа вне допустимого диапазона

     В исходном файле имеется сравнение, в котором участвует
под-выражение, лежащее вне диапазона, допустимого для прочих
типов под-выражений. Например, сравнение числа unsigned с -1
не имеет смысла. Для того, чтобы получить константу unsigned
больше 32767 (десятичное), требуется либо явно задать приве-
дение типа к unsigned [например,  (unsigned)65535], либо до-
бавить к константе буквы u или U (например, 65535u).

     При выдаче  данного  сообщения  компилятор тем не менее
сгенерирует код для сравнения.  Если даже данный  код  будет
всегда  давать одинаковый результат,  например при сравнении
выражения типа char с 4000,  код все равно  будет  выполнять
сравнение.


 Conversion may lose significant bits
 При преобразовании могут быть потеряны значащие биты

 
                           - 299 -
     В операции  присвоения,  или  в других обстоятельствах,
требуется преобразование long или unsigned long  в  int  или
unsigned  int.  Поскольку  переменные типов int и long имеют
разный размер, такое преобразование может привести к измене-
нию работы программы.


 Declaration does not specify a tag or an identifier
 Объявление не задает тег или идентификатор

     Данное объявление   ничего  не  объявляет.  Обычно  это
структура или объединение без тега или переменная в объявле-
нии.

     Некоторые ранние компиляторы С позволяли объявления ви-
да

    struct (* int a; int b; *);

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


 Declare функция prior to use in prototype
 Функция должна быть объявлена до использования в прототипе

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

    int func(struct s *ps);
    struct s (* /* ... */ *)

     Поскольку структуры s в контексте прототипа для func не
существует,  то типом параметра ps является указатель на не-
определенную структуру s; это не то же самое, что для объяв-
ляемой после структуры s. Все это приведет к дальнейшим пре-
дупреждениям   и   сообщениям   об   ошибке,   говорящих   о
несовместимых типах, которые будут вам непонятны без данного
предупреждения. Для решения этой проблемы вы может перенести
объявление структуры s до любого ссылающегося на нее  прото-
типа, либо добавить неполное объявление типа struct s; перед
каждым ссылающимся на эту структуру прототипом.  Если  пара-
метром функции является структура, а не указатель структуры,
то неполного объявления недостаточно;  вы  должны  поместить
полное объявление структуры перед прототипом.


 Division by zero
 Деление на ноль

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


     Functions containing   резервируемое   слово   are  not
expanded inline
     Функции содержащие  резервируемое  слово  не могут быть
встраиваемыми

     Функции, содержащие резервируемые слова do, for, while,
goto,  switch, break, continue и case, не могут быть встраи-
ваемыми, даже при наличии задания их как inline. Функция мо-
жет  использоваться,  но  будет  рассматриваться как обычная
 
                           - 300 -
статическая (не глобальная) функция.  Копия этой функции бу-
дет находиться в любой единице компиляции, где она вызывает-
ся.


 Function should return a value
 Функция должна возвращать значение

     В исходном файле текущая  функция  определена  с  типом
возврата иным,  нежели int или void,  но компилятор встретил
оператор return без значения возврата.  Обычно это  какая-то
ошибка.  Исключение составляют функции int, поскольку в ста-
рых версиях С типа void для обозначения функций без значения
возврата не существовало.


 Hexadecimal value contains more than 3 digits
 Шестнадцатиричное значение содержит более 3 цифр

     В старых версиях С шестнадцатиричная управляющая после-
довательность могла иметь не более трех цифр. Новый стандарт
ANSI позволяет любое число цифр, лишь бы значение помещалось
в байт.  Это сообщение выдается, когда шестнадцатиричная уп-
равляющая последовательность имеет много ведущих нулей (нап-
ример,  "\x00045"). Старые версии С интерпретировали бы дан-
ную строку иначе.


 Ill-formed pragma
 Неправильно оформленная директива pragma

     Директива pragma не соответствует ни одной из ожидаемых
компилятором директив этого рода.


 Initialization is only partially bracketed
 Инициализация только частично заключена в квадратные скобки

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


 Initialization with inappropriate type
 Инициализация неверным типом

     Переменная типа  enum инициализируется значением невер-
ного типа. Например,

    enum count (* zero, one, two *) x = 2;

     приведет к выводу данного предупреждения,  поскольку  2
имеет тип int,  а не тип enum count. При присвоении или ини-
циализации перечислимого типа лучше пользоваться идентифика-
торами, определенными в данном перечислимом типе, а не цело-
численными литералами.


 Initializing идентификатор with тип
 Инициализация идентификатора типом

     Вы пытаетесь  инициализировать  переменную  типа   enum
 
                           - 301 -
значением  неверного  типа.  Это ошибка,  но выдается только
предупреждение, и программа получает шанс заработать.


 Mixing pointers to signed and unsigned char
 Смешанное использование указателей на signed и unsigned char

     Вы преобразовали  указатель  на  char  в  указатель  на
unsigned  char,  либо  наоборот без явного приведения типов.
(Строго говоря,  это допустимо,  но на 8086 часто приводит к
ошибочным результатам).


 No declaration for function функция
 Отсутствует объявление  функции

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


 Non-const function функция called for const object
     Функция, не имеющая типа константы, вызвана для объекта
-константы

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



 Nonportable pointer comparison
 Немобильное сравнение указателей

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


 Nonportable pointer conversion
 Немобильное преобразование указателя

     Ненулевое интегральное значение используется в контекс-
те,  где ожидается указатель или интегральное значение; раз-
меры указателя и интегрального значения одинаковы.  Если  вы
действительно намереваетесь использовать данное преобразова-
ние, следует выполнить явное приведение типов.


 Obsolete syntax; use :: instead
 Устаревший синтаксис; используйте ::

     Старые версииС++ использовали для разделения имени ком-
понента и имени класса в объявлениях или определениях симво-
лы точка  (.)  или  двоеточие  (:).  Это  устаревший  способ
записи;  следует  использовать вместо него двойное двоеточие
(::).

      Старый способ:

    void myclass:func(int i) (* /*  ... */ *)

 
                           - 302 -
      Новый способ:

    void myclass::func(int i) (* /*  ... */ *)


 Overload is now unnecessary and obsolete
 слово overload теперь не нужно и устарело

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


 Parameter параметр is never used
 Параметр  не используется

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


 Possible use of идентификатор before definition
 Возможное использование идентификатора до определения

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


 Possibly incorrect assignment
 Возможно неправильное присвоение

     Данное сообщение   выдается   в   том   случае,   когда
компилятор встречает вместо главной операции условного выра-
жения операцию присвоения  (т.е.  как  часть  оператора  if,
while или do-while).  Очень часто это просто опечатка.  Если
вы желаете подавить вывод данного сообщения, заключите прис-
воение  в круглые скобки и выполните его явное сравнение как
единого целого с нулем. Таким образом,

    if (a = b) ...

 следует переписать как

    if ((a = b) != 0) ...


 Program flow can skip this initialization; try using (**)
 Поток программы может обойти данную инициализация;
 попробуйте использовать (**)

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


 Redefinition of макрос is not identical
 Переопределение  макроса  не идентично

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


 Restarting compile using assembly
 Перезапуск компилятора с использованием ассемблера

     Компилятор встретил оператор  asm,  не  встретив  перед
этим опции командной строки -B или оператора #pragma inline.
происходит перезапуск  компилятора  с  включенным  средством
встроенного ассемблирования.


 Structure passed by value
 Структура передана по значению

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


 Style of function definition is now obsolete
 Этот стиль определения функции устарел

      В С++ старый стиль определения функции запрещен:

    int func(p1, p2) int p1, p2; (* /* ... */ *)

     Он также не допускается и многими другими компиляторами
С++.


 Superfluous & with function
 Избыточная операция & с функцией

     Операция адресации  (&)  с именем функции не требуется;
любая подобная операция отвергается.


 Suspicious pointer convercion
 Подозрительное преобразование указателя

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


 Temporary used to initialize идентификатор
     Для инициализации идентификатора использовалась времен-
ная переменная

 Temporary used for parameter номер in call to идентификатор
     При вызове    идентификатора    для   параметра   номер
использовалась временная переменная

 
                           - 304 -
     Temporary used for parameter параметр in call to  иден-
тификатор
     При вызове идентификатора для  параметра  параметр  ис-
пользовалась временная переменная

 Temporary used for parameter  номер
 Для параметраномер  использовалась временная переменная

 Temporary used for parameter  параметр
 Для параметрапараметр  использовалась временная переменная

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

     Например, функция  f  требует ссылку на int,  а c имеет
тип char:

    f(int&);
    char c;
    f(c);

     Вместо вызова f с адресом c компилятор  генерирует код,
эквивалентный следующему исходному коду С++:

    int X = c, f(X);


 Undefined structure идентификатор
 Неопределенная структура  идентификатор

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


 Unknown assembler instruction
 Неизвестная команда ассемблера

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


 Unreachable code
 Недоступный код

     За оператором break,  continue, goto или return не сле-
дует метка или конец цикла или функции. Компилятор отыскива-
ет циклы while, do и for с условным выражением типа констан-
ты и пытается распознать циклы,  из  которых  не  существует
условия выхода.


 Untyped bit field assumed signed int
 Нетипизированному битовому полю присвоен тип signed int

     Данное битовое  поле  не  имеет спецификации типа и ему
присваивается тип signed int. Некоторые компиляторы присваи-
 
                           - 305 -
вают таким полям по умолчанию тип signed int. Чтобы это пре-
дупреждение не выдавалось, вы должны сами присвоить битовому
полю тип int или unsigned int.


 Void functions may not return a value
 Функции void не могут возвращать значений

     В исходном  файле  текущая  функция  объявлена  с типом
возврата void,  а компилятор встретил оператор return, в ко-
тором задано значение возврата. Это значение игнорируется.


Приложение А       Стандарты ANSI, зависящие от реализации

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


 2.1.1.3  Как идентифицировать диагностические сообщения

     При запуске  с правильной комбинацией опций любое сооб-
щение, выдаваемое компилятором и начинающееся словами Fatal,
Error или Warning, считается диагностическим в смысле, опре-
деляемом ANSI.  Ниже приводятся опции, необходимые для того,
чтобы обеспечивалась данная интерпретация:

 Идентификация диагностических сообщений в TurboC++
 Таблица A.1
 -----------------------------------------------------------
 Опция   Действие
 -----------------------------------------------------------
 -A   Разрешает только ключевые слова ANSI
 -C-  Запрещает вложенные комментарии
 -p-  Устанавливает использование соглашения о связях С
-i32  Устанавливает минимум 32 значащих символа в идентифика-
      торах
-w-   Выключает все предупреждения, кроме следующего
-wbei Включает предупреждение о несоответствии инициализато-
      ров
-wdcl Включает предупреждение об объявлениях  без  типа  или
      класса памяти
-wcpt Включает предупреждение о немобильных сравнениях  ука-
      зателей
-wdup Включает предупреждение о  дублирующихся не-идентичных
      определениях макроса
-wsus Включает предупреждение о подозрительном  преобразова-
      нии указателя
-wrpt Включает предупреждение о  немобильных преобразованиях
      указателей
-wvrt Включает предупреждение о функции  void,  возвращающей
      значение
-wbig Включает предупреждение о слишком большой константе
-wucp Включает предупреждение о смешанном использовании ука-
      зателей signed и unsigned char
-wstu Включает предупреждение о неопределенных структурах
-wext Включает  предупреждение  о  переменных,  определенных
      сразу как external и static
-wfdt Включает предупреждение об определениях  функции,  ис-
      пользующих typedef
 
                           - 306 -
 -----------------------------------------------------------

      Использование следующих опций запрещено:

 -ms! Для моделей данных small SS и DS должны совпадать.
 -mm! Для моделей данных small SS и DS должны совпадать.
 -mt! Для моделей данных small SS и DS должны совпадать.
 -zGxx Имя группы BSS не может быть изменено.
 -zSxx Имя группы данных data не может быть изменено.

     Прочие опции, не упомянутые здесь специально, могут ус-
танавливаться по вашему желанию.

 2.1.2.2  Семантика аргументов, передаваемых функции main

     Когда программа выполняется в операционной системке DOS
версий до 3.0, значение argv[0] представляет собой указатель
на нулевой байт.  Для версии DOS 3.0 и старше argv[0] указы-
вает на имя программы.

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

     2.1.2.3 Что считается интерактивным устройством

      Любое устройства, работающее как консоль.

     2.2.1Компоненты наборов символов - исходного  и времени
выполнения

     Наборы символов,  исходный и времени выполнения, предс-
тавляют собой расширенный набор символов ASCII, поддерживае-
мый IBM PC.  Любой символ,  кроме ^Z (Control-Z) может нахо-
диться в  строковых  литералах,  символьных  константах  или
комментариях.

     2.2.1.2 Состояния сдвига для многобайтных символов

      Многобайтные символы в Turbo C++ не поддерживаются.

     2.2.2Направление печати

     Печать символов происходит слева-направо,  в нормальном
для PC направлении.

     2.2.4.2 Число битов в символе из набора времени  выпол-
нения

     Символ из набора символов времени выполнения имеет дли-
ну 8 битов.

     3.1.2Число значащих начальных символов идентификатора

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

     3.1.2Учитывается ли регистр во  внешних идентификаторах

     Компилятор обычно заставляет компоновщик делать  разли-
 
                           - 307 -
чие  между заглавными и строчными буквами.  Для того,  чтобы
подавить  учет  регистра,  служит  опция  командной   строки
(-l-c).

     3.1.2.5 Представления  и множества принимаемых значений
для различных интегральных типов

 -----------------------------------------------------------
 Тип Минимальное значение   Максимальное значение
 -----------------------------------------------------------
 signed char -128     127
 unsigned char    0     255
 signed short      -32,768  32,767
 unsigned short     0  65,535
 signed int      -32,768  32,767
 unsigned int    0  65,535
 signed long       -2,147,483,648   2,147,483,647
 unsigned long    0   4,294,967,295
 -----------------------------------------------------------

     Все типы  char используют для хранения значения 8-бито-
вый байт.

      Все типы short и int используют 2 байта.

      Все типы long используют 4 байта.

     Если задано выравнивание в памяти (-a), все объекты ин-
тегральных типов,  кроме char, выравниваются по границе чет-
ных байтов.
 Символьные типы не выравниваются никогда.

     3.1.2.5 Представления  и множества принимаемых значений
для различных типов чисел с плавающей точкой

     Для типов с плавающей точкой Turbo C++ Intel  8087  ис-
пользует форматы плавающей точки IEEE.  Тип float использует
32-битовый формат действительных чисел IEEE.  Тип double ис-
пользует  64-битовый  формат действительных чисел IEEE.  Тип
long double использует 80-битовый расширенный  формат  дейс-
твительных чисел IEEE.

     3.1.3.4 Соотношение  между наборами символов - исходным
и времени выполнения

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

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

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

     3.1.3.4 Значение целочисленной константы,  которая  со-
держит более одного символа, или широкая символьная констан-
та, которая содержит более одного многобайтного символа

 
                           - 308 -
     Символьные константы могут содержать один или два  сим-
вола.  Если включено два символа,  то первый символ занимает
младший байт константы, а второй занимает старший байт.

     3.1.3.4 Текущее место действия, используемое для преоб-
разования  многобайтных  символов  в соответствующие широкие
символы для широкой символьной константы

     Широкие символьные константы распознаются,  но рассмат-
риваются,  как  обычные символьные константы.  В этом смысле
"местом действия" является "С".

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

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

     3.2.1.3 Направление усечения, когда число интегрального
типа  преобразуется  в число с плавающей точкой,  которое не
может в точности представлять исходное число

     Целое число  округляется  до  ближайшего  представимого
значения. 31-1
     Например, значение типа long (2 ) преобразуется в  зна-
чение float 31
     2 .  Разрешение направления округления производится  по
стандартным арифметическим правилам IEEE.

     3.2.1.4 Направление  усечения или округления при преоб-
разовании число с плавающей точкой в число с плавающей  точ-
кой меньшей точности представления.

     Число округляется до ближайшего представимого значения.
Разрешение направления округления производится по  стандарт-
ным арифметическим правилам IEEE.

 3.3  Результаты поразрядных операций для целых со знаком

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

     3.3.2.3 Что происходит,  когда доступ к компоненту объ-
екта типа объединения происходит при помощи компонента  дру-
гого типа

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

     3.3.3.4 Тип целого числа, предназначенного для хранения
максимального размера массива

 
                           - 309 -
     Для обычного массива это тип unsigned int, а для масси-
вов в случае модели данных huge это тип signed long.

 3.3.4Результат приведения типа указателя в целое и обратно

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

     3.3.5Знак остатка при целочисленном делении

     Когда только один  из  операндов  отрицателен,  остаток
также будет отрицательным.  Если ни один из операндов не от-
рицателен,  или оба отрицательны,  остаток будет положитель-
ным.

     3.3.6Интегральный тип, необходимый для хранения разнос-
ти между двумя указателями на  элементы  одного  и  того  же
массива, ptrdiff_t

     Для ближних (near) указателей это тип signed int, а для
указателей far или huge - это signed long. Тип ptrdiff_t за-
висит от используемой модели памяти. Для малых моделей памя-
ти это тип int, а для больших моделей памяти - это тип long.

     3.3.7Результат сдвига вправо отрицательного  интеграль-
ного типа со знаком

     Отрицательные отрицательные   значения  со  знаком  при
сдвиге вправо расширяются по знаку.

     3.5.1Степень вероятности фактического размещения в  ре-
гистрах объектов со спецификатором класса памяти register

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

 3.5.2.1  Заполнение и выравнивание компонентов структур

     По умолчанию заполнение структурсимволами-заполнителями
не  выполняется.  При  использовании опции выравнивания (-a)
структуры дополняются до четного размера,  а любые компонен-
ты,не имеющие символьного типа или типа символьного массива,
выравниваются по четным адресам смещения.

     3.5.2.1 Рассматривается ли линейное  битовое  поле  int
как signed int, или же как unsigned int

     Линейные битовые  поля  int рассматриваются как имеющие
тип signed int.

     3.5.2.1 Порядок распределения битового поля в int

 
                           - 310 -
     Битовое поле распределяется в  направлении  от  позиции
младшего бита к позиции старшего бита.

     3.5.2.1 Может ли битовое поле пересекать границу едини-
цы памяти

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

     3.5.2.2 Интегральный тип, которым представляются значе-
ния перечислимого типа

     Если нумераторов  столько,  что  хватает  типа unsigned
char,  то выбирается этот тип; в противном случае выбирается
signed int.

     3.5.4Максимальное число деклараторов, которые могут мо-
дифицировать арифметический тип, структуру или объединение

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

     3.5.5.3 Что представляет собой доступ к объекту,  кото-
рый квалифицирован типом volatile

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

     3.6.4.2 Максимальное  число  case-вариантов в операторе
switch

     На число вариантов оператора switch специальных ограни-
чений нет.  Если памяти достаточно, то компилятор обработает
все.

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

     Все символьные константы,  даже константы условных  ди-
ректив, используют один и тот же набор символов (времени вы-
полнения).  Одно-символьная константа  будет  отрицательной,
если  это символьный тип со знаком signed char (по умолчанию
и при -K не запрашивается).

     3.8.2Метод нахождения включаемых исходных файлов

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

     3.8.2Поддержка задания  в  кавычках имен включаемых ис-
ходных файлов

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

     3.8.2Особенности последовательности  символов  в именах
файлов

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

 3.8.8Определения __DATE__ и __TIME__ , когда они недоступны

     Дата и время доступны всегда и используют системные да-
ту и время DOS.

 4.1.1Тип операции sizeof, size_t

      Тип size_t  -  unsigned int.

 4.1.1Символ, отображающий  десятичную точку

      Это точка (.).

     4.1.5Константа - пустой указатель, в которую расширяет-
ся макрос NULL

      Int или long 0, в зависимости от модели памяти.

     4.2 Печатаемые  диагностические  сообщения  и поведение
при завершении функции assert

     Печатается диагностическое сообщение "Assertion failed:
выражение, file имя_файла, line nn", где выражение - это вы-
ражение с неудачно завершившейся  функцией  контроля  особой
ситуации,  имя_файла  - это имя исходного файла,  а nn - это
номер строки, где выполнялся контроль.

     После вывода на дисплей данного диагностического  сооб-
щения вызывается функция abort.

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

      Отсутствуют, за исключением описанных в п.4.3.1.

     4.3.1Наборы символов,  проверяемые  функциями  isalnum,
isalpha, iscntrl, islower, isprint и isupper

      Первые 128 ASCII-символов.

     4.5.1Значения, возвращаемые  математическими  функциями
при ошибках, связанных с областью определения

      IEEE NAN (не-число).

     4.5.1Устанавливают ли математические  функции  целочис-
ленное  выражение  errno  в значение макроса ERANGE в случае
ошибки отрицательного переполнения

 
                           - 312 -
     Нет, только для других ошибок -  области  переполнения,
сингулярности, переполнения и общей потери точности.

     4.5.6.4 Происходит  ли ошибка области определения,  или
возвращается ноль, когда функция fmod имеет второй аргумент,
равный нулю

      Нет. fmod(x, 0) возвращает 0.

     4.7.1.1 Набор сигналов функции signal

      SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM.

     4.7.1.1 Семантика   каждого   сигнала,  распознаваемого
функцией

   См. описание функции signal в Справочнике по  библиотеке.

     4.7.1.1 Обработка по умолчанию и обработка при загрузке
программы каждого сигнала, распознаваемого функцией signal

   См. описание функции signal в Справочнике по библиотеке.

     4.7.1.1 Блокирование сигнала,  выполняемое если эквива-
лент signal(sig,SIG_DFL);не выполнен перед вызовом  обработ-
чика сигналов

      Эквивалент signal(sig,SID_DFL) выполняется всегда.

     4.7.1.1 Будет  ли отменена обработка сигнала по умолча-
нию при  получении  сигнала  SIGILL  обработчиком,  заданным
функции signal

      Нет.

     4.9.2Требуется ли  в последней строке текстового потока
оконечный символ новой строки

      Нет, не требуется.

     4.9.2Появляются ли при чтении символы пробела, записан-
ные  в  текстовый поток непосредственно перед символом новой
строки

      Да, появляются.

     4.9.2Число нулевых символов,  которые могут быть добав-
лены к данным, записываемым в двоичный поток

      Не добавляются.

     4.9.3Устанавливается ли первоначально указатель позиции
файла потока в режиме добавления (append) в начало или в ко-
нец файла

     Указатель позиции  файла для потока в режиме добавления
первоначально помещается в начало файла. Перед каждой опера-
цией записи он сбрасывается в конец файла.

     4.9.3Вызывает ли запись в текстовый поток усечение свя-
занного с потоком файла за данной позицией

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

 
                           - 313 -
     4.9.3Существуют ли физически файлы нулевой длины

      Да, существуют.

     4.9.3Может ли  один  и тот же файл быть открыт неоднок-
ратно

      Да, может.

     4.9.4.1 Действие функции remove на открытый файл

     Никакой специальной проверки,  является ли файл  откры-
тым,  не выполняется. Ответственность лежит на программисте.

     4.9.4.2 Что  произойдет,  если  файл с новым именем уже
существовал перед вызовом rename

     rename вернет значение -1,  а errno будет установлена в
значение EEXIST.

     4.9.6.1 Вывод в случае преобразования %p в printf

     В случае ближних моделей данных это четыре шестнадцати-
ричных цифры (XXXX). В случае дальних моделей данных это че-
тыре  шестнадцатиричных цифры,  двоеточие и еще четыре шест-
надцатиричных цифры (XXXX:XXXX).

     4.9.6.2 Ввод в случае преобразования %p в fscanf

      См. 4.9.6.1.

     4.9.6.2 Интерпретация символа  дефис  (-),  который  не
является ни первым, ни последнимсимволом в списке сканирова-
ния в случае преобразования %[ в fscanf

      См. описание fscanf в Справочнике по библиотеке.

     4.9.9.1 Значение,  в  которое  устанавливается   макрос
errno функциями fgetpos или ftell при неудачном завершении

      EBADF   - Неверный номер файла.

     4.9.10.4 Сообщения, генерируемые perror

 -----------------------------------------------------------
 Error 0  Invalid data
 Ошибка 0  Неверные данные

 Invalid function number  No such device
 Неверный номер функции   Такого устройства нет

     No such file or directory  Attempt  to  remove  current
directory
     Такого файла или директории нет Попытка удалить текущую
директорию

 Path not found   Not same device
 Путь не найден   Другое устройство

 Too many open files  No more files
 Слишком много открытых файлов  Файлов больше нет

 Permission denied  Invalid argument
 Разрешение не дано  Неверный аргумент

 Bad file number  Arg list too big
 Неверный номер файла  Список аргументов слишком велик
 
                           - 314 -

 Memory arena trashed  Exec format error
 Испорчена память  Ошибка формата запуска

 Not enough memory  Cross-device link
 Недостаточно памяти  Кросс-компоновка устройств

 Invalid memory block address  Math argument
 Неверный адрес блока памяти  Математический аргумент

 Invalid environment  Result too large
 Неверная операционная среда  Результат слишком велик

 Invalid format   File already exists
 Неверный формат  Файл уже существует

 Invalid access code
 Неверный код доступа
 -----------------------------------------------------------

      См. описание perror в Справочнике по библиотеке.

 4.10.3  Поведение calloc, malloc или realloc, если запрошен
 нулевой размер

     calloc и malloc проигнорируют такой запрос. realloc ос-
вободит блок.

     4.10.4.1 Поведение функции abort в отношении открытых и
временных файлов

      Буферы файлов не очищаются, а файлы не закрываются.

     4.10.4.3 Статус, возвращаемый exit при ненулевом значе-
нии аргумента, EXIT_SUCCESS или EXIT_FAILURE

     Особенные действия не предусмотрены. Статус возвращает-
ся в том виде,  в котором он передан. Статус представлен как
signed char.

     4.10.4.4 Набор имен операционной среды и способ измене-
ния значений переменных операционной среды при помощи getenv

     Строки операционной  среды  те самые,  что определяются
командой DOS SET. Для изменения этих строк на время выполне-
ния текущей программы служит putenv,  но для постоянного из-
менения их нужно использовать команду DOS SET.

     4.10.4.5 Содержимое и режим обработки  строки  функцией
system

     Строка интерпретируется  как  команда DOS.  Запускается
COMMAND.COM,  и аргумент функции передается ему как  команда
на выполнение. Могут быть выполнены любые внутренние команды
DOS, а также .BAT-файлы и .EXE-программы.

     4.11.4.4 Последовательность сравнения  набора  символов
времени выполнения

     Последовательность сравнения  набора  символов  времени
выполнения использует значение символа signed char ASCII.

     4.11.6.2 Содержимое строк сообщений об ошибке,  возвра-
щаемых strerror

      См. 4.9.10.4.

 
                           - 315 -
     4.12.1 Использование локального таймера и хранение вре-
мени в форме "AM/PM"

      Определяются локальные время и данные PC.

     4.12.2.1 Отсчет времени системными часами

     Представлен тактами часов, а начало отсчета считается с
момента запуска программы.

     4.12.3.5 Форматы даты и времени

      Turbo C++ реализует форматы ANSI.
 
