Г Л А В А   8...............................................12
     ВИДЕОФУНКЦИИ ТУРБО СИ.......................................12
     В этой главе................................................12
     Несколько слов о видеорежимах...............................14
     Несколько слов о текстовых и графических окнах..............16
     Что такое окно?.............................................16
     Что такое viewport (графическое окно)?......................17
     Координаты..................................................17
     Программирование в текстовых режимах........................19
     Функции ввода/вывода с консоли..............................19
     Вывод текста и манипуляция с ним............................20
     Управление режимами и окнами................................24
     Управление атрибутами.......................................25
     Запрос состояния............................................27
     Текстовые окна..............................................29
     Тип text_modes..............................................30
     Цвета текста................................................32
     Высокоэфективный вывод: переменная directvideo..............35
     Программирование в графическом режиме.......................37
     Функции графической библиотеки..............................40
     Управление графической системой.............................40
     Более подробное обсуждение..................................44

     Рисование и закрашивание....................................47
     Манипуляция экраном и графическим окном.....................51
     Вывод текста в графическом режиме...........................55
     Управление  цветом..........................................60
     Точки растра и палитры......................................61
     Цвет фона и вычерчивания....................................63
     Управление цветом на CGA....................................63
     Низкое разрешение CGA.......................................64
     Высокое разрешение CGA......................................67
     Функции управления палитрой для CGA.........................68
     Управление цветом на EGA и VGA..............................68
     Обработка ошибок в графическом режиме.......................70
     Запрос состояния............................................74
     Г Л А В А  9................................................79
     ЗАМЕЧАНИЯ ДЛЯ ПРОГРАММИСТОВ, РАБОТАЮЩИХ НА ТУРБО ПАСКАЛЕ....79
     Структура программы.........................................81
     Пример......................................................84
     Сравнение базовых элементов.................................86
     В ы в о д...................................................86
     Т и п ы   д а н н ы х.......................................90
     Операции....................................................94
     Ввод........................................................98

                         - 3,4 -

     Блок  операторов............................................101
     Выполнение по условию.......................................102
     Циклы (итерации)............................................109
     Цикл while (пока)...........................................109
     Цикл do...while (выполнять...пока)..........................110
     Цикл for (для)..............................................112
     Подпрограммы................................................116
     Прототипы  функций..........................................120
     Основной пример.............................................123
     ОБЗОР  СТРУКТУР  ДАННЫХ.....................................128
     Pointers (Указатели)........................................128
     Arrays (Массивы)............................................133
     Strings (Строки)............................................136
     Structures (Структуры)......................................143
     Union (Объединение).........................................147
     Выводы по программированию..................................150
     Чувствительность к регистрам................................150
     Приведение типов............................................151
     Константы, переменные записи и инициализация................152
     Типы констант...............................................153
     Инициализация переменных....................................155
     Переменные памяти...........................................156

     Динамическое распределение памяти...........................157
     Аргументы командной строки..................................161
     Файлы ввода/вывода..........................................164
     Общие ошибки Паскаль программистов при работе на Си.........169
     Ошибка #1: Присваивание и сравнение.........................169
     Ошибка #2: Забывание о передаче адреса......................171
     (особенно, при использовании scanf).........................171
     Ошибка #3: пропуск скобок при вызове функции................172
     Ошибка #4: предупреждающие сообщения........................173
     Ошибка #5: индексация в многомерных массивах................174
     Ошибка #6: Забывание о различиях между символьными
     массивами и символьными указателями.........................176
     Ошибка #7: забывание о том, что Си чувствителен
     к размеру букв (строчные-заглавные).........................178
     Ошибка #8: пропуск точки с запятой в последнем
     операторе блока.............................................179
     Г Л А В А  10...............................................181
     ИНТЕРФЕЙС МЕЖДУ ТУРБО СИ И ТУРБО ПРОЛОГОМ...................182
     В этой главе................................................184
     Компоновка Турбо Си и Турбо Пролога: обзор..................185
     Пример 1: Сложение двух целых чисел.........................191
     Исходный файл Турбо Си: CSUM.C..............................191

                         - 5,6 -

     Компиляция  CSUM.C  в  CSUM.OBJ.............................192
     Исходный файл Турбо Пролога : PROSUM.PRO....................194
     Компиляция PROSUM.PRO в PROSUM.OBJ..........................194
     Компоновка CSUM.OBJ и PROSUM.OBJ............................196
     Инициализация Турбо Пролога:................................196
     Главный модуль Турбо Пролога:...............................196
     Набор модулей:..............................................197
     Модуль таблицы идентификаторов:.............................197
     Имя выходного файла.........................................197
     Библиотеки:.................................................198
     Пример 2: Использование библиотеки математических функций...200
     Компиляция CSUM1.C и FACTRL.C в .OBJ........................202
     И с х о д н ы й  ф а й л Турбо Пролога: FACTSUM.PRO.........203
     Компиляция FACTSUM.PRO в FACTSUM.OBJ........................207
     Компоновка СSUM1.OBJ, FACTRL.OBJ и FACTSUM.OBJ..............207
     Пример 3: Шаблоны аргументов и распределение памяти.........209
     Вызов Турбо Пролога из Турбо Си.............................214
     Списки и функторы...........................................220
     Компиляция DUBLIST.C........................................224
     Пример 4. Рисование 3-х мерных диаграмм.....................225
     Компиляция CBAR.C...........................................226
     Программа Турбо Пролога: PBAR.PRO...........................226

     Компиляция PBAR.PRO в PBAR.OBJ..............................228
     Компоновка PBAR.OBJ с модулем CBAR.OBJ......................228
     Р е з ю м е.................................................230
     ГЛАВА 11....................................................231
     РУКОВОДСТВО  ПО  ЯЗЫКУ  ТУРБО  СИ...........................231
     В этой части................................................233
     Комментарии (K&R 2.1).......................................233
     Идентификаторы (K&R 2.2)....................................235
     Ключевые  слова (K&R 2.3)...................................236
     Константы (K&R 2.4).........................................238
     Целые константы (K&R 2.4.1).................................238
     Символьные константы (K&R 2.4.3)............................241
     Константы с плавающей точкой (K&R 2.4.4)....................244
     Строки (K&R 2.5)............................................245
     Зависимость от аппаратных средств (K&R 2.6).................247
     Преобразования (K&R 6)......................................249
     Char, int и enum (K&R 6.1)..................................249
     Указатели (K&R 6.4).........................................250
     Арифметические преобразования (K&R 6.6).....................251
     Операторы (K&R  раздел 7.2).................................254
     Спецификаторы типов и модификаторы (K&R 8.2)................255
     Тип enum....................................................256

                         - 7,8 -

     Тип void....................................................257
     Модификатор signed..........................................259
     Модификатор const...........................................260
     Модификатор volatile........................................262
     Модификаторы cdecl и pascal.................................264
     pascal......................................................265
     cdecl.......................................................266
     Модификаторы near, far и huge...............................267
     Структуры  и  объединения (K&R раздел 8.5)..................270
     Выравнивание слов...........................................270
     Поля бит....................................................271
     Операторы (K&R 9)...........................................273
     Определения внешних функций (K&R 10.1)......................273
     Модификаторы типа функции (K&R 10.1.1)......................274
     Модификатор функции pascal..................................274
     Модификатор функции cdecl...................................276
     Модификатор функции interrupt...............................278
     Модификаторы функций near, far и huge.......................279
     Прототипы функций (K&R 10.1.2)..............................280
     Правила видимости (K&R 11)..................................290
     Команды управления трансляцией (K&R 12).....................292
     Замена лексем (K&R 12.1)....................................292

     Включение файла (K&R 12.2)..................................295
     Условная компиляция (K&R 12.3)..............................297
     Управление строками (K&R 12.4)..............................299
     Директива error (ANSI Си 3.8.5).............................299
     Директива pragma (ANSI Си 3.8.6)............................301
     #pragma inline..............................................301
     Директива null (ANSI Си 3.7)................................305
     Встроенные макроимена (ANSI Си 3.8.8).......................305
     Встроенные макросы Турбо Си.................................308
     Анахронизмы (K&R 17)........................................310
     Г Л А В А  12...............................................311
     УГЛУБЛЕННЫЙ КУРС ПО ТУРБО СИ................................311
     Модели памяти...............................................312
     Регистры микропроцессора 8086...............................312
     Регистры общего назначения..................................316
     Сегментные регистры.........................................317
     Регистры специального назначения............................317
     Сегментация памяти..........................................319
     Вычисление адреса...........................................320
     Указатели типа NEAR, FAR И HUGE.............................322
     Указатели типа NEAR.........................................323
     Указатели типа FAR..........................................323

                         - 9,10 -

     Указатели типа HUGE.........................................326
     Шесть моделей памяти в Турбо Си.............................329
     Крохотная...................................................329
     Малая.......................................................329
     Средняя.....................................................330
     Компактная..................................................330
     Большая.....................................................331
     Огромная....................................................331
     Порядок программирования смешанных моделей памяти:
     модификация типа адресации..................................345
     Объявление функций как NEAR или FAR.........................348
     Объявление указателей как NEAR, FAR или HUGE................351
     Способ указания на данный сегмент: Offset адрес (смещение)..355
     Построение простых операторов объявления....................356
     Использование библиотечных файлов...........................362
     Компоновка смешанных модулей................................365
     Программирование с совмещением языков.......................369
     Последовательности передачи параметров типа Си и Паскаль....369
     Последовательность передачи параметров типа Си..............370
     Последовательность передачи параметров типа Паскаль.........372
     Интерфейс с языком ассемблера...............................378
     Порядок вызова ассемблера из Турбо Си.......................378


     Определение констант данных и переменных....................384
     Определение глобальных и внешних идентификаторов............385
     Порядок вызова Турбо Си из .ASM.............................388
     Указатели на функции........................................388
     Указатели на данные.........................................389
     Создание подпрограмм на ассемблере..........................392
     Передача параметров.........................................394
     Управление возвращаемыми величинами.........................395
     Соглашения по регистрам.....................................403
     Вызов Си-функций из .ASM подпрограмм........................405
     ПРОГРАММИРОВАНИЕ НА НИЗКОМ УРОВНЕ:
     псевдопеременные, встроенный ассемблер и функции прерывания.409
     Псевдопеременные............................................409
     Использование встроенного ассемблера........................416
     Команды.....................................................423
     Строковые команды...........................................426
     Префиксы повторения.........................................427
     Команды перехода............................................427
     Директивы ассемблера........................................429
     Указатели встроенного ассемблера к данным и функциям........429
     Встроенный ассемблер и регистровые переменные...............431

                         - 11,12 -


     смещения и замещения размеров операндов.....................433
     Использование элементов Си-структуры........................434
     Использование команд перехода и меток.......................437
     Функции прерываний..........................................438
     Примеры программирования на низком уровне...................441
     Использование библиотек программ для работы с плавающей
      точкой.....................................................445
     Эмуляция микросхемы 8087/80287..............................447
     сопроцессора 8087/80287.....................................451
     Если вы не используете плавающую точку......................453
     Переменная среды 87.........................................457
     Регистры и 8087/80287.......................................460
     Использование matherr с плавающей точкой....................461
     Предостережения и советы....................................462
     Как Турбо Си использует RAM.................................462
     Нужно ли вам использовать Паскаль-соглашения?...............463
     Заключение..................................................464





                             Г Л А В А   8
                             -------------

                          ВИДЕОФУНКЦИИ ТУРБО СИ
     -----------------------------------------------------------------

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


               В этой главе...
     -----------------------------------------------------------------

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

          Новые видеофункции Турбо Си основаны на заимствовании  прог-
     рамм  из  Турбо Паскаля.  Если вы еще не знакомы с управлением на
     вашем ПК режимами экрана,  с созданием  текстовых  и  графических
     окон и управлением ими,  то уделите несколько минут на знакомство

                         - 13,14 -


     с этой темой.





















          Несколько слов о видеорежимах.
     -----------------------------------------------------------------

          У вашего ПК имеется устройство типа видеоадаптера. Это может
     быть  монохромный  адаптер  дисплея  - Monochrome Display Adapter
     (MDA) для вывода на дисплей только текста,  или  это  может  быть
     адаптер  с  возможностью  вывода графики - Color Graphics Adapter
     (CGA),  Enhanced Graphics Adapter (EGA) или  Hercules  Monochrome
     Graphics Adapter.  Каждый адаптер можно устанавливать в различные
     режимы:  80- и 40-символьный (для текстов),  с разной разрешающей
     способностью (для графики), цветной или черно-белый.

          Режимы экрана  определяются  при  вводе в программу одной из
     соответствующих функций (textmode, initgraph или setgraphmode).

          # При текстовом режиме(text mode) экран  делится  на  ячейки
     (80  или 40 колонок в ширину и 25 строк в высоту).  Каждая ячейка
     состоит из атрибута и символа.  Символ - это выводимый  на  экран
     ASCII  символ,  а  атрибут показывает,  как символ представлен на
     дисплее (цвет,  интенсивность и т.д.). Турбо Си обеспечивает пол-
     ный  набор  подпрограмм для управления текстовым экраном,  непос-
     редственного вывода текста на экран и для  управления  атрибутами

                         - 15,16 -


     ячейки.

          # При  графическом режиме(graphics mode) экран вашего ПК де-
     лится на точки растра (пиксели); каждая точка растра выглядит од-
     ной точкой на экране.  Количество точек (разрешающая способность)
     зависит от типа видеоадаптера, установленного на вашей системе, и
     текущего режима адаптера. Вы можете использовать функции Турбо Си
     из новой графической библиотеки для изображения графики на  экра-
     не:  можете рисовать линии и графики, выделять геометрические фи-
     гуры и управлять цветом каждой точки растра.

          В текстовом режиме верхний левый угол экрана -  это  позиция
     (1,  1), координата x возрастает слева на право, а координата y -
     сверху вниз.  В графическом режиме верхний левый угол - это пози-
     ция (0,0), а координаты направлены таким же образом.







          Несколько слов о текстовых и графических окнах
     -----------------------------------------------------------------

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



               Что такое окно?
     -----------------------------------------------------------------

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

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

     рамма  может  изменить это состояние и сделать окно меньше (с по-
     мощью вызова функции window). Эта функция определяет расположение
     окна в координатах экрана.



               Что такое viewport (графическое окно)?
     -----------------------------------------------------------------

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



          Координаты
     -----------------------------------------------------------------



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
















                         - 19,20 -

          Программирование в текстовых режимах
     -----------------------------------------------------------------

          В этом  разделе  мы даем краткое описание функций текстового
     режима; более подробную информацию смотрите в главе 2 Справочного
     руководства по Турбо Си.

          Пакет функций  Турбо  Си  для прямого ввода/вывода с консоли
     (cprintf,  cputs и т.д.) был расширен и дополнен с  целью  совер-
     шенствования  вывода  текста и для обеспечения управления окнами,
     курсором и атрибутами.  Все эти функции являются частью стандарт-
     ной  библиотеки Турбо Си;  они представлены прототипами в заголо-
     вочном файле CONIO.H.



          Функции ввода/вывода с консоли
     -----------------------------------------------------------------

          Функции работы  с текстами выполняются в любом (из пяти воз-
     можных) текстовом режиме; выбор режима зависит от типа видеоадап-
     тера  и монитора.  Текстовый режим определяется вызовом textmode.


     Использование этой функции описано далее в этой главе,  а также в
     разделе textmode главы 2 Справочного руководства.

          Функции текстового режима представлены  четырьмя  отдельными
     группами:

          - вывода текста и манипуляции с ним;
          - управления окнами и режимами;
          - управления атрибутами;
          - запроса состояния.

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


          Вывод текста и манипуляция с ним
          --------------------------------





                         - 21,22 -

          Ниже представлен  краткий перечень соответствующих функций:
          ============================================================

          Запись и чтение текста:
          cprintf       посылает форматированный вывод на экран
          cputs         посылает строку на экран
          putch         посылает один символ на экран
          getche        читает символ и отображает его на экране

          Манипулирование текстом (и курсором) на экране:
          clrscr        очищает текстовое окно
          clreol        очищает строку с позиции курсора
          delline       удаляет строку, на которой находится курсор
          gotoxy        устанавливает курсор
          insline       вставляет пустую строку под строкой,  содержа-
                        щей курсор
          movetext      копирует текст с одного места экрана на другое

          Пересылка блоков текста в (из) память(и):
          gettext       копирует блок текста с экрана в память
          puttext       копирует блок текста из памяти на экран.
          ============================================================


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

          Когда текст  находится  на  экране,  вы можете:  стереть его
     (т.е. очистить активное окно) - clrscr, уничтожить часть строки -
     clreol,  удалить строку целиком - delline, вставить пустую строку
     - insline. Последние три функции действуют в соответствии с поло-
     жением курсора; вы можете передвигать курсор к определенному мес-
     ту при помощи функции gotoxy.  Вы также можете скопировать  целый
     блок текста из одного прямоугольного участка окна в другой коман-
     дой movetext.

          Перенос прямоугольного блока текста с экрана в память  вызы-
     вается  функцией gettext,  а обратная пересылка на экран (в любое

                         - 23,24 -


     желаемое место) выполняется функцией puttext.





















          Управление режимами и окнами
          ----------------------------

          Вот две функции управления режимом и окном:
          ============================================================

          textmode      установка экрана в текстовый режим
          window        задание окна в текстовом режиме
          ============================================================

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

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




                         - 25,26 -

          Управление атрибутами
          ---------------------

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

          ============================================================

          Установка цвета символов и цвета фона:
          textcolor      устанавливает цвет текста (атрибут)
          textbackground устанавливает цвет фона (атрибут)
          textattr       устанавливает цвет символа и цвет фона (атри-
                         буты) одновременно.

          Изменение интенсивности:
          highvideo      устанавливает повышенную интенсивность текста
          lowvideo       устанавливает пониженную интенсивность текста
          normvideo      устанавливает нормальную интенсивность текста

          ============================================================

          Функции управления атрибутами устанавливают текущий атрибут,

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

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

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


                         - 27,28 -

          Запрос состояния
          ----------------

          Здесь приводится  краткий перечень функций запроса состояния

          ============================================================

          gettextinfo   заполняет  структуру  text_info  информацией о
                        текущем текстовом окне
          wherex        сообщает x координату ячейки с курсором
          wherey        сообщает y координату ячейки с курсором

          ============================================================

          Среди функций Турбо Си для ввода/вывода  с  консоли  имеется
     несколько функций для "запроса состояния". С помощью этих функций
     вы можете скорректировать информацию о текстовом окне и положении
     курсора в нем.

          Функция gettextinfo  записывает в структуру text_info (опре-
     деленную в conio.h) информацию о текстовом окне, а именно:



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

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












                         - 29,30 -

          Текстовые окна
     -----------------------------------------------------------------

          По умолчанию текстовое окно занимает весь экран.  Вы  можете
     изменить его, сократив вызовом функции window. Текстовые окна мо-
     гут включать до 25 строк (максимальное  количество  строк  экрана
     для всех текстовых режимов) и до 40 или 80 колонок (в зависимости
     от установленного текстового режима).

          Отсчет координат текстового окна Турбо Си ведется от верхне-
     го левого угла. Координаты текстового окна в левом верхнем углу -
     (1,1);  координаты правого нижнего угла (при 80-символьном полно-
     экранном текстовом окне) - (80,25).

          Пример
          ------

          Предположим ваша 100%  совместимая с IBM PC система установ-
     лена в 80-колоночный текстовый режим и вы захотели создать окно с
     верхним левым углом (10,8) и нижним  правым  углом  (50,21).  Для
     этого необходимо вызвать функцию window :


          window(10, 8, 50, 21);

          Теперь, после создания текстового окна,  вы можете переслать
     курсор на позицию (5,8) в окне и записать сюда  текст,  используя
     gotoxy и cputs.

          gotoxy(5, 8);
          cputs("С днем рождения, Фрэнк Борланд");



               Тип text_modes
     -----------------------------------------------------------------

          Вы можете перевести ваш монитор в один из пяти текстовых ре-
     жимов,  вызвав функцию textmode. Перечислимый тип text_modes, оп-
     ределенный в CONIO.H, дает возможность использовать символические
     имена для установки аргумента mode  в  функции  textmode,  взамен
     "сухого" номера режима.  Конечно, если вы используете символичес-
     кие константы, то должны включить #include в текст вашей
     программы.


                         - 31,32 -

          Числовые и  символьные  значения,  определенные  text_modes,
     следующие:

          ------------------------------------------------------------
           Символьная  Числовое    Текстовый видеорежим
           константа   значение
          ------------------------------------------------------------
           LASTMODE      -1      Предыдущий текстовый режим
           BW40           0      Черно-белый, 40 колонок
           C40            1      16-цветный, 40 колонок
           BW80           2      Черно-белый, 80 колонок
           C80            3      16-цветный, 80 колонок
           MONO           7      Монохромный, 80 колонок
          ------------------------------------------------------------

          Например, следующие вызовы textmode  будут  переключать  ваш
     цветной монитор в такие режимы:

          ------------------------------------------------------------
           Вызов                    Режим
          ------------------------------------------------------------
           textmode(0)           Черно-белый, 40 колонок

           textmode(BW80)        Черно-белый, 80 колонок
           textmode(C40)         16-цветный, 40 колонок
           textmode(3)           16-цветный, 80 колонок
          ------------------------------------------------------------



               Цвета текста
     -----------------------------------------------------------------

          За подробным описанием установки атрибутов ячейки обратитесь
     к разделу textattr главы 2 Справочного руководства.

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

          В файле CONIO.H определены символические имена для различных
     цветов.  Если вы используете символические константы,  вы  должны

                         - 33,34 -


     включить в текст вашей программы строку #include.

          Следующая таблица  представляет  список  этих  символических
     констант и соответствующих им числовых значений.  Запомните,  что
     только   первые   восемь   цветов   доступны   для   выбора  фона
     (background),  в то время как все 16 доступны  для  выбора  цвета
     символов (foreground).
















     -----------------------------------------------------------------
     Символическая    Численное    Цвет символа     Соответствующий
       константа      значение     или цвет фона?      цвет
     -----------------------------------------------------------------
     BLACK               0           обоих          ЧЕРНЫЙ
     BLUE                1           обоих          СИНИЙ
     GREEN               2           обоих          ЗЕЛЕНЫЙ
     CYAN                3           обоих          ГОЛУБОЙ
     RED                 4           обоих          КРАСНЫЙ
     MAGENTA             5           обоих          МАЛИНОВЫЙ
     BROWN               6           обоих          КОРИЧНЕВЫЙ
     LIGHTGRAY           7           обоих          СВЕТЛОСЕРЫЙ
     DARKGRAY            8          Символа         ТЕМНОСЕРЫЙ
     LIGHTBLUE           9          Символа         СВЕТЛОСИНИЙ
     LIGHTGREEN         10          Символа         СВЕТЛОЗЕЛЕНЫЙ
     LIGHTCYAN          11          Символа         СВЕТЛОГОЛУБОЙ
     LIGHTRED           12          Символа         СВЕТЛОКРАСНЫЙ
     LIGHMAGENTA        13          Символа         СВЕТЛОМАЛИНОВЫЙ
     YELLOW             14          Символа         ЖЕЛТЫЙ
     WHITE              15          Символа         БЕЛЫЙ
     BLINK              128         Символа         МЕРЦАЮЩИЙ

                         - 35,36 -

     -----------------------------------------------------------------

          Вы можете добавить символическую константу  BLINK  (числовое
     значение  128) для аргумента цвета символа,  если вы хотите чтобы
     символ мерцал.



               Высокоэфективный вывод: переменная directvideo
     -----------------------------------------------------------------

          Консольный пакет  ввода/вывода Турбо Си включает переменную,
     называемую directvideo. Эта переменная направляет ваш программный
     вывод  прямо  в видеопамять (directvideo = 1) или переключает его
     на использование BIOS - базовой системы ввода/вывода (directvideo
     = 0).

          Предопределенное значение  directvideo = 1 (консольный вывод
     направляется прямо в видеопамять).  В общем случае  использование
     непосредственного вывода в видеопамять дает высокую эффективность
     (быстрый вывод),  но для этого требуется 100% совместимость с IBM
     PC  вашего  компьютера:  исполнение  адаптера дисплея должно быть


     идентично IBM.  Установка directvideo = 0 предназначена для любой
     машины BIOS совместимой с IBM,  но текст будет выводиться на кон-
     соль медленее.



















                         - 37,38 -

          Программирование в графическом режиме
     -----------------------------------------------------------------

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

          Турбо Си предоставляет отдельную библиотеку из более  чем 70
     графических функций,  от высокоуровневых (таких как setviewoport,
     bar3d и drawpoly) до побитно-ориентированных функций  (таких  как
     getimage  и  putimage).  Эта  графическая библиотека поддерживает
     множество разнообразных видов линий и закрашивания,  а также нес-
     колько текстовых шрифтов, которые вы можете увеличивать, выравни-
     вать и ориентировать в горизонтальном или вертикальном  положени-
     ях.

          Данные функции  хранятся в новой библиотеке GRAPHICS.LIB,  а
     их прототипы - в заголовочном файле GRAPHICS.H.  Кроме этих  двух
     файлов,  графический  пакет  включает в себя драйверы графических
     устройств (файлы *.BGI) и штриховые (векторные) символьные шрифты
     (файлы  *.CHR);  эти  дополнительные файлы мы обсудим в следующих

     разделах.

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

          # При   использовании    TC.EXE    переключить    библиотеку
            Options/Linker/Graphics в положение On.  Сборщик автомати-
            чески подключит графическую библиотеку к  вашей программе.

          # При использования TC.EXE вы должны включить GRAPHICS.LIB в
            командную строку.  Например,  если ваша программа MYPROG.C
            использует графику,  то командная  строка  для  TCC  будет
            иметь вид:

            tcc myprog graphics.lib

          Важное замечание:  имеется только одна графическая библиоте-
     ка, не разделяемая на версии для каждой модели памяти (в противо-
     вес стандартным библиотекам CS.LIB,  CC.LIB, CM.LIB и т.д., соот-
     ветствующим  определенным  моделям  памяти).  Каждая  функция   в
     GRAPHICS.LIB  является функцией типа far,  а те графические функ-
     ции,  которые используют указатели, принимают указатели типа far.
     Для нормальной работы этих функций важно,  чтобы в каждый модуль,

                         - 39,40 -


     использующий графику,  была включена строка #include.






















          Функции графической библиотеки
     -----------------------------------------------------------------

          Графические функции Турбо Си делятся на семь категорий:

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


          Управление графической системой
          -------------------------------

          Здесь приводится краткий перечень функций,  управляющих гра-
     фической системой:



                         - 41,42 -


     =================================================================

       closegraph     выключает графическую систему
       detectgraph    проверяет  аппаратуру  и  определяет необходимый
                      графический драйвер и режим использования
       graphdefaults  заменяет  все  переменные графической системы на
                      их значения по умолчанию
       _graphfreemem  освобождает графическую память; используется для
                      включения вашей собственной программы
       _graphgetmem   распределяет  память,  используемую  графической
                      системой; используется для включения вашей собс-
                      твенной программы
       getgraphmode   возвращает текущий графический режим
       getmoderange   возвращает  минимальный  и  максимальный режимы,
                      являющиеся корректными для  заданного устройства
       initgraph      инициирует графическую систему и переводит аппа-
                      ратуру в графический режим
       installuserdriver загружает дополнительный драйвер устройства в
                      таблицу драйверов устройств BGI
       installuserfont загружает  файл векторного шрифта, недоступного
                      для графических подпрограмм

       registerbgidriver регистрирует  скомпонованный  или загруженный
                      пользователем файл-драйвер для  подключения  его
                      на этапе сборки
       restorecrtmode восстанавливает первоначальный (до использования
                      initgraph) режим экрана
       setgraphbufsize задает размер внутренного графического буфера
       setgraphmode   выбирает заданный графический режим, очищает эк-
                      ран и восстанавливает все значения по умолчанию

     =================================================================

          Графический пакет Турбо Си предоставляет графические драйве-
     ры  следующих графических адаптеров (и полностью с ними совмести-
     мых):
          # Цветной графический адаптер (CGA)
          # Многцветный графический адаптер (MCGA)
          # Улучшенный графический адаптер (EGA)
          # Видеографический адаптер (VGA)
          # Графический адаптер Hercules
          # 400-строчный графический адаптер фирмы AT&T
          # Графический адаптер 3270 PC
          # Графический адаптер IBM 8514

                         - 43,44 -


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

          Загрузив графический драйвер, вы можете получить его имя при
     помощи  функции  getdrivename,  а число поддерживаемых им режимов
     при помощи функции getmaxmode. Функция getgraphmode сообщит вам о
     текущем графическом режиме.  Узнав номер режима,  вы можете полу-
     чить его название при помощи getmodename.  Вы можете  переключать
     графические режимы функцией setgraphmode,  а также вернуть видео-
     режим в изначальное состояние (то,  которое было до инициализации
     графики) функцией restorecrtmode.  Эта функция возвращает экран в
     текстовый режим,  но графическую систему не выключает  (шрифты  и

     драйверы остаются в памяти).

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

          Функции installuserdriver и installuserfont позволяют допол-
     нять BGI дополнительными драйверами и шрифтами.

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


          Более подробное обсуждение.
          ---------------------------

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


                         - 45,46 -

          Обычно функция  initgraph загружает графический драйвер пос-
     редством выделения памяти для этого драйвера с  последующей  заг-
     рузкой с диска соответствующего файла .BGI.  В качестве альтерна-
     тивы этой схеме динамической загрузки вы можете скомпоновать файл
     графического  драйвера  (или  несколько файлов) непосредственно с
     файлом вашей программы.  Для этого, во-первых, вы должны преобра-
     зовать файл .BGI в файл .OBJ (с помощью утилититы BGIOBJ),  а за-
     тем включить в текст вашей программы вызов registerbgidriver (пе-
     ред   вызовом   initgraph)  для  регистрации  этого  графического
     драйвера.  При компоновке программы вы должны будете скомпоновать
     файлы .OBJ для всех зарегистрированных драйверов.

          После определения (при помощи detectgraph) того, какой драй-
     вер должен использоваться,  initgraph проверяет,  зарегистрирован
     ли этот драйвер.  Если да,  то initgraph использует зарегистриро-
     ванный драйвер непосредственно  из  памяти.  В  противном  случае
     initgraph выделяет память для драйвера и загружает с диска файл .
     BGI.

          Замечание: использование registerbgidriver  является  углуб-
     ленным методом программирования и,  поэтому, не рекомендуется для
     начинающих программистов.  Более детально эта функция  описана  в

     Приложении D Справочного Руководства.

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

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

          Замечание. Если   вы    определили    собственную    функцию
     _graphgetmem  или  _graphfreemem,  то  может  появиться сообщение
     "Warning: duplicate symbols" вследствие того, что функции с таки-
     ми  же  именами находятся в графической библиотеке.  Не обращайте
     внимания на это сообщение.



                         - 47,48 -

          Рисование и закрашивание
          ------------------------

          Ниже приведено  краткое  описание  рисующих  и закрашивающих
     функций:

     =================================================================

          Рисование:
       arc             вычерчивает круговую дугу
       circle          вычерчивает окружность
       drawpoly        вычерчивает многоугольник
       ellipse         вычерчивает эллиптическую дугу
       getarccoords    возвращает координаты последнего обращения к
                       arc или ellipse
       getaspectratio  возвращает коэффициент сжатия текущего графиче-
                       ского режима
       getlinesettings возвращает текущие стиль, шаблон и  толщину ли-
                       нии
       line            вычерчивает линию от (x0, y0) до (x1, y1)
       linerel         вычерчивает линию  от  текущей позиции (ТП)  до
                       точки на заданном от нее расстоянии

       lineto          вычерчивает линию от ТП до (x,y)
       moveto          устанавливает ТП в (x,y)
       moverel         перемещает ТП на заданное расстояние
       rectangle       вычерчивает прямоугольник
       setaspectratio  изменяет коэффициент сжатия
       setlinestyle    устанавливает текущую толщину и стиль линии

          Закрашивание:
       bar             вычерчивает и закрашивает прямоугольник
       bar3d           вычерчивает и закрашивает параллелепипед
       fillellipse     вычерчивает и закрашивает эллипс
       fillpoly        вычерчивает и закрашивает многоугольник
       floodfill       "лавинно" заполняет ограниченную область
       getfillpattern  возвращает описанный пользователем шаблон
                       закрашивания
       getfillsettings возвращает информацию о текущем шаблоне
                       и цвете закрашивания
       pieslice        вычерчивает и закрашивает круговой сектор
       sector          вычерчивает и закрашивает эллиптический сектор
       setfillpattern  устанавливает заданный пользователем шаблон
                       закрашивания
       setfillstyle    устанавливает шаблон и цвет

                         - 49,50 -


     =================================================================

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

          Вы вычерчиваете линии и незакрашенные фигуры с помощью функ-
     ций arc,  circle,  drawpoly,  ellipse,  line,  linerel,  lineto и
     rectangle.  Вы можете закрашивать эти фигуры с помощью floodfill,
     или осуществлять вычерчивание и закрашивание за один  шаг  с  по-
     мощью bar, bar3d, fillellipse, fillpoly, pieslice или sector.

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

     кой  в  соответствии  с  заданным  вами   шаблоном.   С   помощью
     setfillstyle вы можете выбрать предопределенный шаблон закрашива-
     ния,  а с помощью setfillpattern - описать свой собственный  шаб-
     лон. С помощью moveto вы можете передвигать текущую позицию в за-
     данную точку, а с помощью moverel - на заданное расстояние.

          Для определения текущих стиля и толщины линии вы можете  об-
     ратиться  к  getlinesettings.  Для получения информации о текущих
     шаблоне   и   цвете   закрашивания   вы    можете    использовать
     getfillsettings. Для получения пользовательского шаблона закраши-
     вания используйте getfillpattern.

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





                         - 51,52 -

          Манипуляция экраном и графическим окном.
          ----------------------------------------

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

     =================================================================

          Манипуляция экраном:
       cleardevice     очищает экран (активную страницу)
       setactivepage   устанавливает активную страницу для графическо-
                       го вывода
       setvisualpage   устанавливает номер видимой графической страни-
                       цы

          Манипуляция графическим окном:
       clearviewport   очищает текущее окно
       getviewsettings возвращает информацию о текущем окне
       setviewport     устанавливает окно для текущего графического вы-
                       вода

          Манипуляция образом:

       getimage        записывает  в память битовый образ заданной об-
                       ласти
       imagesize       возвращает число байт, требуемых для сохранения
                       прямоугольной области экрана
       putimage        выводит ранее сохраненный битовый образ на  эк-
                       ран

          Манипуляция точками растра:
       getpixel        возвращает цвет точки в (x,y)
       putpixel        вычерчивает точку в (x,y)

     =================================================================

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


                         - 53,54 -

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

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

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



          Координаты для всех функций вывода (рисования, закрашивания,
     вывода текста и т.д.) связаны с данным окном.

          Кроме того,  с помощью getpixel (возвращающей цвет  заданной
     точки)  и putpixel (вычерчивающей заданную точку заданным цветом)
     вы можете манипулировать цветом отдельных точек растра.
















                         - 55,56 -

          Вывод текста в графическом режиме
          ---------------------------------

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

     =================================================================

       gettextsettings возвращает текущие  текстовые шрифт,  направле-
                       ние, размер и выравнивание.
       outtext         выводит строку в текущую позицию (ТП) экрана
       outtextxy       выводит строку в заданную позицию экрана
       registerbgifont регистрирует  скомпонованный  или   загруженный
                       пользователем шрифт
       settextjustify  устанавливает величину выравнивания текста, ис-
                       пользуемую функциями outtext и outtextxy
       settextstyle    устанавливает текущие текстовые шрифт, стиль  и
                       коэффициент увеличения знаков
       setusercharsize устанавливает отношение  ширины  к  высоте  для
                       штриховых (векторных) шрифтов
       textheight      возвращает высоту строки в точках растра
       textwidth       возвращает ширину строки в точках растра


     =================================================================

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

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

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


          С помощью outtext и outtextxy вы можете выводить графический
     текст, а с помощью settextjustify - управлять выравниванием выво-
     димого текста (относительно ТП). С помощью settextstyle вы можете
     выбрать символьный шрифт,  направление (горизонтальное или верти-
     кальное) и размер (масштаб).  Для того чтобы получить текущие па-
     раметры текста, вызывается функция gettextsettings, которая запи-
     сывает   в   структуру   textsettings  текущие  текстовый  шрифт,
     выравнивание,  увеличение и направление.  Функция setusercharsize
     позволяет модифицировать ширину и высоту векторных шрифтов.

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

          Функции textheight (которая вычисляет высоту строки в точках
     растра)  и  textwidth  (которая  вычисляет ширину строки в точках
     растра) можно использовать для определения размера на экране  за-
     данной текстовой строки.


          Принятый по  умолчанию  растровый шрифт 8x8 встроен в графи-
     ческий пакет,  поэтому во время работы программы он всегда досту-
     пен. Штриховые шрифты хранятся каждый в отдельном файле .CHR; они
     могут быть загружены во время выполнения программы или преобразо-
     ваны в файлы .OBJ (с помощью утилиты BGIOBJ) и скомпонованы с ва-
     шим файлом .EXE.

          Обычно функция settextstyle загружает  шрифтовой  файл  пос-
     редством выделения памяти для шрифта и загрузки с диска соответс-
     твующего файла .CHR.  В качестве альтернативы  этой  динамической
     схеме  загрузки  вы  можете  скомпоновать файл символьного шрифта
     (или несколько таких файлов) непосредственно  в  ваш  исполняемый
     файл. Для этого, во-первых, вы преобразуете файл .CHR в файл .OBJ
     (с помощью утилиты BGIOBJ), а затем помещаете в текст вашей прог-
     раммы   вызов   функции   registerbgifont   (перед  обращением  к
     settextstyle) для регистрации символьного шрифта.  При компоновке
     вашей программы вы должны скомпоновать файлы .OBJ для тех штрихо-
     вых шрифтов, которые вы зарегистрировали.

          Замечание: Использование registerbgifont является  углублен-
     ным методом программирования,  и поэтому не рекомендуется для на-

                         - 59,60 -


     чинающих программистов. Более детально эта функция описана в При-
     ложении D Справочного руководства.




















          Управление  цветом
          ------------------

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

     =================================================================

        Получение информации о цвете:
       getbkcolor      возвращает текущий цвет фона
       getcolor        возвращает текущий цвет вычерчивания
       getdefaultpalette возвращает структуру, определяющую палитру
       getmaxcolor     возвращает максимальное значение, которое можно
                       присвоить  точке  растра  в текущем графическом
                       режиме
       getpalette      возвращает текущую палитру и ее размер
       getpalettesize  возвращает размер палитры (т.е справочной  таб-
                       лицы палитры)

        Установка одного или нескольких цветов:
       setallpalette   изменяет все цвета палитры на заданные
       setbkcolor      устанавливает текущий цвет фона
       setcolor        устанавливает текущий цвет вычерчивания

                         - 61,62 -

       setpalette      изменяет один  цвет  палитры в соответствии с
                       заданным аргументом

     =================================================================

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


          Точки растра и палитры
          ----------------------

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

          Данная косвенная схема имеет ряд применений. Хотя аппаратура
     может быть способна отображать много цветов,  в конкретный момент

     времени может отображаться только некоторое подмножество  из этих
     цветов. Число  цветов, способных отображаться одновременно, равно
     числу элементов в палитре (размеру палитры). К примеру, EGA аппа-
     ратура может производить 64 различных цвета,  но только 16 из них
     могут отображаться одновременно; размер палитры для EGA=16.

          Размер палитры size определяет диапазон значений точек раст-
     ра: от 0 до (size-1). Функция getmaxcolor возвращает максимальное
     значение  точки растра (size-1) для текущих графического адаптера
     и режима.

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




                         - 63,64 -

          Цвет фона и вычерчивания
          ------------------------

          Цвет фона  всегда соответствует значению 0 точки растра. При
     очистке некоторой области в цвет фона,  точки растра этой области
     просто устанавливаются в 0.

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



          Управление цветом на CGA
          ------------------------

          Из-за различий в графической аппаратуре  действительное  уп-
     равление  цветом  несколько различается на EGA и CGA,  поэтому мы
     опишем эти адаптеры раздельно. Управление цветом на адаптере AT&T
     и, при низком разрешении, на MCGA аналогично управлению цветом на
     CGA.


          На CGA вы можете выбирать между отображением графики в  низ-
     ком разрешении (320x200),  что позволит вам использовать 4 цвета,
     и в высоком разрешении (640x200), при котором вы можете использо-
     вать 2 цвета.



               Низкое разрешение CGA.

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

          Выбор конкретной палитры осуществляется путем выбора опреде-
     ленного режима (CGAC0, CGAC1, CGAC2, CGAC3); эти режимы соответс-
     твуют цветовым палитрам от 0 до 3,  что  отображено  в  следующей
     таблице. Цвета вычерчивания для CGA и соответствующие им констан-
     ты определены в файле graphics.h.

                         - 65,66 -


     -----------------------------------------------------------------
        Номер палитры    Цвет, соответствующий значению точки растра
                               1              2            3
     -----------------------------------------------------------------
             0           CGA_LIGHTGREEN  CGA_LIGHTRED     CGA_YELLOW
                        (светлозеленый) (светлокрасный)   (желтый)
             1           CGA_LIGHTCYAN   CGA_LIGHTMAGENTA CGA_WHITE
                        (светлоголубой) (светломалиновый) (белый)
             2           CGA_GREEN       CGA_RED          CGA_BROWN
                         (зеленый)       (красный)        (коричневый)
             3           CGA_CYAN        CGA_MAGENTA     CGA_LIGHTGRAY
                         (голубой)      (малиновый)      (светлосерый)
     -----------------------------------------------------------------

          Для того, чтобы назначить один из этих цветов цветом рисова-
     ния для CGA, вызовите функцию setcolor, передав ей в качестве ар-
     гумента либо номер цвета, либо соответствующее символическое имя;
     например, если  вы используете палитру 3 и хотите назначить голу-
     бой цветом рисования:

          setcolor(1); или setcolor(CGA_CYAN);


          Доступные на CGA цвета фона,  описанные в GRAPHICS.H, приве-
     дены в следующей таблице.

           ----------------------------------------------
           Числовое  Символическая     Соответствующий
           значение    константа          цвет
           ----------------------------------------------
              0      BLACK             ЧЕРНЫЙ
              1      BLUE              СИНИЙ
              2      GREEN             ЗЕЛЕНЫЙ
              3      CYAN              ГОЛУБОЙ
              4      RED               КРАСНЫЙ
              5      MAGENTA           МАЛИНОВЫЙ
              6      BROWN             КОРИЧНЕВЫЙ
              7      LIGHTGRAY         СВЕТЛОСЕРЫЙ
              8      DARKGRAY          ТЕМНОСЕРЫЙ
              9      LIGHTBLUE         СВЕТЛОСИНИЙ
              10     LIGHTGREEN        СВЕТЛОЗЕЛЕНЫЙ
              11     LIGHTCYAN         СВЕТЛОГОЛУБОЙ
              12     LIGHTRED          СВЕТЛОКРАСНЫЙ
              13     LIGHTMAGENTA      СВЕТЛОМАЛИНОВЫЙ

                         - 67,68 -

              14     YELLOW            ЖЕЛТЫЙ
              15     WHITE             БЕЛЫЙ
            ---------------------------------------------

          Для присваивания одного из этих цветов цвету фона на CGA ис-
     пользуйте setbkcolor(color), где color - один из элементов приве-
     денной выше таблицы.  Отметим,  что для CGA этот цвет не является
     значением точки растра (индексом палитры); он непосредственно за-
     дает действительный цвет.



               Высокое разрешение CGA

          В режиме  высокого  разрешения  (640x200) CGA отображает два
     цвета:  черный цвет фона и цветное  изображение.  Значение  точек
     растра  может  быть равным 0 и 1.  Из-за особенностей самого CGA,
     аппаратура, в действительности, воспринимает цвет изображения как
     цвет фона,  поэтому вы можете устанавливать его с помощью функции
     setbkcolor. (Удивительно, но факт.)

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

     CGA использует определенный цвет для отображения всех точек раст-
     ра, значение которых равно 1.

          Режимы, работающие  описанным   образом:   CGAHI,   MCGAMED,
     MCGAHI, ATT400MED, ATT400HI.


               Функции управления палитрой для CGA

          Так как палитра CGA предопределена, вам не следует использо-
     вать функцию setallpalette. Кроме того, функцию setpalette(index,
     actual_color) вы можете использовать только при index=0. (Это яв-
     ляется  альтернативным способом для установки цвета фона на CGA в
     actual_color.)



          Управление цветом на EGA и VGA
          ------------------------------

          На EGA палитра содержит 16 элементов из 64 возможных цветов,
     причем каждый из них может быть установлен пользователем.

                         - 69,70 -


          Вы можете  получить  текущую  палитру  с   помощью   функции
     getpalette, которая заполняет структуру размером с палитру (16) и
     массив действительных элементов палитры ("номера цветов для аппа-
     ратуры",  хранящихся  в палитре).  С помощью setpalette вы можете
     изменять  каждый   цвет   палитры   поодиночке,   а   с   помощью
     setallpalette - все сразу.

          По умолчанию палитра EGA соответствует 16 цветам CGA, приве-
     денным в предыдущей таблице,  - черный соответствует элементу  0,
     голубой - 1, ..., белый - 15. В GRAPHICS.H описаны константы, со-
     ответствующие значениям аппаратурных цветов: EGA_BLACK, EGA_WHITE
     и  т.д.  Кроме  того,  вы  можете получить эти величины с помощью
     getpalette.

          Поведение функции setbkcolor(color) различается на адаптерах
     CGA  и EGA.  На EGA setbkcolor переносит величину действительного
     цвета из color в нулевой элемент палитры.

          Как только цвета установлены, драйвер VGA начинает вести се-
     бя так же,  как драйвер EGA. Отличие лишь в том, что у него более
     высокое разрешение (и соответственно более мелкие  точки растра).


          Обработка ошибок в графическом режиме
          -------------------------------------

          Вот функции для обработки ошибок в графическом режиме:

     =================================================================

     grapherrormsg    возвращает строку с сообщением об ошибке для за-
                      данного кода ошибки

     graphresult      возвращает код ошибки для последней графической
                      операции, вызвавшей затруднения

     =================================================================

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


                         - 71,72 -


     -----------------------------------------------------------------
     код      символическая    соответствующая строка с сообщением
     ошибки   константа        об ошибке
     -----------------------------------------------------------------
      0       grOK             No error (нет ошибки)
     -1       grNoInitGraph    (BGI) graphics not installed
                               (графика не    запущена,    используйте
                               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 selected
                               driver (недопустимый  графический режим
                               для заданного драйвера)
     -11      grError          Graphics error (ошибка графики)
     -12      grIOerror        Graphics I/O error (ошибка ввода/вывода
                               в графическом режиме)
     -13      grInvalidFont    Invalid font file
                               (неверный файл шрифта)
     -14      grInvalidFontNum Invalid font number
                               (неверный номер шрифта)
     -15      grInvalidDeviceNum Invalid device number
                               (неверный номер устройства)
     -18      grInvalidVersion Invalid version of file
                               (недопустимая версия файла)

     -----------------------------------------------------------------

                         - 73,74 -



          В результате  обращения  grapherrormsg(graphresult())  будет
     возвращена соответствующая строка (см. таблицу).

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












          Запрос состояния
          ----------------

          Вот перечень функций запроса состояния в графическом режиме:

     =================================================================

     getarccoords      возвращает информацию о координатах последнего
                       обращения к arc или ellipse
     getaspectratio    возвращает коэффициент сжатия графического эк-
                       рана
     getbkcolor        возвращает текущий цвет фона
     getcolor          возвращает текущий цвет рисования
     getdrivename      возвращает имя текущего графического драйвера
     getfillpattern    возвращает заданный пользователем шаблон закра-
                       шивания
     getfillsettings   возвращает информацию о текущих шаблоне закра-
                       шивания и цвете
     getgraphmode      возвращает текущий графический режим
     getlinesettings   возвращает текущие стиль, шаблон и толщину ли-
                       нии
     getmaxcolor       возвращает  максимальное  допустимое  значение

                         - 75,76 -

                       точки растра
     getmaxmode        возвращает максимальный номер режима для теку-
                       щего драйвера
     getmaxx           возвращает текущее разрешение по оси x
     getmaxy           возвращает текущее разрешение по оси y
     getmodename       возвращает имя данного режима драйвера
     getmoderange      возвращает диапазон режимов для данного драй-
                       вера
     getpalette        возвращает текущую палитру и ее размер
     getpixel          возвращает цвет точки растра (x,y)
     gettextsettings   возвращает текущие шрифт, направление, размер и
                       выравнивание текста
     getviewsettings   возвращает информацию о текущем графическом ок-
                       не
     getx              возвращает координату x текущей позиции
     gety              возвращает координату y текущей позиции

     =================================================================

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

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

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

          Функции запроса состояния при рисовании и закрашивании - это
     getarccoords,  getaspectratio,  getfillpattern, getfillsettings и
     getlinesettings.  getarccoords заполняет  структуру  координатами
     последнего обращения к arc или ellipse; getaspectratio сообщает о
     коэффициенте сжатия для текущего режима, который позволяет графи-
     ческой  системе вычерчивать правильные окружности. getfillpattern

                         - 77,78 -

     возвращает текущий пользовательский шаблон.  getfillsettings  за-
     полняет   структуру   информацией  о  текущем  шаблоне  и  цвете.
     getlinesettings заполняет структуру информацией  о  текущих  сти-
     ле(сплошная, пунктирная и т.д.), толщине (нормальная или тонкая),
     и шаблоне линии.

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

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



          Категория функций управления  цветом  включает  три  функции
     запроса состояния. Функция getbkcolor возвращает текущий цвет фо-
     на, getcolor - текущий цвет рисования. Функция getpalette записы-
     вает  в  структуру  размер текущей палитры рисования и содержимое
     палитры.  getmaxcolor возвращает максимальное допустимое значение
     точки  растра  для  текущего графического драйвера и режима (т.е.
     размер палитры минус единица).

          И наконец, функции getmodename и getdrivename возвращают со-
     ответственно имена данного режима драйвера и текущего графическо-
     го драйвера.











                         - 79,80 -

                             Г Л А В А  9
                             ------------

          ЗАМЕЧАНИЯ ДЛЯ ПРОГРАММИСТОВ, РАБОТАЮЩИХ НА ТУРБО ПАСКАЛЕ.
     -----------------------------------------------------------------

          Перед тем,  как продолжить свое знакомство с Турбо Си, восс-
     тановите в памяти главы 6 и 7. Вспомните, как Си определяет базо-
     вые элементы программирования. В этой главе мы рассмотрим некото-
     рые из   основных  понятий  Си,  однако в главах 6 и 7 есть много
     деталей, которых вы здесь не найдете.

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

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



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
















                         - 81,82 -

          Структура программы.
     -----------------------------------------------------------------

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

                    program ИмяПрограммы
                   < раздел описания:
                     const
                     type                {очередность необязательна}
                     var
                     procedures and functions >
                     begin      {заголовок программы ИмяПрограммы}
                     <операторы>
                     end.       {Конец программы}

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

          Структура программ на Си несколько более гибкая:


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


          Функции в свою очередь имеют следующую структуру:

             FuncName {<параметры с описанием>}
            {
                <локальные описания>
                <операторы>
            }

          Из всех функций,  которые вы объявляете,  только одна должна
     иметь имя main. Это и есть главный модуль вашей программы. Други-
     ми словами, когда ваша программа начинает выполняться, вызывается
     функция main,    а она может включать в свою очередь вызов других
     функций. Любая  Си программа состоит только из функций.   Однако,
     некоторые функции   имеют тип void и не возвращают значений; так,

                         - 83,84 -


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



















          Пример
     ----------------------------------------------------------------

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

           Турбо Паскаль                              Турбо Си
     ________________________________________________________________

         program My_Prog;
         var
           I,J,K : Integer;                int  i,j,к

     function Max(A,B:Integer):Integer;    int max (int a, int b)
     begin                                 {
         if A>B                             if (a>b) return (a);
            then Max:=A                     else return (b);
            else Max:=B                    }                                    
         end;                              /* конец max () */                   
         {конец функции Max}                                                    
                                                                                

                         - 85,86 -
                                                                                
     procedure Swap (var A,B: Integer);    void swap (int *a,int *b)            
     var   Temp : Integer;                                                      
     begin                                 { int temp;                          
            Temp:=A; A:=B; B:=Temp           temp= *a; *a=*b; *b=temp;          
     end;                                  }                                    
     {конец процедуры  swap}               /*конец swap () */                   
                                                                                
                                                                                
     begin    {глав. модуль программы      main ()                              
                               My_Prog}                                         
         I:=10; J:=15;                     {                                    
         K:=Max (I,J);                      i=10; j=15;                         
         Swap (I,K);                        k=max (i,j);                        
     Write ('I=',I:2, 'J=', J:2);           swap (&i, &k);                      
     Writeln  ('K=', K:2)                   printf("i=%2d j=%2d",i,j);          
     end                                    printf ("k=%2d\n",k);               
     {конец программы My_Prog}             }                                    
                                           /*конец main*/                       
     -----------------------------------------------------------------          
                                                                                
                                                                                
                                                                                
                                                                                
          Сравнение базовых элементов.                                          
     ----------------------------------------------------------------           
                                                                                
          Как и в главе 6,  рассмотрим семь базовых элементарных поня-          
     тий программирования - вывод, типы данных, операции, ввод, выпол-          
     нение по условию, выполнение в цикле и подпрограммы.                       
                                                                                
                                                                                
                                                                                
                                                                                
          В ы в о д.                                                            
     -----------------------------------------------------------------          
                                                                                
          Основные команды вывода Турбо Паскаля это - Write и Writeln.          
     Турбо Си, напротив, имеет множество команд, позволяющих выполнять          
     точно то,  что вы хотите.  Основной командой является printf. Она          
     имеет формат:                                                              
                                                                                
          printf(<форматная строка>, <перем.>, <перем.>...);                    
                                                                                
     где <форматная строка> - это строка символов или символьная пере-          
     менная (напоминаем,   в Си используются двойные кавычки),  а <пе-          

                         - 87,88 -
                                                                                
                                                                                
     рем.> - переменная или выражение,  которые выводятся в  соответс-          
     твии с   командами форматирования,  форматной строки; (см.главу 6          
     для уточнения деталей). Для перехода на новую строку (по аналогии          
     с Writeln) надо вставить символ \n в конец форматной строки.               
                                                                                
          Пример                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
         Турбо Паскаль                                Турбо Си                  
     ----------------------------------------------------------------           
     var                                                                        
         A,B,C: Integer;                int  a,b,c;                             
         Amt  : Real;                   float amt;                              
         Name : string(20);             char name[21]; (или *name)              
         Ans  : Char;                   char ans;                               
                                                                                
     Writeln('Hello, world.');          printf("Hello, world.\n");              
     Write('What''s your name?');       printf("What's your name?");            
     Writeln('"Hello," said John');     printf("\"Hello,\"said                  
                                                           John\n");            
                                                                                
     Writeln(A,' + ',B,' = ',C);        printf("%d + %d =                       
                                                        %d\n",a,b,c);           
     Writeln('You owe us S',Amt:6:2);   printf("You owe us                      
                                                      S%6.2f\n",amt);           
                                                                                
     Writeln('Your name is ',Name,'?'); printf("Your name is                    
                                                        %s?\n",name);           
     Writeln('The answer is ',Ans);     printf("The answer is                   
                                                          %c\n",ans);           

                         - 89,90 -
                                                                                
                                                                                
     Write(' A = ',A:4);                printf(" a = %4d",a);                   
     Writeln(' A*A = ',(A*A):6);        printf(" a*a = %6d\n",a*a);             
                                                                                
     ----------------------------------------------------------------           
                                                                                
          Другие два оператора вывода Си, которые бы вы вероятно хоте-          
     ли бы знать - это puts и putchar. puts получает строку в качестве          
     аргумента и выводит ее, автоматически добавляя символ перехода на          
     новую строку. Например, следующие команды эквивалентны:                    
                                                                                
     Writeln(Name);                     puts(Name);                             
     Writeln('Hi, there!');Writeln;     puts("Hi, there!\n");                   
                                                                                
          Команда  putсhar (вывести символ) значительно проще. Она вы-          
     водит всего один символ. Например:                                         
                                                                                
     write(Ch);                         putchar(ch);                            
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Т и п ы   д а н н ы х.                                                
     ----------------------------------------------------------------           
                                                                                
                                                                                
          Основные типы данных Турбо Паскаля имеют соответствующие эк-          
     виваленты в Турбо Си.  Однако Си имеет  как  значительно  большее          
     разнообразие типов   данных с широким выбором числовых диапазонов          
     для значений целых и с плавающей точкой,   так  и  спецификаторов          
     signed и unsigned (со знаком и без знака). Ниже приведена таблица          
     соответствия между типами данных на Паскале и Си.                          
                                                                                
        Турбо Паскаль (версия 3.х)              Турбо Си                        
     ----------------------------------------------------------------           
                                                                                
     char (1 байт) chr(0-255)        char     (1 байт)     -129 - 127           
                                                                                
     byte (1 байт)    0 - 255        unsigned char (1 байт)   0 - 255           
                                                                                
     integer (2 байт) -32763 - 32767 short    (2 байта) -32768 - 32767          
                                                                                
                                     int      (2 байта) -32769 - 32767          
                                                                                

                         - 91,92 -
                                                                                
                                     unsigned int (2 байта)  0 - 65535          
                                                                                
                                     long     (4 байта) -2^31 - 2^31-1          
                                                                                
                                     unsigned long (4 байта)                    
                                                          0 - (2^32-1)          
                                                                                
     real (6 байта)  1E-38 - 1E+38   float    (4 байта)     +-3.4E+308          
                                                                                
                                     double   (8 байт)      +-1.7E+308          
                                                                                
     boolean (1 байт) false, true    0 = false, не ноль = true                  
                                                                                
     ----------------------------------------------------------------           
                                                                                
          Обратите внимание на то, что в Си нет логического типа дан-           
     ных: выражения, в которых требуются логические значения,  интер-           
     претируют значения "ноль", как false (ложь),  а все другие,  как           
     "true" (истина).                                                           
                                                                                
          В дополнение к приведенным типам,  Турбо Си имеет enumerated          
     (перечислимый) тип  данных; однако, в отличие от Паскаля, это пе-          
                                                                                
                                                                                
     реназначаемые целые константы и они полностью совместимы со всеми          
     целыми типами.                                                             
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 93,94 -
                                                                                
                                                                                
                 Турбо Паскаль                         Турбо Си                 
     ----------------------------------------------------------------           
     type                                                                       
       Days = (Sun,Mon,Tues,Wed,     enum days = (Sun,Mon,Tues,Wed,             
               Thurs,Fri,Sat);                    Thurs,Fri,Sat);               
     var                                                                        
       Today : Days;                 enum days today;                           
     ----------------------------------------------------------------           
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Операции.                                                             
     ----------------------------------------------------------------           
                                                                                
          В Турбо Си имеются все операции Турбо Паскаля и еще некото-           
     рые другие.                                                                
                                                                                
          Основным  отличием  между  этими двумя языками является ис-           
     пользование  операции присвоения. В Паскале, присвоение (:=) яв-           
     ляется оператором. В Си присвоение (=) - оператор, который может           
     использоваться в выражениях.                                               
                                                                                
          В таблице 6.1 дается сравнение операций на Турбо Паскале и            
     Турбо Си. Операции  объединены  в группы и приводятся в порядке            
     приоритета.                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 95,96 -
                                                                                
                    Табл.6.1  Операции на Паскале и Си                          
     ----------------------------------------------------------------           
                                                                                
        унарный минус       A:= - B;              a= - b;                       
        унарный плюс        A:= + B;              a= + b;                       
        логическое НЕ       not Flag;             !flag;                        
        поразрядное                                                             
           дополнение       A:= not B;            a=-b;                         
        адрес               A:=Addr(B);           a=&b;                         
        указатель(ссылка)   A:= IntPtr^;          a=*intptr;                    
        размер              A:= SizeOf(B);        a=sizeof(b);                  
        увеличение          A:= Suсс(A);          a++ и ++a                     
        уменьшение          A:= Pred(A);          a-- и --a                     
                                                                                
        умножение           A:= B*C;              a= b*c;                       
        целочисленное                                                           
           деление          A:= BdivC;            a= b/c;                       
        деление             X:= B/C;              x= b/c;                       
        модуль (остаток)    A:= B mod C;          a= b%c;                       
                                                                                
        сложение            A:= B + C;            a=b+c;                        
        вычитание           A:= B - C;            a=b-c;                        
                                                                                
                                                                                
        сдвиг вправо        A:= B shr C;          a=b>>c;                       
        сдвиг влево         A:= B shl C;          a=b< B                 a > b                         
        больше или равно    A > = B               a >= b                        
        меньше чем          A < B                 a < b                         
        меньше или равно    A < = B               a <= b                        
                                                                                
        равно               A = B                 a==b                          
        не равно            A<>B                  a!=b                          
        поразрядное И       A:= B and C;          a=b&c;                        
                                                                                
        поразрядное ИЛИ     A:= B or C;           a=b|c;                        
                                                                                
        поразрядное                                                             
        исключающее ИЛИ     A:= B xor C;          a=b^c;                        
                                                                                
        логическое И        Flag1 and Flag2       flag1 && flag2                
                                                                                
        логическое ИЛИ      Flag1 or Flag2        flag1 || flag2                
                                                                                

                         - 97,98 -
                                                                                
        присвоение          A:= B;                a=b;                          
                            A:= A  B;         a  = b;                   
     ----------------------------------------------------------------           
                                                                                
          Отметим некоторые важные особенности Си.                              
                                                                                
          Во-первых, увеличение  (положительное  приращение)   (++)  и          
     уменьшение (отрицательное приращение) (--) могут располагаться до          
     и после имени переменной. Если оператор расположен до переменной,          
     то она увеличивается (уменьшается),  перед вычислением выражения;          
     если после,  то сначала вычисляется выражение, а затем изменяется          
     значение переменной.                                                       
                                                                                
          Во-вторых, логические операции в Си (&&,||) - эффективно вы-          
     числяемые. Это значит, что если первый параметр определяет истин-          
     ность выражения, то второй никогда не вычисляется. Так, в отличие          
     от Паскаля, Си позволяет записать:                                         
                                                                                
                 while (i <= limit && list[i] != 0) ... ;                       
                                                                                
     где limit  -  максимальный  допустимый индекс массива list.  Если          
     первое выражение (i <= limit) ложно, то Си знает, что все выраже-          
                                                                                
     ние ложно и не надо вычислять второе выражение (list(i)!=0),  где          
     может быть ошибка в диапазоне индекса массива.                             
                                                                                
          В-третьих, Си позволяет вам брать общее выражение вида                
                                                                                
                 A=AB,                                                      
                                                                                
     где  любая бинарная операция (кроме && и ||), и заменять его           
     на                                                                         
                                                                                
                 А=В.                                                       
                                                                                
     Так, например, вместо А=А*В, вы можете написать А*=В,и так далее.          
                                                                                
                                                                                
                                                                                
                                                                                
               Ввод.                                                            
     ----------------------------------------------------------------           
                                                                                
          Опять же  в Турбо Паскале имеется одна базовая команда ввода          
     Read() с  некоторыми вариантами (Readln(), Read(f), ...). В Турбо          

                         - 99,100 -
                                                                                
     Си главная  функция,  используемая для ввода с клавиатуры - scanf          
     имеет формат:                                                              
                                                                                
            scanf (<форматная строка>, <адр1>, <адр2>,...);                     
                                                                                
     где <форматная строка> - строка, содержащая опции форматирования,          
     (аналогично printf),   а каждый <адр> - адрес,  по которому scanf          
     размещает вводимые данные.  Это значит, что вам часто необходимо,          
     будет использовать оператор адреса (&). Есть также другие общеис-          
     пользуемые команды:  gets,  которая читает входную строку до  тех          
     пор, пока вы не нажмете Ввод, и getch, которая читает символ пря-          
     мо с клавиатуры без эхо.                                                   
                                                                                
          Приведем несколько  команд ввода на Паскале и, соответствен-          
     но, на Си.                                                                 
                                                                                
         Турбо Паскаль (версия 3.x)           Турбо Си                          
     ----------------------------------------------------------------           
                                                                                
         Readln(A,B);                         scanf("%d%d",&a,&b);              
         Readln(Name);                        scanf("%s",name);                 
                                                /* или gets(name); */           
                                                                                
                                                                                
         Readln(X,A);                         scanf("%f%d",&x,&a);              
         Readln(Ch);                          scanf("%c",ch);                   
         Read(Kbd,Ch);                        ch = getch();                     
     ----------------------------------------------------------------           
                                                                                
          Отметим одно важное  отличие  между  двумя  способами  ввода          
     строк (scanf и gets).  Scanf читает все символы до тех пор,  пока          
     не встретится пробел (табуляция,  конец строки); напротив,   gets          
     считывает любые символы, пока вы не нажмете Ввод.                          
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 101,102 -
                                                                                
          Блок  операторов.                                                     
     -----------------------------------------------------------------          
                                                                                
          И Паскаль,  и Си поддерживают концепцию блока (группы опера-          
     торов, которая  может быть помещена в любое место как один опера-          
     тор). В Паскале блок имеет вид:                                            
                                                                                
             begin <операт.>; <операт.>; ... <операт.> end;                     
                                                                                
          А в Cи принята форма:                                                 
                                                                                
             { <операт.>; <операт.>; ... <операт.>; }                           
                                                                                
          Эти две формы очень похожи, но имеют два важных отличия:              
                                                                                
          - В  Паскале вы не должны ставить точку с запятой за послед-          
     ним оператором, а в Си должны.                                             
                                                                                
          - В Си вы никогда не ставите точку с запятой после  фигурной          
     скобки (}); в Паскале должны поставить.                                    
                                                                                
                                                                                
                                                                                
                                                                                
               Выполнение по условию.                                           
     -----------------------------------------------------------------          
                                                                                
          Как Паскаль,  так и Си поддерживают две конструкции выполне-          
     ния по условию: оператор if/then/else и оператор case.                     
                                                                                
          if/then/else очень прост в обоих языках                               
                                                                                
          if <булево выраж>                    if(<выраж.>)                     
          then <оператор>                      <оператор>;                      
          else <оператор>                      else <оператор.>;                
                                                                                
          И в Паскале,  и в Си else (иначе)  - необязательная часть, а          
     <оператор> может   быть заменен блоком как ранее описано.  Однако          
     имеется несколько важных различий:                                         
                                                                                
          -  В Си <выраж> не обязано быть булевским, оно может возвра-          
     щать любое нулевое или ненулевое значение, причем ноль рассматри-          
     вается как false (ложь), а не ноль - как true (истина).                    
                                                                                
          -  В Си <выраж.> должно быть заключено в круглые скобки.              

                         - 103,104 -
                                                                                
                                                                                
                                                                                
          -  В Си отсутствует then.                                             
                                                                                
          -  В Си  после оператора обязательно требуется точка с запя-          
     той, естественно за исключением случая, когда там стоит блок.              
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Приведем несколько примеров на Паскале и на Си:                       
                                                                                
             Турбо Паскаль                          Турбо Си                    
     ----------------------------------------------------------------           
                                                                                
     if B = 0 then                       if (B==0)                              
       Writeln('C is undefined')           puts("c is undefined");              
     else begin                          else {                                 
       C := A div B;                       c = a/b;                             
       Writeln('C = ',C)                   printf("c = %d\n",c);                
     end;                                }                                      
                                                                                
     C := A * B;                                                                
     if C < > 0                          if ((c = a * b) != 0)                  
       then C := C + B                        c += b;                           
       else C := A                       else                                   
                                              c = a;                            
     ----------------------------------------------------------------           
                                                                                
                                                                                
                                                                                

                         - 105,106 -
                                                                                
                                                                                
          Оператор  case  также реализован в обоих языках (в Си он из-          
     вестен, как оператор switch),но имеются некоторые важные отличия.          
                                                                                
          Общий формат операторов следующий:                                    
                                                                                
             Турбо Паскаль                          Турбо Си                    
     ----------------------------------------------------------------           
                                                                                
     case <выраж> of                  switch (<выраж>) {                        
       <список>  : <оператор>;          case <элем.>  : <операторы>             
       <список>  : <оператор>;          case <элем.>  : <операторы>             
       ...                              ...                                     
       <список>  : <оператор>;          case <элем.>  : <операторы>             
       else <операторы>                 default      : <операторы>              
     end;                             }                                         
                                                                                
     ----------------------------------------------------------------           
                                                                                
          Кроме  изменений в синтаксисе, имеются и серъезные различия.          
                                                                                
          Во-первых, в Паскале,  <список> может быть списком значений:          
                                                                                
                                                                                
     на Турбо Паскале, это может быть диапазон (А...Z). В Си <элем.> -          
     всегда одно   значение.В обоих языках вы ограничены в выборе типа          
     значения, которое  вычисляется в <выраж>. Оно должно быть целого,          
     символьного или перечислимого типа.                                        
                                                                                
          Во-вторых (и  это очень важно)  в Паскале,  <оператор> - это          
     одиночный оператор или блок; после его выполнения,  все следующие          
     операторы пропускаются. В Си, <операторы> состоят из нулевого или          
     большего числа операторов,  разделенных точкой с запятой. Однако,          
     после выполнения   их,   управление не передается в конец switch;          
     вместо этого,  продолжается выполнение списка операторов  до  тех          
     пор, пока  не встретится оператор break. Тогда и только тогда ос-          
     тальные операторы будут пропущены.  Это позволяет понимать каждый          
     case <элем.>,  как метку, на которую передается управление в слу-          
     чае, когда    <выражение>  оператора  switch  принимает  значение          
     <элем.>.                                                                   
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 107,108 -
                                                                                
                                                                                
           Турбо Паскаль                    Турбо Си                            
     ----------------------------------------------------------------           
                                                                                
      case Ch of                     switch (ch)                                
                                     {                                          
       'С' : DoCompile;               case 'C' : DoCompile(); break;            
       'R' : begin                    case 'R' :                                
         if not Compiled                 if (!compiled)                         
         then DoCompile;                    DoCompile();                        
         RunProgram                      RunProgram();                          
       end;                              break;                                 
       'S' : SaveFile;                case 'S' : SaveFile(); break;             
       'E' : EditFile;                case 'E' : EditFile(); break;             
       'Q' : begin                    case 'Q' :                                
         if not Saved                   if (! saved)                            
         then SaveFile                      SaveFile();                         
       end;                              break;                                 
     end;                            }                                          
                                                                                
     case Today of                   switch (today)                             
                                     {                                          
                                                                                
     Mon..Fri : Writeln('go work!');    case Mon :                              
     Sat, Sun : begin                   case Tue :                              
         if Today = Sat then begin      case Wed :                              
         Write('clean the yard');       case Thur:                              
         Write(' and ')                 case Fri :puts("go work!");             
                                          break;                                
         end;                           case Sat :printf("%s","clean"           
                                                   " the yard and ");           
         Writeln('relax!')              case Sun :puts("relax!");               
         end                                                                    
     end;                            }                                          
                                                                                
     -----------------------------------------------------------------          
                                                                                
          Примечания по второму примеру.  Запись - сase <элем.> (часть          
     оператора switch) в любом случае должна быть написана. Части case          
     от Mon до Thur имеют пустую часть <операторы> и управление  пере-          
     ходит далее   пока не встретится метка case Fri:.  Оператор break          
     передает управление в конец оператора switch. Однако, в конце не-          
     дели программа будет работать по другому:метка case Sat: заставит          
     выполниться printf,  после чего управление перейдет  к  оператору          
     puts.                                                                      

                         - 109,110 -
                                                                                
                    Циклы (итерации).                                           
     -----------------------------------------------------------------          
                                                                                
          В  Си,  так же как в Паскале,  есть  3  типа  циклов: while,          
     do...while и for, которые соответствуют трем конструкциям  Паска-          
     ля: while, repeat...until, for.                                            
                                                                                
                                                                                
                                                                                
               Цикл while (пока).                                               
     -----------------------------------------------------------------          
                                                                                
          Этот цикл наиболее близок в обоих языках:                             
                                                                                
          while <булево выраж> do              while (<выраж>)                  
             <оператор>;                          <оператор>;                   
                                                                                
          В обоих языках, вы можете использовать блок для вставки нес-          
     кольких операторов в цикл. Единственная разница в том, что Си об-          
     ладает большей гибкостью в том,  что принимается за  <выражение>.          
     Для примера, сравните следующие два цикла:                                 
                                                                                
                                                                                
         Read(Kbd,Ch);                                                          
         while Ch <> 'g' do begin       while ((ch = getch()) != 'g')           
            Write(Ch); Read(Kbd,Ch);       putchar(ch);                         
         end;                                                                   
                                                                                
                                                                                
                                                                                
               Цикл do...while (выполнять...пока).                              
     ----------------------------------------------------------------           
                                                                                
          Цикл do...while аналогичен циклу repeat...until в Паскале:            
                                                                                
              repeat                     do                                     
                  <операторы>                <оператор>;                        
              until<булево выраж>;       while <выраж>;                         
                                                                                
          Но есть и два важных отличия между двумя циклами:                     
                                                                                
          - Цикл  do...while  выполняется  пока  <выраж>  истино,    а          
            repeat...until до   тех пор,  когда <булево выраж.> станет          
            истинно.                                                            
                                                                                

                         - 111,112 -
                                                                                
                                                                                
          - Оператор repeat...until не требует  блока  для  нескольких          
            операторов, а do...while требует.                                   
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Ниже приводятся примеры для каждого цикла:                            
                                                                                
                Турбо Паскаль                     Турбо Си                      
     ----------------------------------------------------------------           
                                                                                
     repeat                           do {                                      
       Write('Введите значение: ');     printf("Введите значение: ");           
       Readln(A)                         scanf("%d",@a); }                      
     until(Low<=A) and (A<=High);     while (a < low || a > high);              
                                                                                
     ----------------------------------------------------------------           
                                                                                
          В дополнение  отметим  важные различия в Си и Паскале:  в Си          
     операции сравнения (<,> и др.) имеют более высокий приоритет, чем          
     логические (&&,  ||). Это позволяет вам не заключать каждое выра-          
     жение сравнения в круглые скобки, как это вы делаете в Паскале.            
                                                                                
                                                                                
                                                                                
               Цикл for (для).                                                  
     -----------------------------------------------------------------          
                                                                                

                         - 113,114 -
                                                                                
          Реализация цикла for наиболее различается при программирова-          
     нии на Паскале и Си.  В Паскале for - цикл более постоянный,  а в          
     Си он более гибкий, разрешающий конструкции, с которыми он теряет          
     всякое сходство с циклом for.                                              
                                                                                
          Формат этого цикла следующий:                                         
                                                                                
     for<индекс>:=<нач>to<кон>do      for(<выраж1>;<выраж2>;<выраж3>)           
            <оператор>;                           <оператор>;                   
                                                                                
          В Си оператор for - это просто специальный случай оператора           
     while:                                                                     
              <выраж1>                                                          
              while (<выраж2>) {                                                
                  <оператор>;                                                   
                  <выраж3>;                                                     
              }                                                                 
                                                                                
          где <выраж1> используется для инициализации,                          
              <выраж2> для определения конца цикла,                             
              <выраж3> для изменения параметра(ов) цикла.                       
                                                                                
                                                                                
                                                                                
          Ниже приведено несколько примеров,  которые используют  цикл          
     while на Паскале:                                                          
                                                                                
          Турбо Паскаль                   Турбо Си                              
     ----------------------------------------------------------------           
                                                                                
     for I := i to 10 do begin        for (i = i;i<=10;i++) {                   
     Write('I = ',I:2);               printf("i = %2d ",i);                     
     Write(' I*I = ',(I*I):4);        printf("i*i = %4d ",i*i);                 
     Writeln(' I**3 = ',(I*I*I):6)    printf("i**3=%6d\n",i*i*i);               
     end;                             }                                         
                                                                                
     I := 17; K := I;                                                           
     while (I>-450) do begin          for (i=17, k=i; i >-450;                  
     K := K + I;                      k+=i, i-=15)                              
     Writeln('K=',K,'I=',I);          printf("k=%d i=%d\n",k,i);                
     I := I - 15                                                                
     end;                                                                       
                                                                                
     X := D/2.0;                      for(x=d/2; fabs(x*x-d)>0.01;              
     while(Abs(X*X-D)>0.0I) do        x = (x+d/x)/2)                            

                         - 115,116 -
                                                                                
                                                                                
     X := (X + D/X)/2.0;              ;      /*Пустой оператор*/                
     ----------------------------------------------------------------           
                                                                                
          Заметим, что этот цикл позволяет включать внутрь секции  for          
     так много,    что в конце концов может не остаться операторов для          
     выполнения; все действия будут производится внутри заголовка цик-          
     ла.                                                                        
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Подпрограммы.                                                         
     -----------------------------------------------------------------          
                                                                                
          И в  Паскале и Си имеются подпрограммы; Паскаль имеет проце-          
     дуры и функции,  в то время как Си имеет только функции.  Однако,          
     вы можете объявить функцию типа void,  что позволит ей не возвра-          
     щать никакого значения; если вы захотите, то можете также игнори-          
     ровать значение, выдаваемое функцией.                                      
                                                                                
          Формат функции в обоих языках следующий:                              
                                                                                
          Турбо Паскаль                       Турбо Си                          
     ----------------------------------------------------------------           
                                                                                
     function FName(<описан.парам>)       <тип> FName(<описан.парам>)           
                :<тип>;                                                         
       <локальные описания>                                                     
     begin                                 {                                    
       <операторы>                            <локальные описания>              
     end;                                     <операторы>                       
                                           }                                    
     ----------------------------------------------------------------           

                         - 117,118 -
                                                                                
                                                                                
          В Паскале <описание параметров> дается в форме <имена пара-           
     метров>:<тип>;  для каждой группы параметров. В Си - <тип><имена           
     параметров>.                                                               
                                                                                
          Другие важные различия видны из примера:                              
                                                                                
          Турбо Паскаль                       Турбо Си                          
     ----------------------------------------------------------------           
                                                                                
     function Max(A,B : Integer):Integer;   int max(int a, int b)               
     begin                                  {                                   
       if A > B                                 if (a > b)                      
       them Max := A                               return(a);                   
       else Max := B                            else                            
                                                   return(b);                   
     end;                                    }                                  
     ----------------------------------------------------------------           
                                                                                
          Заметим, что в Си оператор return используется для возвраще-          
     ния значения функции,  в то время как в Паскале  это  достигается          
     присвоением значения имени функции.                                        
                                                                                
                                                                                
          Турбо Паскаль                      Турбо Си                           
     -----------------------------------------------------------------          
                                                                                
     procedure Swap(var X,Y : Real);    void swap(float *x, float *y)           
                                                                                
     var                                                                        
       Temp : Real;                      {                                      
     begin                                 float temp;                          
       Temp := X;                          temp= *a;                            
       X := Y;                             *a = *b;                             
       Y := Temp                           *b = temp;                           
     end;                                }                                      
     -----------------------------------------------------------------          
                                                                                
          B Паскале имеется 2 типа параметров:var (передача по адресу)          
     и value (по значению). В Си параметры передаются только по значе-          
     нию. Если  вы хотите иметь параметры,  передаваемые по адресу, то          
     вы должны передавать адрес,  а формальный параметр определить как          
     указатель. Это  было продемонстрировано в вышеприведенном примере          
     функции swap. Ниже приведена программа вызова этих подпрограмм:            
                                                                                

                         - 119,120 -
                                                                                
                                                                                
          Турбо Паскаль                  Турбо Си                               
     ----------------------------------------------------------------           
                                                                                
     Q := 7.5;                        g = 7.5;                                  
     R := 9.2;                        r = 9.2;                                  
     Writeln('Q=',Q:5:1,'R=',R:5:1);  printf("g=%5.1f r=%5.1f\n",g,r);          
     Swap(Q,R);                       swap(&g,&r);                              
     Writeln('Q=',0:5:1,'R=',R:5:1);  printf("g=%5.1f r=%5.1f\n",g,r);          
                                                                                
     -----------------------------------------------------------------          
                                                                                
          Отметим использование операции адреса (&)  в Си при передаче          
     параметров g и r подпрограмме swap.                                        
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Прототипы  функций.                                                   
     -----------------------------------------------------------------          
                                                                                
          Наиболее важные различия между Паскалем и Си касаются  функ-          
     ций: Паскаль  всегда делает проверку на соответствие количества и          
     типов параметров,  определенных в функции, с количеством и типами          
     параметров, используемых при вызыве этой функцией. Другими слова-          
     ми, допустим вы определили функцию:                                        
                                                                                
              function Max(I,J : Integer) : Integer;                            
                                                                                
     и пытаетесь   вызвать    ее    с    действительными    значениями          
     (А:=Мах(В,3.52);). Что    произойдет?  Вы получите при трансляции          
     ошибку, сообщающую вам, что присутствует несоответствие типов, т.          
     к. 3.52 не является целым.                                                 
                                                                                
          В Си  это  не так,  по умолчанию Си-компилятор не производит          
     проверку ошибок при вызове функции:  он не проверяет возвращаемые          
     функцией параметры,  их типы и количество.  С одной стороны - это          
     дает некоторую свободу,  так как можно вызывать функцию до  того,          
     как она будет определена, с другой - может причинить и беспокойс-          
     тво (см. "Ошибку #2" в заключении главы). Можно ли это как-то из-          

                         - 121,122 -
                                                                                
     бежать?                                                                    
                                                                                
          Турбо Си поддерживает прототипы функций.  Вы можете понимать          
     это, как нечто подобное предварительному описанию в Паскале. Ста-          
     райтесь располагать   прототипы функций в начале текста программы          
     (перед вызовом этих функций).  Запомните,  что прототип функции -          
     является видом  описания и должен предшествовать фактическому вы-          
     зову функции. Прототип функции имеет формат:                               
                                                                                
             <тип> FName (<тип><имя>,<тип><имя>, и т.д.);                       
                                                                                
          Это очень похоже на описание функций в Паскале, но с некото-          
     рыми отличиями.  Во-первых,  описание каждого параметра разделяет          
     запятая (а не точка с запятой); во-вторых,  вам  нельзя  задавать          
     список нескольких <имен> для одного <типа>.                                
                                                                                
          Приведем несколько примеров:                                          
                                                                                
           int    max(int a, int b);                                            
           void   swap(float *x, float *y);                                     
           void   swapitem (listitem *i, listitem *j);                          
           void   sortlist (list l, int c);                                     
                                                                                
           void   dumplist (list l, int c);                                     
                                                                                
          В отличие от оператора предварительного описания - forward у          
     Паскаля, прототипы  функций в Си освобождают вас от лишних усилий          
     при фактическом   определении функции.  Другими словами вы можете          
     определить функцию как вам хочется (или определить ее,  используя          
     современный стиль программирования). Конечно, если описание функ-          
     ции не будет соответствовать прототипу,  Турбо Си  выдаст  ошибку          
     компиляции.                                                                
                                                                                
          Турбо Си  поддерживает и классический,  и современный стиль,          
     однако сейчас в Си тенденция к использованию нового стиля, поэто-          
     му и мы рекомендуем вам применять прототипы функций.                       
                                                                                
          Использование прототипов функций помогает избежать некоторых          
     проблем, особенно, когда вы используете оттранслированные библио-          
     теки Си программ. Вы можете создать отдельный файл и ввести в не-          
     го заголовки функций всех подпрограмм из библиотеки. Когда вы за-          
     хотите использовать  любую программу из библиотеки,  включите ваш          
     заголовок файла в программу (с помощью директивы #include). Такой          
     способ поможет  вам избежать неприятностей от возможной ошибки во          
     время трансляции.                                                          

                         - 123,124 -
                                                                                
          Основной пример.                                                      
     -----------------------------------------------------------------          
                                                                                
          Теперь приведем большой пример,  целую программу, использую-          
     щую многое из того, что вы узнали до сих пор. Программа определя-          
     ет массив myList,  длина которого - LMAX, а тип (List Item) соот-          
     ветствует целому. Массив инициализируется неупорядоченными числа-          
     ми, выводится  на дисплей подпрограммой dumplist,  сортируется по          
     порядку с помощью sortlist, и снова выводится на дисплей.                  
                                                                                
          Отметим, что данная Си версия программы не самая лучшая. Она          
     была написана в максимально возможном  приближении  к  версии  на          
     Паскаль. Некоторые   места,  которые не соответствуют друг другу,          
     демонстрируют коренные различия Си и Паскаля.                              
                                                                                
               Турбо Паскаль                      Турбо Си                      
     ----------------------------------------------------------------           
                                                                                
     program DoSort;                                                            
     const                                                                      
       LMax = 100;                       #define LMAX 100                       
     type                                                                       
                                                                                
       Item = Integer;                   typedef int  item;                     
       List = array[1..LMax] of Item;    typedef item list[LMAX];               
     var                                                                        
       myList  : List;                   list    myList;                        
       Count,I : Integer;                int     count, i;                      
       Ch      : Char;                   char    ch;                            
                                                                                
     procedure SortList(var L:List;      void swapitem(item *i,item*j)          
       C : Integer);                                                            
     var                                 {                                      
       Top,Min,K : Integer;              item temp;                             
                                         temp = *i; *i = *j; *j =temp;          
     procedure SwapItem(var I,J:Item);   } /* swapitem */                       
     var                                                                        
       Temp : Item;                      void sortlist(list l, int c)           
     begin                                                                      
       Temp:=I; I:=J; J:=Temp                                                   
     end; (*конец SwapItem*)                                                    
                                         {                                      
                                           int top,min,k;                       
     begin (*Основное тело SortList*)                                           
     for Top:=l to C-1 do begin            for(top=0; top First              strcpy(name,first);                       
         then Name:=First;        i=823;sprintf(temp,"\d", i);                  
       I:=823;Str(I,Temp);        i=atoi(temp);                                 
       Val(Temp,Err);             ptr=(char*)malloc(81);                        
       GetMem(Ptr,81);            strcpy(ptr,"This is a test.");                
                                  printf("ptr=\s\n",ptr);                       
       Ptr^:='This is a test.';   free(ptr);                                    
       Writeln('Ptr= ',Ptr^);                                                   
       FreeMem(Ptr,81);                                                         
                                                                                
                                                                                
     end.                         }                                             
                                                                                
                                                                                
          Использование Ptr в исходной программе, написанной на Паска-          
     ле - это одна из вариантных записей и служит для того,  чтобы по-          
     лучить эквивалент Си кода.                                                 
                                                                                
          Последний пункт: прототипы функции для подпрограмм Си, вызы-          
     ваемых в   этом примере,  перечислены в заголовке файлов; так для          
     обнаружения ошибки вам нужно расположить следующие #include  опе-          
     раторы в начале программы Турбо Си.                                        
                                                                                
           # include                                                   
           # include                                                  
           # include                                                  
           # include                                                   
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 143,144 -
                                                                                
          Structures (Структуры).                                               
     -----------------------------------------------------------------          
                                                                                
          Оба языка и Паскаль, и Си позволяют определить унифицирован-          
     ные и неоднородные структуры данных.                                       
                                                                                
          В Паскале они называются records, а в Си - структуры.                 
                                                                                
          Формат для обоих:                                                     
                                                                                
          Турбо Паскаль                        Турбо Си                         
     ----------------------------------------------------------------           
                                                                                
     type                                       typedef struct {                
        =record                             ;              
        :;                           ;              
        :;                            ...                         
         ...                                       ;              
        :                          } ;                     
     end;                                                                       
                                                                                
     var                                                                        
                                                                                
        :;                        ;               
                                                                                
                                                                                
          Существует также более строгий формат в Си для прямого опре-          
     деления структур переменных, более аналогичный формату Паскаля.            
                                                                                
               Турбо Паскаль                        Турбо Си                    
     ----------------------------------------------------------------           
     var                                                                        
        :record                      struct                      
                                             {                                  
        :                        ;                  
         ...                                    ...                             
        :                        ;                  
      end;                                   } ;                        
                                                                                
          В этом варианте,  необязательная структура; вы должны          
     записать ее,  если планируете  объявить  другие  переменные  типа          
     . Кроме  того,  записи в Паскале и структуры в Си довольно          
     близки.                                                                    
                                                                                
          Турбо Паскаль                         Турбо Си                        

                         - 145,146 -
                                                                                
     ----------------------------------------------------------------           
     Type                                                                       
        Student=record                    struct student {                      
        Last,First:string[20];              char last[20],first[20];            
        SSN  : string[11];                  char ssn[11];                       
        Fgt  : Integer;                     int  age;                           
        Tests: array[1..5] if integer;      int  tests [5];                     
        GPA  : Real                         float gra;                          
     end;                                 } current;                            
                                                                                
     var                                  main ()                               
       Current: Student;                  {                                     
     begin                                                                      
       Current.Last='Smith';             strcpy(current.last,"Smith");          
       Current.Age =21;                     current.age=21;                     
       Current.Test[1]=97;                  current.test[0]=97;                 
       Current.GPA=3.94;                    current.gpa=3.94;                   
                                                                                
     end                                  }                                     
                                                                                
                                                                                
          Главное различие между Паскалем и Си в том,  что  в  Паскале          
                                                                                
                                                                                
     имеются операторы,   а в Си - нет.  Мы могли бы переписать код на          
     Паскале, использовав выше оператор with Current do , и затем ссы-          
     латься на   поля  без  указания в начале Current.  В Си вы всегда          
     должны указывать Current в начале.  Однако, Си также имеет опера-          
     тор доступа member (->),  используемый, когда идентификатор слева          
     от оператора является указателем на собственную  структуру.   Для          
     примера, если pstudent является указателем на struct, тогда                
                                                                                
           strcpy(pstudent -> last,"Jones");                                    
                                                                                
     присвоит переменной last строку "Jones".                                   
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 147,148 -
                                                                                
          Union (Объединение).                                                  
     -----------------------------------------------------------------          
                                                                                
          И Паскаль, и Си содержат сходное понятие, называемое: в Пас-          
     кале -  переменная (вариантная) структура записи,  a в Си - union          
     (объединение).  Конкретные способы его описания показаны в приме-          
     ре:                                                                        
                                                                                
          Турбо Паскаль                  Турбо Си                               
     ----------------------------------------------------------------           
                                                                                
     type                                                                       
        =record                     union {                       
                                  ;                    
     case of                           ;                    
         :()                ...                               
         :()               ;                    
         ...                               };                                   
         :()                                                   
     end;                                                                       
                                                                                
          B Паскале  является обычной записью последователь-          
                                                                                
     ностей :;, повторяемой нужное количество раз.                 
                                                                                
          Два основных различия между Паскалем и Си в следующем:                
                                                                                
          1) Паскаль заставляет положить объединение в основу регуляр-          
     ной записи,  а Си - нет.  Однако, вы можете сначала объявить объ-          
     единение, а  затем указать поле в структуре,  принадлежащее типу,          
     описанному в объединении.                                                  
                                                                                
          2) Паскаль  позволяет  вам иметь множество типов для каждого          
     варианта в  объединении.   Си  позволяет  иметь  множество  полей          
     (), но все должны быть одного типа.                                
                                                                                
          Вот образец,   написанный в Паскаль и Си версиях,  как можно          
     ближе к друг другу (однако,  можно допустить,  что они не  вполне          
     идентичны).                                                                
                                                                                
          Турбо Паскаль                    Турбо C                              
     ----------------------------------------------------------------           
                                                                                
     type                                   typedef union {                     
         trick_word=record                   int w;                             

                         - 149,150 -
                                                                                
                                                                                
     case integer  of                        struct {                           
         0:(w:integer);                         char lob;                       
         1:(lob,hib:byte);                      char hib;                       
     end;                                    } b;                               
     var xp:trick_word;                      } trick_word;                      
                                             trick_word xc;                     
                                                                                
          Примечание: определение trick_word ни в Си, ни в Паскале не           
     переносимо. Они оба зависят от порядка байт 8086.                          
                                                                                
          В Си объединении, как и в структуре, вы можете вставить поле          
      между  закрытой скобкой и точкой с запятой,  чтобы прямо          
     указать переменные того типа.  В этом операторе выбора вы  можете          
     оставить свободным   ,   если вы не собираетесь указывать          
     больше таких переменных.  Полями ссылок в Паскале являются: xp.w,          
     xp.hib и xp.lob, а в Си - xc.w, xc.b.hib и xc.b.lob.                       
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Выводы по программированию.                                           
     -----------------------------------------------------------------          
                                                                                
          Как программист  на Паскале,  вы не должны ощущать трудности          
     при изучении Турбо Си. Но есть небольшая область в программирова-          
     нии, которая вызывает некоторые трудности при переходе с языка на          
     язык. Мы обсудим каждый из таких разделов в этой части.                    
                                                                                
                                                                                
                                                                                
               Чувствительность к регистрам.                                    
     -----------------------------------------------------------------          
                                                                                
          Паскаль, в отличие от  Си,   не  чувствителен  к  типу  букв          
     (строчные - заглавные).  Если идентификаторы indx,  Indx и INDX в          
     Паскале ссылаются на одинаковые переменные, то в Си - на три раз-          
     личные переменные.                                                         
                                                                                
          Примечание: т.к. вызовы функций не проявляют себя до тех пор          
     пока программа на  Си  не  скомпанована,  то различие в регистрах          
     может не чувствоваться. Для вашей собственной пользы будьте точны          
     при выборе регистра на Си.                                                 

                         - 151,152 -
                                                                                
                                                                                
                                                                                
                                                                                
               Приведение типов.                                                
     -----------------------------------------------------------------          
                                                                                
          Паскаль, как правило,  позволяет использовать только ограни-          
     чение типов. Функция Ord() переводит из любого порядкового (пере-          
     числимого) типа  в Integer; Chr() переведит из Integer (или родс-          
     твенных типов) в Char. Турбо Паскаль допускает некоторые добавоч-          
     ные типы при переводе (так называемое retyping)  между всеми  по-          
     рядковыми типами (Integer,  Char, Boolean и переменным типам дан-          
     ных). Си  более свободен и позволяет вам пробовать  смену  одного          
     типа на любой другой, но без гарантии благоприятного исхода.               
                                                                                
          Турбо Паскаль                           Турбо Си                      
     ----------------------------------------------------------------           
                                                                                
        :=();                   =();          
        var Ch : Char;                           char ch;                       
                                                                                
        I  := Integer (Ch);                          i = (int) ch;              
                                                                                
        Ch := Char (Today);                         ch = (char) today;          
        Noday :=Days(3);                         today = (days) 3;              
                                                                                
          Добавим, что  Турбо  Си  может сделать большинство переводов          
     автоматически, главным  образом между типами,   которые  являются          
     Integer compatible  (тип,   чьи выделенные представления являются          
     величинами Integer).  Исходя из этого, все три оператора могли бы          
     пропускать определенное указание типа значения выражения. Вы мог-          
     ли бы написать:                                                            
                                                                                
         i=ch;        ch=today;     today=3;                                    
                                                                                
                                                                                
                                                                                
               Константы, переменные записи и инициализация.                    
     -----------------------------------------------------------------          
                                                                                
          Турбо Паскаль не инициализирует переменные,  которые вы ука-          
     зали. Также  не сохраняет величины переменных, указанные в преде-          
     лах процедуры (и функции) между вызовами подпрограмм. Главное ис-          
     ключение - инициализируемые постоянные,  они будут сохранять свои          
     величины между вызовами подпрограмм,  в которых  они  определены,          

                         - 153,154 -
                                                                                
     включая любые  величины,  которые вы можете назначить им во время          
     выполнения.                                                                
                                                                                
          В Си по умолчанию  глобальным переменным присваивается нача-          
     льное значение  0,  если  вам не ясно,  какое присвоить начальное          
     значение.                                                                  
                                                                                
          Турбо Си дает вам два типа констант, разрешает предваритель-          
     но инициализировать любую переменную,  и сохраняет значения пере-          
     менных, объявленных внутри функции, как статические.                       
                                                                                
                                                                                
                                                                                
               Типы констант.                                                   
     ----------------------------------------------------------------           
                                                                                
          Существуют два типа констант с форматом:                              
                                                                                
          #define                                                 
          const                = ;                          
                                                                                
          Первый  тип (#define...)  более точно подходит к определению          
                                                                                
                                                                                
     констант в Паскале,  в котором   (значение) прямо присваи-          
     вается там, где будет найдено имя константы .                       
                                                                                
          Второй  тип  (const...)    больше   похож  на типизированные          
     константы в Турбо Паскале, однако, исключает возможность изменять          
     значение ;  любая попытка модификации или назначения новой          
     величины дает в итоге ошибку.                                              
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 155,156 -
                                                                                
     Инициализация переменных.                                                  
     ----------------------------------------------------------------           
                                                                                
          Турбо Си позволяет инициализировать любую переменную,  кото-          
     рая, таким образом, совпадает с типизированной константой в Турбо          
     Паскале. Формат следующий:                                                 
                                                                                
               =  ;                                         
                                                                                
          Элементы данных,  нуждающиеся более  чем  в  одной  величине          
     (массивы, структуры)  должны иметь значения, заключенные в фигур-          
     ные скобки и отделенные запятыми ({like_this, and_this, and_this_          
     too}).                                                                     
                                                                                
         int  x = 1, y = 2;                                                     
         char name []="Франк";                                                  
         char answer ='Y';                                                      
         char key = 3;                                                          
         char list[2] [10] = {"Первый", "Второй"};                              
                                                                                
                                                                                
                                                                                
                                                                                
               Переменные памяти.                                               
     -----------------------------------------------------------------          
                                                                                
          Си определяет  несколько  классов памяти для переменных; два          
     наиболее важных - external (внешний)  и automatic (local)  (мест-          
     ный). Глобальные  переменные (которые описываются вне любой функ-          
     ции, включая основную) являются внешними по умолчанию. Это подра-          
     зумевает, что    им  присваивается  начальное значение 0 в начале          
     выполнения программы, если вы сами не присвоили им значения.               
                                                                                
          Переменные, указываемые в пределах функций (в том числе  ос-          
     новной), принимаются  по умолчанию automatic. Им не присваиваются          
     значения, если вы не сделали это, и они теряют свои величины меж-          
     ду вызовами этой функции. Однако вы можете указать такие перемен-          
     ные static; им будет присвоен 0 (сразу, в начале выполнения прог-          
     раммы) и они сохранят свои значения между вызовами функций.                
                                                                                
          Рассмотрим следующий пример                                           
                                                                                
          init test (void)                                                      
          {                                                                     
            int i;                                                              

                         - 157,158 -
                                                                                
            static int count;                                                   
            ...                                                                 
          }                                                                     
                                                                                
          Переменная i находится в стеке и может быть инициализирован-          
     на функцией test каждый раз,  когда вызывается программа.  Cтати-          
     ческая переменная count,  c другой стороны находится в глобальной          
     зоне данных и инициализируется нулем, когда программа выполняется          
     впервые. Count  сохраняет свое предыдущее значение при  следующем          
     вызове функции test.                                                       
                                                                                
                                                                                
                                                                                
               Динамическое распределение памяти.                               
     -----------------------------------------------------------------          
                                                                                
          В Турбо Паскале есть несколько методов для управления  кучей          
     (динамической областью памяти). Дадим следующее описание на Турбо          
     Паскале:                                                                   
                                                                                
       type                                                                     
         ItemType=Integer;                                                      
                                                                                
         ItemPtr=^ItemType;                                                     
                                                                                
       var                                                                      
         p : ItemPtr;                                                           
                                                                                
           На практике используется три различных метода распределения          
     и освобождения динамической памяти:                                        
                                                                                
         (* New и Dispose*)                                                     
                                                                                
         New(p);                   {Автоматическое размещение                   
                                                   требуемой памяти}            
         ...                                                                    
         Dispose(p);               {Автоматическое освобождение                 
                                              задействованой памяти}            
                                                                                
         (*New, Mark и Release*)                                                
                                                                                
         New (p);                  {Автоматическое размещение                   
                                                   требуемой памяти}            
         ...                                                                    
         Mark(p);                                                               

                         - 159,160 -
                                                                                
         Release(p)                {Освобождение всей динамической              
                                   памяти, начиная с p до конца кучи}           
                                                                                
         (*FreeMem и GetMem*)                                                   
                                                                                
         GetMem(p, Sizeof(ItemType));  {Определение объема памяти               
                                                     для размещения}            
         ...                                                                    
         FreeMem(p, Sizeof(ItemType)); {Определение объема памяти               
                                                   для освобождения}            
                                                                                
          В Турбо Си для распределения и освобождения динамической па-          
     мяти  используются  подпрограммы,  которые  абсолютно  похожи  на          
     GetMem и Dispose в Турбо Паскале:                                          
                                                                                
          *;                                                         
                                                                                
         =(*) calloc(,);                                  
         /* или =(*) malloc();*/                         
         /* или =(*) realloc(,);*/                         
         free ();                                                          
                                                                                
                                                                                
         typedef int ItemType;                                                  
         ItemType *p;                                                           
                                                                                
         p = (ItemType*) malloc(sizeof(ItemType));                              
         ...                                                                    
         free(p);                                                               
                                                                                
          Все три подпрограммы в Си возвращают родовой указатель,  ко-          
     торый может  быть переведен в соответствующий тип.  Все они также          
     возвращают NULL, если нет достаточной памяти для распределения.            
                                                                                
          Функция саlloc ожидает, что вы передадите ей количество соз-          
     даваемых элементов   данных и размер (в байтах)  одного элемента.          
     Она создает элементы данных,  присваивает им всем 0 и  возвращает          
     указатель в  исходный блок.  Это очень удобно для автоматического          
     создания массивов.                                                         
                                                                                
          malloc сообщается, сколько нужно байт для распределения.              
                                                                                
          free освободит пямять, на которую указывает .                    
                                                                                
                                                                                

                         - 161,162 -
                                                                                
               Аргументы командной строки.                                      
     -----------------------------------------------------------------          
                                                                                
          Когда вы создаете Com файл,  используя Турбо Паскаль,   ваша          
     программа может считать любые аргументы,  указанные в строке, ис-          
     пользуя функции ParamCount и ParamStr.  Для примера, если вы соз-          
     дали программу,  назвав ее DUMPIT.COM,  и исполняете ее следующим          
     образом:                                                                   
                                                                                
          A>dumpit myfile.txt other.txt 72                                      
                                                                                
          ParamCount вернет величину 3, и ParamStr возвратит следующие          
     величины:                                                                  
                                                                                
          ParamStr(1)                  myfile.txt                               
          ParamStr(2)                  other.txt                                
          ParamStr(3)                  72                                       
                                                                                
          Турбо Си позволяет вам идентифицировать argc, argv, env как           
     основные параметры:                                                        
                                                                                
          main(int argc, char *argv[], char *env[]);                            
                                                                                
          {                                                                     
          ...основное тело...                                                   
          }                                                                     
                                                                                
     где argc - количество аргументов, argv[] - массив строк, включаю-          
     щий параметры. В этом примере argc равен 4, а argv[] указывает на          
     следующее:                                                                 
                                                                                
          argv[0]                      A:\DUMPIT.EXE                            
          argv[1]                      myfile.txt                               
          argv[2]                      other.txt                                
          argv[3]                      72                                       
          argv[4]                      (null)                                   
                                                                                
          В Си  под  управлением MS-DOS версий 3.x,  argv[0] определен          
     (ParamStr(0) - нет) и содержит имя выполняемой программы. Для MS-          
     DOS версии 2.х,  argv[0] указывает на пустую строку (""), argv[4]          
     также содержит ноль.                                                       
                                                                                
          Третий аргумент env[] - это массив, содержащий строки следу-          
     ющей формы:                                                                
                                                                                

                         - 163,164 -
                                                                                
                                                                                
          envvar = value                                                        
                                                                                
     где envvar - имя окружения переменной,  а value - строковая вели-          
     чина, которую содержит envvar.                                             
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
               Файлы ввода/вывода.                                              
     -----------------------------------------------------------------          
                                                                                
          В стандартном Паскале имеется два  типа  файлов:   текстовый          
     (описываемый как  text)  и данных (date,  описываемый как file of          
     <тип>). Последовательность  для открытия,  модификации и закрытия          
     файлов почти идентична для обоих типов.  Турбо Паскаль имеет тре-          
     тий тип (нетипизированный файл),  что очень  напоминает  двоичные          
     файловые операции, используемые в Турбо Си.                                
                                                                                
          В Си  с  файлами  обычно обращаются,  как с потоком символов          
     (побайтно); для вас текстовый файл от файла данных в основном от-          
     личается признаками - t (текстовый)  и b (binary - двоичный), ко-          
     торые могут указываться при определении файла.                             
                                                                                
          Эквиваленты между двумя языками:                                      
                                                                                
          Турбо Паскаль                        Турбо Си                         
     ----------------------------------------------------------------           
                                                                                
     var                                                                        
     I       :Integer;                   Int    i;                              

                         - 165,166 -
                                                                                
     X       :Real;                      float  x;                              
     Ch      :Char;                      char   ch;                             
     Line    :string[80];                char   line[80];                       
     myRec   :RecType;                   struct rectype myrec;                  
     buffer  :array[1..1024] of char     char   buffer[1024]                    
                                                                                
     F1      :text;                      FILE   *f1;                            
     F2      :file of RecType;           FILE   *f2;                            
     F3      :file;                      FILE   *f3;                            
     Assign(,);             =fopen(,"r");             
     Reset ();                    /* or                                   
                                         =fopen(,"r+");*/          
     Reset (,); /* or                                   
                                            f1=fopen(,"r+t");*/          
                                        /* or                                   
                                            f2=fopen(,"r+b");*/          
                                                                                
     Assign(,);            =fopen(,"w");              
     Rewrite();                   /* or                                   
                                         =fopen(,"w+");*/          
     Rewrite(,);/* or                                   
                                            f1=fopen(,"w+t");*/          
                                                                                
                                        /* or                                   
                                            f2=fopen(,"w+b");*/          
                                                                                
     Assign(,);            =fopen(,"a+";              
     Append();               /* or                                   
                                        =fopen(,"a+t");*/          
                                        /* or                                   
                                        =fopen(,"a+b");*/          
                                                                                
     Read(F1,Ch);                         ch=getc(f1);                          
     Readln (F1,Line);                    fgets(f1,80,line);                    
     Readln (F1,I,X);                     fscanf(f1,"%d%f",gi,gx);              
     Read  (F2,MyRec);                    fread(&myrec,                         
                                                sizeof(myrec),i,1,f2);          
     BlockRead(F3,buffer,Sizeof(buffer)); fread (&bufer,1,                      
                                                  sizeof(buffer), f3);          
                                                                                
     Write(F1,Ch);                        fputc(ch,f1);                         
                                          /*or fprint (f1,"%c",ch);*/           
     Write(F1,Line);                      fputs(line,f1);                       
                                          /*or fprint(f1,"%s",line);*/          
     Write(F1,,,I,X);                     fprint(f1,"%d%f",i,x);                

                         - 167,168 -
                                                                                
     Writeln(F1,I,X);                     fprint(f1,"%d%f\n",i,x);              
     Write(F2,MyRec);                     fwrite(&myrec,                        
                                                  sizeof(myrec),1,f2);          
                                                                                
     Seek(F2,);                     fseek(f2,                             
                                            *sizeof(rectype),0);          
     Flush();                       fflush();                       
     Close();                       fclose();                       
     BlockWrite(F3,buffer,Sizeof(buffer); fwrite(&buffer,1,                     
                                                   sizeof(buffer),f3);          
                                                                                
          Вам необходимо обратиться к книге 2 (Справочное руководство)          
     для более детального понимания работы каждой из программ на Турбо          
     Си.                                                                        
                                                                                
          Это короткий пример программы демонстрирует вывод текстового          
     файла (имя которого вводится в командной строке) на экран.                 
                                                                                
               Турбо Паскаль                 Турбо Си                           
     ----------------------------------------------------------------           
                                                                                
     program DumpIt;                       # include                   
                                                                                
     var                                   main(int argc,char *argv[])          
        F  :Text;                          {                                    
        Ch :Char;                            FILE *f;                           
     begin                                   int  ch;                           
        Assign(F,ParamStr(1));                                                  
        {SI-}Reset(F);{SI+}                  f=fopen(argv[1],"r");              
        if IOResult<>0 then begin              if (f==NULL){                    
         Writeln ('Cannot open ',ParamStr(1));   printf("Cannot                 
                                                  open %s\n",argv[1]);          
         Halt(1);                                return(1);                     
        end;                                     }                              
        While not EOF(F) do begin                                               
           Read(F,Ch);                        while((ch=getc(f))!=DCF)          
           Write (Ch)                            putchar(ch);                   
        end;                                                                    
        Close (F)                             fclose(f);                        
      end.                                  }                                   
                                                                                
     -----------------------------------------------------------------          
                                                                                
                                                                                
                                                                                

                         - 169,170 -
                                                                                
               Общие ошибки Паскаль программистов при работе на Си.             
     -----------------------------------------------------------------          
                                                                                
          Значительная схожесть  языков  Паскаль и Си делает некоторые          
     ошибки очень распространенными.  Далее приведем наиболее типичные          
     ошибки, которые вам необходимо избежать. Эти ошибки Паскаль прог-          
     раммисту очень трудно заметить,  а компилятор их может не обнару-          
     жить. (Некоторые общие ошибки также рассматриваются в главе 7.)            
                                                                                
                                                                                
                                                                                
                                                                                
               Ошибка #1: Присваивание и сравнение.                             
     -----------------------------------------------------------------          
                                                                                
          В Паскале A  =  B  -  булево  выражение  A  равно B, которое          
     возвращает значение true или false. В Си  A = B - присваивание: A          
     получает значение  B;  однако (и это существенно  для понимания),          
     это  выражение также возвращает  значение равное B  (которое было          
     присвоено A). Для Паскаль программиста характерен такой оператор:          
                                                                                
          if (A=B) <оператор>;                                                  
                                                                                
                                                                                
          Этот оператор разрешен в Си и выполнится так:                         
                                                                                
          # значение B присвоится A,                                            
                                                                                
          # выражение A = B возвратит значение B,                               
                                                                                
          # если это значение ненулевое (в Си это true), <оператор>             
            будет выполнен.                                                     
                                                                                
          То, что вы реально хотели написать, будет                             
                                                                                
          if (A==B) <оператор>;                                                 
                                                                                
          который сделает то,  что вам надо: если A и B равны, <опера-          
     тор> выполнится.  Запомните:  в Си сравнение на равенство это два          
     знака равенства (==), а не один (=). Один знак равенства в Си это          
     операция присвоения.                                                       
                                                                                
                                                                                
                                                                                
                                                                                

                         - 171,172 -
                                                                                
              Ошибка #2: Забывание о передаче адреса                            
              (особенно, при использовании scanf).                              
     -----------------------------------------------------------------          
                                                                                
          Как мы отмечали, Си позволяет вам передавать параметры функ-          
     ции только по значению; если вы хотите передать параметр по адре-          
     су, вам  необходимо непосредственно указать этот адрес.  Допустим          
     вы используете  функцию swap,  которая была описана в этой главе.          
     Вы можете допустить ошибку при ее вызове так:  swap(q,r), где q и          
     r имеют тип float. В этом случае swap возьмет значения q и r, ин-          
     терпретирует их как адрес, а затем поменяет местами значения, на-          
     ходящиеся по этим адресам.                                                 
                                                                                
          Как избежать  такой  ошибки?  Лучший  путь это использование          
     прототипов функций. При этом Турбо Си сделает соответсвующую про-          
     верку при компиляции.  Для swap вы можете ввести следующий прото-          
     тип где-нибудь возле начала вашего исходного файла:                        
                                                                                
          void swap (float *x, float *y);                                       
                                                                                
          Теперь, если вы прокомпилируете  вашу программу с оператором          
     swap(q,r),  вы получите ошибку, сообщающую, что вы имеете несоот-          
                                                                                
     ветствие типов при вызове swap.                                            
                                                                                
                                                                                
                                                                                
               Ошибка #3: пропуск скобок при вызове функции.                    
     -----------------------------------------------------------------          
                                                                                
          В Паскале,  процедура,  не имеющая  параметров,   вызывается          
     просто по имени:                                                           
                                                                                
          AnyProcedure;                                                         
          i := AnyFunction;                                                     
                                                                                
          В Си,  название функции,  если даже нет параметров,   должно          
     всегда содержать открытые и закрытые скобки. Легко ошибиться так:          
                                                                                
          AnyFunction;                   /*Нет никакого действия*/              
          i = AnyFunction;    /*Присвоение i адреса AnyFunction */              
                                                                                
          когда вы действительно хотели:                                        
                                                                                
          AnyFunction();                   /*Вызвать AnyFunction*/              

                         - 173,174 -
                                                                                
          i = AnyFunction();  /*Вызвать AnyFunction, присвоить                  
                                                    результат i */              
                                                                                
                                                                                
                                                                                
               Ошибка #4: предупреждающие сообщения.                            
     -----------------------------------------------------------------          
                                                                                
          В добавление к основным ошибочным сообщениям, Турбо Си также          
     выдает предостережения  о не фатальных ошибках.  Используя некор-          
     ректную функцию вызова из предшествующего примера, Турбо Си может          
     выдать следующие предостережения:                                          
                                                                                
          Warning test.c 5: Code has no effect in function main                 
                            (код не исполняется в основной функции)             
          Warning test.c 6: Non-portable pointer assigment in                   
                                                   function main                
                    (в основной функции присваивается не мобильный              
                                                   указатель)                   
                                                                                
          Оба оператора действительно  законны,  и  так  как ошибки не          
     имели место, может быть создан файл .OBJ. Остерегайтесь! Эти типы          
                                                                                
     предупреждений будут всегда при фатальных  ошибках. Не относитесь          
     к предостережениям легкомысленно.                                          
                                                                                
                                                                                
                                                                                
               Ошибка #5: индексация в многомерных массивах.                    
     -----------------------------------------------------------------          
                                                                                
          Предположим, вы имеете двумерный массив, названный matrix, и          
     хотите сослаться на ячейку памяти (i,j).  Как программист на Пас-          
     кале, вы склоны записать это следующим образом:                            
                                                                                
          x = matrix [i,j];                                                     
                                                                                
          Это вполне возможно, однако не делайте так.                           
                                                                                
          В Си  это  означает серию выражений,  отделенных запятыми; в          
     этом случае,  полное выражение берет величину из последнего выра-          
     жения, так предшествующий оператор является эквивалентом                   
                                                                                
          x = matrix[j];                                                        
                                                                                

                         - 175,176 -
                                                                                
                                                                                
          Это не то,  что вы хотели, но это законченный оператор в Си.          
     Все, что вы получите, это предостережение, так как Си думает, что          
     вы пробуете присвоить x адрес matrix [j], то есть j-той строки. В          
     Си вы должны явно индексировать каждый массив. Так вам надо напи-          
     сать                                                                       
                                                                                
          x = matrix[i] [j];                                                    
                                                                                
          Памятка. Для  многомерного массива каждый индекс заключается          
     в отдельные скобки.                                                        
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
               Ошибка #6: Забывание о различиях между символьными               
                          массивами и символьными указателями.                  
     -----------------------------------------------------------------          
                                                                                
          Предположим, что мы имеем следующие операторы:                        
                                                                                
          сhar  *str1, str2[30];                                                
          str1 = "This is test";                                                
          str2 = "This is another test";                                        
                                                                                
          Первое присвоение доступно,  второе - нет. Почему? str1 ука-          
     зывает на строку, и когда транслятор видит этот оператор присвое-          
     ния, он  создает строку "This is test" где-нибудь в вашем объект-          
     ном файле и присваивает адрес str1.                                        
                                                                                
          Напротив, str2 - постоянный указатель блока из 30 байт;вы не          
     можете изменить адрес, который он содержит. То, что вы хотите на-          
     писать, будет:                                                             
                                                                                
          strcpy (str2, "This is another test");                                
                                                                                
          В этом варианте из постоянной строки  "This is another test"          

                         - 177,178 -
                                                                                
                                                                                
     байт за байтом копируется  в область, адресуемую str2.                     
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
               Ошибка #7: забывание о том, что Си чувствителен                  
                          к размеру букв (строчные-заглавные).                  
     -----------------------------------------------------------------          
                                                                                
          В  Паскале идентификаторы  indx,  Indx,  INDX все одинаковы;          
     заглавные и строчные буквы  являются тождественными.  В Си - нет.          
     Если вы напишите:                                                          
                                                                                
          int       Indx;                                                       
                                                                                
     а потом:                                                                   
                                                                                
          for (indx=1; indx<10; indx++) <оператор>;                             
                                                                                
          Транслятор выдаст ошибку, говорящую о том, что он не узнал            
     indx.                                                                      
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 179,180 -
                                                                                
               Ошибка #8: пропуск точки с запятой в последнем                   
                          операторе блока.                                      
     -----------------------------------------------------------------          
                                                                                
          Если вы программист на Паскале, который ставит точки с запя-          
     той там,  где они требуются (как противоположность тому,  где они          
     допускаются), вы  будете иметь некоторые  проблемы.   К  счастью,          
     транслятор поймает  это и отметит довольно ясно.  Необходимо пом-          
     нить, что  каждый оператор Си,  кроме двух случаев,  должен иметь          
     точку с запятой.                                                           
                                                                                
          Первый исключительный случай - функциональный оператор:               
                                                                                
           FuncName(<имена параметров>)                                   
                                                                                
          который не  имеет точку с запятой после  себя.  Не путайте с          
     прототипом функции:                                                        
                                                                                
           FuncName(, , ...);                   
                                                                                
     который используется для  указания функции,  но не определяет ее,          
     это подобно предварительному описанию в Паскале.                           
                                                                                
                                                                                
                                                                                
          Другой исключительный случай - это набор препроцессорных ко-          
     манд (#<команда>), подобно как:                                            
                                                                                
          #include                                                     
          #define  LMAX  100                                                    
                                                                                
          Если вы забудете и введете  #define LMAX 100;, тогда препро-          
     цессор подставит 100; везде где встретится LMAX, (точка с запятой          
     и все).                                                                    
                                                                                
          Запомните -  Си требует внимательности от программистов.  Вы          
     должны быть более точными, чем при программировании на языке Пас-          
     каль.                                                                      
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 181,182 -
                                                                                
                                                                                
                             Г Л А В А  10                                      
                             --------------                                     
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
            ИНТЕРФЕЙС МЕЖДУ ТУРБО СИ И ТУРБО ПРОЛОГОМ.                          
     -----------------------------------------------------------------          
                                                                                
          Теперь, получив знания по Турбо Си, вы можете объединить два          
     очень мощных языка,  реализованных в настоящий момент на PC. Ком-          
     понуя  модули Турбо Си с модулями Турбо Пролога,  вы можете,  тем          
     самым, "подключать" искусственный интеллект (ИИ) к своим приклад-          
     ным программам,  написанным на Турбо Си. Если вы опытный програм-          
     мист, работающий на Си, то должны были отметить несколько преиму-          
     ществ  Турбо  Си перед другими реализациями языка Си.  Если же вы          
     только изучаете Си,  то сейчас самый подходящий момент для  того,          
     чтобы посмотреть, как Турбо Си и Турбо Пролог дополняют друг дру-          
     га.                                                                        
                                                                                
          Турбо Си - процедурный язык, а Турбо Пролог - язык, базирую-          
     щийся на логическом программировании. Связывание ваших прикладных          
     программ,  написанных на Турбо Си и на Турбо Прологе,  может дать          
     следующие преимущества интеллектуальной технологии:                        
                                                                                
          - продукционно-ориентированные управляющие структуры;                 
                                                                                
          - интегрированную оболочку на базе естественного языка.               

                         - 183,184 -
                                                                                
                                                                                
                                                                                
          Компоновка с  Турбо Прологом позволяет включать в прикладные          
     программы,  написанные на Турбо Си, мощь искусственного интеллек-          
     та,  так что вы можете решать перспективные проблемы, просто опи-          
     сывая их и запуская в работу логический механизм  Турбо  Пролога.          
     Для  многих прикладных программ на Турбо Си компоновка с програм-          
     мами Турбо Пролога будет значительно сокращать  время  разработки          
     программного  обеспечения,  увеличивать  прозрачность  и гибкость          
     программ.                                                                  
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
               В этой главе...                                                  
     -----------------------------------------------------------------          
                                                                                
          В этой главе мы объясним этапы компиляции и компоновки Турбо          
     Си и Турбо Пролог программ и приведем четыре примера,  демонстри-          
     рующие этот процесс.  Первый пример - простейшая  программа,  де-          
     монстрирующая компиляцию и компоновку. Второй пример идет немного          
     дальше, он покажет, как компоновать программы, используя дополни-          
     тельные библиотеки Си.  Третий пример демонстрирует распределение          
     памяти.  Последний пример описывает практическую программу работы          
     с  графикой и демонстрирует некоторые мощные возможности, которые          
     вы получите от комбинирования двух языков.                                 
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 185,186 -
                                                                                
               Компоновка Турбо Си и Турбо Пролога: обзор                       
     -----------------------------------------------------------------          
                                                                                
          Компиляция и компоновка ваших модулей,  написанных на  Турбо          
     Си, с  модулями и программами Турбо Пролога выполняется напрямую.          
     Вам только необходимо запомнить следующие пункты:                          
                                                                                
          Компиляция ваших программных модулей:                                 
                                                                                
       # Ваши Си функции, вызываемые Турбо Прологом, должны иметь суф-          
         фикс _0 (см.  первый пример программы на Си,  CSUM.C,  в этой          
         главе) , если вы не используете в Турбо Прологе расширение as          
         "".                                                          
                                                                                
       # Ваш главный модуль,  написанный на Турбо Прологе, т.е. модуль          
         с разделом goal, заменяет ваш главный модуль на Си.                    
                                                                                
       # Главный модуль Турбо Пролога должен содержать ваши  Си  функ-          
         ции,  описанные как глобальные предикаты. (Смотри первый при-          
         мер Пролог программы PROSUM.PRO в этой главе).                         
                                                                                
       # Все программные модули должны компилироваться в большой моде-          
                                                                                
         ли памяти (которая является единственным размером памяти, ис-          
         пользуемым при компиляции Турбо Пролога).                              
                                                                                
       # Если ваша программа вызывает библиотеки Турбо  Пролога версий          
         1.1  - 2.0 ,  то вы должны компилировать модули в выключенном          
         режиме размещения переменных в регистрах (-r-).                        
                                                                                
       # Генерация подчеркивания должна быть отключена (-u-).                   
                                                                                
                                                                                
          Компоновка ваших программных модулей:                                 
                                                                                
       # INIT.OBJ  должен  быть  первым  объектным  файлом компоновки.          
         (Этот модуль инициализации Турбо Пролога находится  на  диске          
         библиотек Турбо Пролога.)                                              
                                                                                
       # Если  вам  необходимы подпрограммы из библиотек Турбо Си,  то          
         используйте CL.LIB,  а если вы используете арифметику с дейс-          
         твительными  числами  (real  arithmetic),  то еще - EMU.LIB и          
         MATHL.LIB.                                                             
                                                                                
          Командная строка компоновщика должна иметь вид:                       

                         - 187,188 -
                                                                                
                                                                                
     tlink init     Другие_файлы   , [exe_файл], [ваши_библиотеки]  prolog  [emulibmathl] cl          
                                                                                
     (она должна быть набрана в одной командной строке)                         
                                                                                
          В дополнение к предыдущим правилам вам нужно запомнить                
     следующее:                                                                 
                                                                                
       # Функции Турбо Пролога могут вызывать функции,  написанные  на          
         Турбо Си,  по аналогии со встроенными в Турбо Пролог предика-          
         тами (функциями).                                                      
                                                                                
       # Все вызовы функций из библиотеки Турбо Си должны  иметь  пре-          
         фикс - нижнее подчеркивание (_). Замечание. Все имена библио-          
         течных функций Турбо Си содержат префикс  нижнее  подчеркива-          
         ние. Поэтому,  поскольку генерация нижнего подчеркивания вык-          
         лючена,  то к вызову библиотечных функций должен явно  добав-          
         ляться символ нижнего подчеркивания.  Определяемые же пользо-          
         вателем функции не нуждаются в этом символе.                           
                                                                                
       # malloc,  calloc,  free и другие функции распределения  памяти          
                                                                                
         Турбо  Си заменяются в Турбо Прологе на alloc_gstack, _malloc          
         и _free.  Эти функции доступны в Турбо Прологе и  служат  для          
         размещения памяти внутри ваших Турбо Си функций.                       
                                                                                
          Функция alloc_gstack  распределяет память в глобальном стеке          
          и вызывается так:                                                     
                                                                                
          void *alloc_gstack(int size).                                         
                                                                                
          _malloc распределяет память в "куче" (heap) Пролога и  вызы-          
          вается так:                                                           
                                                                                
          void *_malloc (int size).                                             
                                                                                
          _free освобождает память,  распределеную в "куче" Пролога, и          
          вызывается так:                                                       
                                                                                
          _free(void *ptr, int size).                                           
                                                                                
          Когда используется alloc_gstack,  память при неудачной отра-          
          ботке освобождается автоматически, что заставляет Турбо Про-          
          лог производить откат через операцию размещения памяти.               

                         - 189,190 -
                                                                                
                                                                                
        # printf, putc и соответствующие им функции вывода на экран не          
          работают  после компоновки Турбо Си и Турбо Пролога.  Тем не          
          менее, писать символы в окна Пролога может wrch, а zwf явля-          
          ется  аналогом  предиката  Турбо  Пролога writef.  zwf - это          
          printf с некоторыми ограничениями.                                    
                                                                                
          zwf(FormatString, аргумент 1, аргумент 2, аргумент 3, ...)            
                                                                                
          FormatString - строка формата,  аналогичная  используемой  в          
          printf.  Посмотрите в справочном руководстве по Турбо Проло-          
          гу, какие спецификации преобразования поддерживаются.                 
                                                                                
          zwf и wrch находятся в PROLOG.LIB.                                    
                                                                                
        # Функции Си,  вызываемые Турбо Прологом, не должны возвращать          
          значений и должны быть определены как void.  Шаблон на аргу-          
          менты должен быть специфицирован  в  глобальных  объявлениях          
          предикатов Турбо Пролога. Так:                                        
                                                                                
          factorial(integer,real) - (i,o) language c                            
                                                                                
                                                                                
                                                                                
          позволяет определить  Турбо  Прологу,  что  factorial - есть          
          функция от двух аргументов: первый - integer (целый), второй          
          -  real  (с плавающей точкой).  Конструкция (i,o) указывает,          
          что первый аргумент (integer) является входным,  а второй  -          
          указатель на число с плавающей точкой,  которому будет прис-          
          воено значение факториала. Буква с в этой конструкции указы-          
          вает  Турбо  Прологу,  что эта функция использует соглашения          
          вызывов Си (смотри третий пример  программы  в  этой  главе:          
          DUBLIST.C и PLIST.PRO).                                               
                                                                                
               Заметьте, что  значение  возвращается в виде указателя.          
          Для большей информации по  описанию  шаблонов  на  аргументы          
          смотрите обсуждение его альтернативы в примере 3.                     
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 191,192 -
                                                                                
          Пример 1: Сложение двух целых чисел                                   
     -----------------------------------------------------------------          
                                                                                
          Следующий пример комбинирует функцию Турбо Си (которая скла-          
     дывает два целых числа) с модулем на Турбо Прологе, который поме-          
     щает результат, вычисленный функцией Си, в текущее окно.                   
                                                                                
                                                                                
               Исходный файл Турбо Си: CSUM.C                                   
     -----------------------------------------------------------------          
     /*                                                                         
     подпрограмма вывода zwf работает подобно подпрограмме printf язы-          
     ка Си. Она печатает результат в текущем окне                               
     */                                                                         
     extern void zwf(char *format, ...);                                        
     void sum_0(int parm1,int parm2, int *res_p)                                
     {                                                                          
     zwf("Это функция суммирования: parm1=%d, parm2=%d, parm1,parm2);           
     *res_p=parm1+parm2;                                                        
                                                                                
     }                                                                          
     /* конец sum_0 */                                                          
                                                                                
               Компиляция  CSUM.C  в  CSUM.OBJ                                  
     -----------------------------------------------------------------          
                                                                                
          После редактирования и сохранения на диске файла CSUM.C, вам          
     необходимо выбрать опции для его компиляции.  Турбо Си для  этого          
     предоставляет вам два метода:                                              
                                                                                
          1. Выберете  опции  для  компиляции  из меню Интегрированной          
     среды Турбо Си:                                                            
                                                                                
     Options/Compiler/Model/Large (-ml)                                         
     Options/Compiler/Optimization/Jump Optimization...On (-O)                  
     Options/Compiler/Code Generation/Generate Underbars...Off (-u-)            
     Options/Compiler/Optimization/Use Register Variables...Off (-r-)           
                                                                                
          Сразу после  выбора  опций  выберите в главном меню Турбо Си          
     опцию Options/Store options - Опции/Сохранить опции, когда же на-          
     чальная  установка параметров будет сохранена,  выберите Compile/          
     Compile to OBJ.  Турбо Си скомпилирует CSUM.C в выбранных режимах          
     в объектный модуль CSUM.OBJ.                                               
                                                                                
          2. Если  вы предпочтете компилировать CSUM.C командной стро-          

                         - 193,194 -
                                                                                
                                                                                
     кой по стандарту DOS вместо использования меню Турбо Си, то после          
     приглашения операционной системы, введите                                  
                                                                                
          tcc -ml -O -c -u- -r- csum                                            
                                                                                
          Замечание: Турбо Пролог компилируется только в большой моде-          
     ли памяти,  поэтому для обеспечения связи между Турбо Си и  Турбо          
     Прологом вы должны использовать при компиляции опцию -ml.                  
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
               Исходный файл Турбо Пролога : PROSUM.PRO                         
     -----------------------------------------------------------------          
                                                                                
     global predicates                                                          
       sum(integer,integer,integer) - (i,i,o) language c                        
                           /* описание аргументов функции суммирования          
                           - sum определено как (i,i,o) и специфициру-          
                           ет,  что третий аргумент возвращает  значе-          
                           ние, а первые два принимают). */                     
       goal  sum(7,6,X),write("Sum=",X).                                        
                                                                                
                                                                                
                                                                                
               Компиляция PROSUM.PRO в PROSUM.OBJ                               
     -----------------------------------------------------------------          
                                                                                
          После редактирования  и сохранения PROSUM.PRO вам необходимо          
     откомпилировать его в обьектный  (.OBJ)  файл,  который  позволит          
     скомпоновать его с обьектным модулем Турбо Си. Чтобы сделать это,          
     выберите опцию Compile из главного меню Турбо Пролога,  а затем -          
     OBJ File . Когда Турбо Пролог завершит компиляцию исходного файла          
     в обьектный файл,  вы можете выполнить компоновку и запуск вашего          

                         - 195,196 -
                                                                                
                                                                                
     примера.                                                                   
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
               Компоновка CSUM.OBJ и PROSUM.OBJ                                 
     -----------------------------------------------------------------          
                                                                                
          Для компоновки модулей Турбо Пролога с модулями Турбо Си, вы          
     можете использовать или интегрированную среду Турбо Си,  или ком-          
     поновщик TLINK (автономный компоновщик,  включенный в  ваш  пакет          
     Турбо Си).  За командой tlink следуют аргументы командной строки:          
     главные модули Турбо Пролога, набор других модулей, выходные фай-          
     лы и библиотеки. Кроме специально оговоренных случаев, они должны          
     следовать в следующем порядке:                                             
                                                                                
                                                                                
     Инициализация Турбо Пролога:                                               
     ---------------------------                                                
                                                                                
          # INT.OBJ (модуль инициализации Турбо Пролога)                        
                                                                                
     Главный модуль Турбо Пролога:                                              
     ----------------------------                                               
                                                                                
          # главный модуль Турбо Пролога, который содержит раздел gоal          
                                                                                

                         - 197,198 -
                                                                                
     Набор модулей:                                                             
     -------------                                                              
                                                                                
          (эти модули не обязательно выстраивать в какой-то определен-          
     ный порядок)                                                               
                                                                                
          # ассемблеровские .OBJ модули                                         
          # Турбо Си        .OBJ модули                                         
          # Турбо Пролог    .OBJ модули                                         
                                                                                
     Модуль таблицы идентификаторов:                                            
     ------------------------------                                             
                                                                                
          # Имя основной таблицы идентификаторов Пролога  (это  обяза-          
     тельный параметр, он должен завершать список модулей)                      
                                                                                
     Имя выходного файла                                                        
     -------------------                                                        
                                                                                
          # имя выполняемого файла, который будет создан                        
                                                                                
                                                                                
                                                                                
     Библиотеки:                                                                
     ----------                                                                 
                                                                                
          # перечисляются все библиотеки,  содержащие функции, необхо-          
     димые для компонуемых модулей.  Последовательность важна: первыми          
     идут библиотеки,  определяемые пользователями,  затем PROLOG.LIB,          
     далее, если необходимо, EMU.LIB и MATHL.LIB, и наконец, CL.LIB.            
                                                                                
          В этом примере мы использовали  программу  компоновки  Turbo          
     Link  (обозначаемую как tlink) и передали ей следующие аргументы:          
                                                                                
          # Программы Турбо Пролога, INIT.OBJ и PROSUM.OBJ                      
          # Объектный модуль Турбо Си CSUM.OBJ                                  
          # Таблицу  идентификаторов  PROSUM.SYM  и  выполняемый  файл          
            TEST.EXE                                                            
          # библиотеки PROLOG.LIB, CL.LIB (для работы с плавающей точ-          
            кой используйте EMU.LIB и MATHL.LIB)                                
                                                                                
          Замечание: PROSUM.SYM является  файлом,  содержащим  таблицу          
     имен и типов переменных, имеющихся в программе PROSUM.OBJ.                 
                                                                                
          Ниже приведена командная строка для компоновки нашего перво-          

                         - 199,200 -
                                                                                
                                                                                
     го примера:                                                                
                                                                                
     tlink init prosum csum prosum.sym, test.exe,,prolog+cl                     
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Пример 2: Использование библиотеки математических функций             
     -----------------------------------------------------------------          
                                                                                
          Второй пример похож на первый, он демонстрирует, как записы-          
     ваются две функции на Турбо Си и как они комбинируются с програм-          
     мой на Турбо Прологе. Мы оформляем каждую из используемых функций          
     Турбо Си в виде отдельного исходного  файла;  CSUM1.C  складывает          
     два действительных числа и возвращает сумму, а FACTRL.C вычисляет          
     факториал целого числа.  Программа на Турбо  Прологе  FACTSUM.PRO          
     записывает результаты программы в два окна Пролога. Данный пример          
     использует библиотеку математических функций большой модели памя-          
     ти - MATHL.LIB.                                                            
                                                                                
                                                                                
          И с х о д н ы й  ф а й л Турбо Си: CSUM1.C                            
                                                                                
     extern void zwf(char *format, ...);                                        
     void sum_0(double parml, double parm2, double *res_p)                      
     {                                                                          
       *res_p=parm1+parm2;                                                      
       zwf("Это функция суммирования: parm1=%f, parm2=%f, result=%f",           
                         parm1,parm2,*res_p);                                   

                         - 201,202 -
                                                                                
     }                                                                          
                                                                                
          И с х о д н ы й  ф а й л Турбо Си: FACTRL.C                           
                                                                                
     void factorial_0(int top, double *result) /* Вычисляет                     
                                                      факториал */              
     {                                                                          
         double x;                                                              
         int i;                                                                 
         if (top<1) {                                                           
            *result = 0.0;                                                      
            return;                                                             
         }                                                                      
         *result = 1.0;                                                         
         x = 2.0;                                                               
         while (top-- >1)                                                       
         {                                                                      
           *result = *result*x;                                                 
           x++;                                                                 
         }                                                                      
     }                                                                          
     /* Конец функции factorial_0 */                                            
                                                                                
               Компиляция CSUM1.C и FACTRL.C в .OBJ                             
     -----------------------------------------------------------------          
                                                                                
          Как и в первом примере, вы должны откомпилировать два модуля          
     на  Турбо  Си  в  объектный модуль (.OBJ ) перед компоновкой их с          
     другими модулями и с главной программой Турбо Пролога.  Вы можете          
     выбрать и сохранить опции для компиляции средствами главного меню          
     Турбо Си,  а затем выбрать команду  Compile  (компилировать)  для          
     каждого  из  .С исходных файлов.  Или вы можете указать опцию для          
     компиляции обоих исходных файлов .С в  командной  строке,  как  в          
     стандартном Си,  используя для этого команду tcc. В обоих вариан-          
     тах вы должны указать по крайней мере следующие опции компиляции:          
                                                                                
      Options/Compiler/Model/Large (-ml)     (большая модель памяти)            
      Options/Compiler/Optimization/Jump Optimizanion...On (-O)                 
                                                       (оптимизация)            
      Options/Compiler/Code Generation/Generate Underbars...Off (-u-)           
                          (генерация нижнего подчеркивания отключена)           
      Options/Compiler/Optimization/Use Register Variables...Off (-r-)          
                     (использование регистровых переменных запрещено)           
                                                                                
                                                                                

                         - 203,204 -
                                                                                
          И с х о д н ы й  ф а й л Турбо Пролога: FACTSUM.PRO                   
     -----------------------------------------------------------------          
                                                                                
          FACTSUM.PRO - главная программа на  Турбо  Прологе,  которая          
     создает  два  окна:  одно для отображения выходных данных модулей          
     Турбо Си, другое для отображения выходных данных программы, напи-          
     санной  на Турбо Прологе.  Ниже приводится работа модулей и прог-          
     раммы в диалоге:                                                           
                                                                                
          1. Турбо Пролог программа FACTSUM.PRO приглашает пользовате-          
     ля ввести целое Int, которое она передаст модулю FACTRL.C .                
                                                                                
          2. Функция факториал (factorial) Турбо Си в FACTRL.C возвра-          
     щает Result, который является факториалом Int, в FACTSUM.PRO.              
                                                                                
          3. FACTSUM.PRO записывает Result в окно и  снова  приглашает          
     пользователя ввести число (на этот раз - действительное).                  
                                                                                
          4. FACTSOM.PRO передает введеное во второй раз число (Real),          
     и ранее вычисленный факториал (Result) в модуль CSUM1.C.                   
                                                                                
          5. Функция суммирования (sum),  в CSUM1.C, складывает Real и          
                                                                                
     Result и возвращает результат Sum в FACTSUM.PRO.                           
                                                                                
          6. FACTSUM.PRO  записывает  Sum в окно и программа завершает          
     свою работу.                                                               
                                                                                
          Ниже представлена программа на Турбо Прологе FACTSUM.PRO:             
     /*                                                                         
     Описание модулей на Турбо Си должно находиться  за  определениями          
     доменов  и  баз данных Турбо Пролога (если таковые имеются).  Все          
     глобальные модули вызываются из Турбо Пролога как глобальные пре-          
     дикаты, за которыми при их описании должны следовать шаблон аргу-          
     ментов и спецификация языка.                                               
     */                                                                         
                                                                                
     global predicates                                                          
        sum(real,real,real) - (i,i,o) language c                                
        factorial(integer,real) - (i,o) language c                              
     /*                                                                         
     Это очень  простой  пример,  поскольку  использует только внешние          
     правила (модули Турбо Си),  поэтому требуется наличие только раз-          
     дела  goal .  Тем не менее,  в других прикладных программах может          
     быть также необходим раздел clause. */                                     

                         - 205,206 -
                                                                                
     goal                                                                       
        makewindow(1,49,31,                                                     
        "Окно взаимодействия Турбо Пролога с программами Турбо Си",             
                                                           0,0,15,80),          
        makewindow(2,47,3,                                                      
        "Окно Турбо Пролога для Турбо Пролог программы",                        
                                                          15,0,10,80),          
        /* Приглашение пользователя к первому вводу */                          
        write("Введите целое число; Турбо Си вычислит факториал."),             
        readint(Int), nl,                                                       
        shiftwindow(1), /* Переадресация окна вывода в окно Турбо               
                                                                 Си */          
        /* Вызов модуля Турбо Си factrl и вычисление факториала */              
        factorial(Int, Result),                                                 
        shiftwindow(2), /* Переадресация окна вывода в окно Турбо               
                                                            Пролога */          
        /* Приглашение пользователя ко второму вводу */                         
        write("Введите действительное число для сложения с                      
                                                        факториалом"),          
        readreal(Real),nl,                                                      
        shiftwindow(1), /* Переадресация окна вывода в окно Турбо               
                                                                Си */           
                                                                                
                                                                                
        /* Вызов модуля Турбо Си сsum1 и вычисление суммы */                    
        sum(Result,Real,Sum),                                                   
        shiftwindow(2), /* Переадресация вывода в окно Турбо                    
                                                      Пролога */                
        /* Запись результата первого вычисления в окно */                       
        write("Значение факториала числа ",Int," равно ",Result),nl,            
                                                                                
        /* Запись результата второго вычисления в окно */                       
        write("Результат ", Result, " + ",Real," = ",Sum),nl.                   
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 207,208 -
                                                                                
          Компиляция FACTSUM.PRO в FACTSUM.OBJ                                  
     -----------------------------------------------------------------          
                                                                                
          Также как  и в первом примере вы должны откомпилировать файл          
     с исходным текстом на Турбо Прологе в обьектный файл (.OBJ) перед          
     компоновкой его с другими модулями.  Перед тем как выполнить ком-          
     пиляцию, выберите из главного меню Турбо Пролога Оptions/Obj.              
                                                                                
                                                                                
          Компоновка СSUM1.OBJ, FACTRL.OBJ и FACTSUM.OBJ                        
     -----------------------------------------------------------------          
                                                                                
          В команде на компоновку, используемой в этом примере,                 
                                                                                
          - объектными модулями Турбо Пролога являются модули INIT.OBJ          
     и FACTSUM.OBJ;                                                             
          - объектными модулями Турбо Си являются модули  CSUM1.OBJ  и          
     FACTRL.OBJ;                                                                
          - именами  выходных  файлов  являются  FACTSUM.SYM  (таблица          
     идентификаторов) и SUM.EXE (выполняемый файл);                             
          - необходимыми библиотеками  являются  PROLOG.LIB,  EMU.LIB,          
     MATHL.LIB и CL.LIB.                                                        
                                                                                
                                                                                
                                                                                
          Приведенная ниже команда компонует модули:                            
                                                                                
     tlink  init factsum  factrl csum1 factsum.sym,sum,,prolog                  
     +emu+mathl+cl                                                              
                                                                                
          Замечание: команда должна быть набрана в одной строке.                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 209,210 -
                                                                                
               Пример 3: Шаблоны аргументов и распределение памяти.             
     -----------------------------------------------------------------          
                                                                                
          Следующая программа  представляет  код  по созданию на Турбо          
     Прологе функтора (functor) и списка (list) в Турбо  Си,  а  также          
     возвращению этих новых структур в Турбо Пролог. Этот пример также          
     демонстрирует возможное распределение памяти в  глобальном  стеке          
     Турбо Пролога. Списки представляют собой рекурсивные структуры из          
     трех элементов, а функторы - структуры языка Си из двух элементов          
     (они описаны более полно после этого примера).                             
                                                                                
          - Модуль  на Турбо Си DUBLIST.C содержит три функции. Первые          
     две могут брать список,  состоящий из целых чисел,  и  возвращать          
     структуру  с целым элементом,  являющимся первым числом (из спис-          
     ка),  или же брать структуру, в которой есть целое число, и возв-          
     ращать список с этим числом. Третья функция берет целое число n и          
     создает список из двух целых чисел, первым элементом которого яв-          
     ляется само число n, а вторым - число 2n.                                  
                                                                                
          - Необходимо отметить, что для каждого глобального предиката          
     Турбо Пролога  возможно  использование  альтернативных  шаблонов,          
     описывающих аргументы,  и что каждый такой шаблон требует альтер-          
                                                                                
     нативной функции Турбо Си. Для следующего примера функция clist_0          
     должна соответствовать первому шаблону описания аргументов (i,о),          
     а функция clist_1 - второму шаблону (о,i).                                 
                                                                                
          global predicates                                                     
              clist(ilist,ifunc) - (i,o) (o,i) language c                       
                                                                                
         - Спецификация (i,o) означает, что ilist будет передана в ва-          
     шу функцию Турбо Си clist_0,  а ifunk - указатель  на  структуру,          
     который будет определен внутри функции Турбо Си clist_0. Специфи-          
     кация (o,i) означает, что ifunс будет передана в clist_1, а ilist          
     -  указатель  на  списковую  структуру,  которая будет определена          
     внутри clist_1.                                                            
                                                                                
          - Если дополнительный шаблон был специфицирован в глобальном          
     домене вашей Турбо Пролог программы, то функция clist_2 вынуждена          
     будет обрабатывать этот дополнительный шаблон.                             
                                                                                
          И с х о д н ы й  ф а й л  Турбо Си: DUBLIST.C                         
                                                                                
     void fail_cc(void);                                                        
     void *alloc_gstack(int size);                                              

                         - 211,212 -
                                                                                
                                                                                
                                                                                
     struct ilist {                                                             
         char functor;              /* тип элемента списка */                   
                                    /* 1 = элемент списка  */                   
                                    /* 2 = конец списка    */                   
         int val;                   /* фактический элемент */                   
         struct ilist *next /* указатель на следующий узел */                   
     };                                                                         
     struсt ifunc {                                                             
         char type;                  /*    тип функтора   */                    
         int value;                  /* значение функтора */                    
     };                                                                         
     void clist_0(struct ilist *in, struct ifunc **out)                         
     {                                                                          
         if (in->functor != 1) fail_cc();/*завершить, если список пуст          
                                         */                                     
         *оut = alloc_gstack(sizeof(struct ifunc));                             
         (*out)->value = in->val; /* out присваивает значение f(x)*/            
         (*out)->type = 1         /* задание типа функтора */                   
     }                                                                          
     void clist_1(struct ilist **out, struct ifunc *in)                         
                                                                                
     {                                                                          
         int temp = 0;                                                          
         struct ilist *endlist= alloc_gstack(sizeof(struct ilist));             
                                                                                
         endlist->functor = 2;                                                  
         temp = in->value;                                                      
         temp += temp;                                                          
         *out = alloc_gstack(sizeof(struct ilist));                             
         (*out)->val = temp; /* возвращается [2*X], как элемент    */           
                             /*                             списка */           
          (*out)->functor = 1;/* устанавливается тип элемента      */           
                             /* списка. Если это не выполнять,     */           
                             /* то возвращается  значение лишенное */           
                             /* смысла                             */           
         (*out)->next = endlist;                                                
     }                                                                          
     void dublist_0(int n, struct ilist **out) {                                
     /*                                                                         
         эта функция создает список [n,n+n]                                     
     */                                                                         
         struct ilist *temp;                                                    
         struct ilist *endlist= alloc_gstack(sizeof(struct ilist));             

                         - 213,214 -
                                                                                
                                                                                
         endlist->functor = 2;                                                  
         temp = alloc_gstack(sizeof(struct ilist));                             
         temp->val = n;  /* первому элементу списка присваивается               
                                                         значение n */          
         temp -> functor = 1;                                                   
         *out = temp;                                                           
       /* теперь мы должны разместить второй элемент списка */                  
         temp = alloc_gstack(sizeof(struct ilist));                             
         temp->val = n + n; /* второму элементу списка присваи-                 
                                                вается значение n+n */          
         temp->functor = 1;                                                     
         temp->next = endlist /* установка узла за вторым элементом */          
                              /* на последний узел списка           */          
         (*out)->next = temp; /* установка второго элемента после   */          
                              /* первого                            */          
     }                                                                          
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Вызов Турбо Пролога из Турбо Си                                       
     -----------------------------------------------------------------          
                                                                                
          Возможен не только вызов Турбо Прологом предикатов, написан-          
     ных на Турбо Си,  но также и вызов Турбо Си прологовских предика-          
     тов.  Если  некоторый  глобальный предикат описан в Турбо Прологе          
     как language c и существуют  предложения  (clauses)  для  данного          
     предиката,  то  Турбо Пролог создаст подпрограмму,  которую можно          
     будет вызывать из Турбо Си.                                                
                                                                                
          Следующая программа на Турбо Прологе объявляет два  глобаль-          
     ных предиката языка Си: message и hello_c. Предикат message может          
     быть вызван из модуля Си посредством использования  имени функции          
     message_0 в тексте программы на Си.                                        
                                                                                
       global predicates                                                        
          message(string) - (i) language c                                      
          hello_c - language c                                                  
                                                                                
       clauses                                                                  
          message(S)  :-                                                        
             makewindow(13,7,7,"",10,10,3,50),                                  

                         - 215,216 -
                                                                                
             write(S), readchar(_),                                             
             removewindow.                                                      
                                                                                
       goal                                                                     
          message("Привет из Турбо Пролога"),                                   
          hello_c.                                                              
                                                                                
          Раздел  goal  в  данном  примере  вызывает функцию  Турбо Си          
     hello_c, которая, в свою очередь, вызывает предикат Турбо Пролога          
     message_0 для вывода сообщения.                                            
                                                                                
        void message_0(char *str);                                              
                                                                                
        void hello_c_0(void)                                                    
        {                                                                       
           message_0("Привет из Турбо Си");                                     
        }                                                                       
                                                                                
          Вы можете использовать эту возможность для легкого доступа к          
     мощной библиотеке Турбо Пролога из других языков.                          
                                                                                
          Вы легко  можете  определить  ваши  собственные библиотечные          
                                                                                
     подпрограммы в модуле на Турбо Прологе, например так:                      
                                                                                
       project "dummy"  /* подставьте сюда имя */                               
                                                                                
       global predicates                                                        
          myfail language c as "fail"                                           
          mymakewindow(integer,integer,integer,string,integer,integer,          
                        integer,integer)                                        
                - (i,i,i,i,i,i,i,i) language c as "makewindow"                  
          myshiftwindow(integer) - (i) language c as "shiftwindow"              
          myremovewindow  language c as "removewindow"                          
          write_integer(integer) - (i) language c as "write_integer"            
          write_real(real) - (i) language c as "write_real"                     
          write_string(string) - (i) language c as "write_string"               
          myreadchar(char) - (o) language c as "readchar"                       
          myreadline(string) - (o) language c as "readline"                     
                                                                                
          extprog language c as "extprog"                                       
                                                                                
       clauses                                                                  
          myfail :- fail.                                                       
                                                                                

                         - 217,218 -
                                                                                
          mymakewindow(Wno,Wattr,Fattr,Text,Srow,Scol,Rows,Cols) :-             
             makewindow(Wno,Wattr,Fattr,Text,Srow,Scol,Rows,Cols).              
                                                                                
          myshiftwindow(WNO) :- shiftwindow(WNO).                               
                                                                                
          myremovewindow :- removewindow.                                       
                                                                                
          write_integer(I) :- write(I).                                         
                                                                                
          write_real(R) :- write(R).                                            
                                                                                
          write_string(S) :- write(S).                                          
                                                                                
          myreadchar(CH) :- readchar(CH).                                       
                                                                                
          myreadline(S) :- readln(S).                                           
                                                                                
          Следующая процедура Турбо Си, extprog, демонстрирует исполь-          
     зование этих новых библиотечных подпрограмм. extprog создает окно          
     Турбо Пролога, после чего выполнит в этом окне некоторые процеду-          
     ры ввода и вывода.                                                         
                                                                                
                                                                                
       void makewindow(int wno,int wattr,int fattr,char *title,                 
                                         int row,int srow,int scol);            
       void write_string(char *text);                                           
       void readchar(char *ch);                                                 
       void readline(char *in_str[]);                                           
       void removewindow(void);                                                 
                                                                                
       void extprog(void)                                                       
       {                                                                        
         char dummychar;                                                        
         char *Name;                                                            
                                                                                
         makewindow(1,7,7,"Hello there",5,5,15,60);                             
         write_string("\n\nIsn't it easy");                                     
         readchar(&dummychar);                                                  
         write_string("\nEnter your name: ");                                   
         readline(&Name);                                                       
         write_string("\nYour name is: ");                                      
         write_string(Name);                                                    
         readchar(&dummychar);                                                  
         removewindow();                                                        
       }                                                                        

                         - 219,220 -
                                                                                
                                                                                
                                                                                
          Единственное ограничение  при  вызове Турбо Пролога из Турбо          
     Си состоит в том,  что программа на  Турбо  Прологе  должна  быть          
     главной программой ввиду того, что Турбо Прологу необходимо уста-          
     навливать "кучу" и стеки.                                                  
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Списки и функторы                                                     
     -----------------------------------------------------------------          
                                                                                
          Турбо прологовские списки и функторы - это  структуры  Турбо          
     Си (смотри DUBLIST.C).                                                     
                                                                                
          Списки -  это рекурсивные структуры,  состоящие из трех эле-          
     ментов.  Первый элемент структуры является типом, он может прини-          
     мать  значение  1  (если  это элемент списка) и 2 (если это конец          
     списка). Второй элемент структуры - фактическое значение; он дол-          
     жен соответствовать типу элемента, определенного в программе Тур-          
     бо Пролога.  Например, список из действительных чисел может иметь          
     вид:                                                                       
                                                                                
         struct alist {                                                         
         char funct;                                                            
         double elem; /* cписок элементов типа real */                          
         struct alist *next;                                                    
         }                                                                      
                                                                                
          Третий элемент является указателем на следующий узел.                 
                                                                                

                         - 221,222 -
                                                                                
          Функторы Турбо Пролога - это структуры Си, состоящие из двух          
     элементов.  Первый элемент соответствует описанию домена на Турбо          
     Прологе. (Для получения более полной информации об описании доме-          
     нов в программах Турбо Пролога обратитесь к справочному руководс-          
     тву Турбо Пролога). Например:                                              
                                                                                
          domains                                                               
              func = i(integer); s(string)                                      
          predicates                                                            
              call(func)                                                        
          goal                                                                  
              call(X)                                                           
                                                                                
          Функтор Турбо Пролога func имеет два типа значений: первый -          
     integer, второй   - string.  Поэтому в этом примере тип элементов          
     структуры Турбо Си должен быть 1 или 2; 1  соответствует  первому          
     типу (целые числа), а тип 2 - второму типу (строковому).                   
                                                                                
          Второй элемент  структуры Турбо Си является фактическим зна-          
     чением функтора и определяется как union (объединение)  для  воз-          
     можных типов аргументов.                                                   
                                                                                
                                                                                
        union val {                                                             
          int ival;                                                             
          char *svar;                                                           
        };                                                                      
        struct func {                                                           
          char type; /* тип может быть 1 или 2 в соответствии с   */            
                     /* описанием домена в Турбо Прологе          */            
          union val value;         /* значение элемента функтора. */            
        }                                                                       
                                                                                
          Замечание: функции alloc_gstack,  _malloc и _free должны ис-          
     пользоваться  для   распределения   памяти   (они   находятся   в          
     CPINIT.OBJ). Эти функции необходимы, во-первых, для выделения па-          
     мяти структурам Турбо Си и занесения их  в  динамическую  область          
     или  стек Турбо Пролога и,  во-вторых,  для освобождения памяти в          
     динамической памяти Турбо Пролога.                                         
                                                                                
          Когда используется функция alloc_gstack,  память при  ложных          
     правилах будет освобождаться автоматически, вынуждая Турбо Пролог          
     выполнять повторное распределение памяти.  Синтаксис этих функций          
     Турбо Си приведен ниже:                                                    
                                                                                

                         - 223,224 -
                                                                                
          void *alloc_gstack(size) /* распределяет память в стеке */            
          void *_malloc(size)      /* распределяет память в "куче" */           
          _free(void * ptr,size)   /* освобождает "кучу" */                     
                                                                                
          Ниже приведен главный модуль Турбо Пролога PLIST.PRO,  кото-          
     рый вызывает функции из DUBLIST.C и печатает результаты.                   
                                                                                
        domains                                                                 
           ilist = integer*                                                     
           ifunc = f(integer)                                                   
        global predicates                                                       
           clist(ilist,ifunc) - (i,o) (o,i) language c                          
           dublist(integer,ilist) - (i,o) language c                            
        goal                                                                    
           clearwindow,                                                         
           clist([3],X),        /* связывает X с f(3) */                        
           write("X = ",X),nl,                                                  
           clist(Y,X),          /* связывает Y с [6] */                         
           write("Y = ",Y),nl,                                                  
           dublist(6,Z),        /* связывает Z с [6,12] */                      
           write(Z),nl.                                                         
                                                                                
                                                                                
                                                                                
          Компиляция DUBLIST.C                                                  
     -----------------------------------------------------------------          
                                                                                
          Также как и в первых двух примерах,  вы должны  откомпилиро-          
     вать модуль  DUBLIST.C в объектный (.OBJ)  файл перед компоновкой          
     его с главным модулем на Турбо Прологе PLIST.PRO.                          
                                                                                
          Вот команда для компоновки:                                           
                                                                                
     tlink init plist dublist plist.sym, dublist, ,                             
     prolog+emu+mathl+cl                                                        
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 225,226 -
                                                                                
                                                                                
          Пример 4. Рисование 3-х мерных диаграмм.                              
     -----------------------------------------------------------------          
                                                                                
          На этом примере мы рассмотрим,  как компилировать и компоно-          
     вать модули Си и Пролога для создания  унифицированной  программы          
     со смешением языков, комбинирующей гибкость искусственного интел-          
     лекта с графическими возможностями Си.                                     
                                                                                
          Конкретно программа включает следующее:                               
                                                                                
          - Модуль Турбо Си CBAR.C,  который рисует столбиковые  диаг-          
     раммы (гистограммы), используя входные данные из другого файла.            
                                                                                
          - Главный модуль Турбо Пролога PBAR.PRO,  который требует от          
     пользователя ввода данных.                                                 
                                                                                
          Текст программы на Си находится в файле CBAR.C на  диске  из          
     комплекта поставки.                                                        
                                                                                
                                                                                
                                                                                
                                                                                
          Компиляция CBAR.C                                                     
     -----------------------------------------------------------------          
                                                                                
          Также как и в первых трех примерах,  вы должны компилировать          
     модуль Турбо Си CBAR.C в объектный (.OBJ) файл  перед компоновкой          
     его с главным модулем на Турбо Прологе PBAR.PRO.                           
                                                                                
                                                                                
          Программа Турбо Пролога: PBAR.PRO                                     
          ---------------------------------                                     
                                                                                
          Текст программы находится в файле PBAR.PRO.                           
                                                                                
          Эта программа  на Турбо Прологе приглашает пользователя соз-          
     давать, сохранять, загружать или рисовать диаграммы.                       
                                                                                
          Если пользоваталь пожелает создавать диаграмму, то эта прог-          
     рамма  запросит у него спецификацию для ее построения,  определит          
     позицию каждого столбика в окне и вызовет модуль Си для изображе-          
     ния диаграммы. После того, как каждая полоса будет вычерчена, ди-          
     аграмма будет включена в базу данных Турбо Пролога.                        
                                                                                

                         - 227,228 -
                                                                                
                                                                                
          Пользователь может выбрать опцию сохранения  диаграммы; PBAR          
     запишет  описание  текущей  диаграммы в файл для последующего ис-          
     пользования.                                                               
                                                                                
          Если пользователь укажет режим загрузки,  то PBAR  уничтожит          
     текущее описание диаграммы и загрузит специфицированное пользова-          
     телем описание диаграммы из файла.                                         
                                                                                
          Получив последнее задание draw (рисовать),  PBAR  будет  ис-          
     пользовать описание из базы данных и осуществит рекурсивный вызов          
     модуля BAR Турбо Си,  который изобразит столбиковую  диаграмму  в          
     соответствии с ее текущим описанием в базе данных.                         
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Компиляция PBAR.PRO в PBAR.OBJ                                        
     -----------------------------------------------------------------          
                                                                                
          Как и в примере 1,  вы должны  компилировать  исходный  файл          
     главного  модуля Турбо Пролога в объектный (.OBJ) перед компонов-          
     кой его с модулями Турбо Си.                                               
                                                                                
                                                                                
          Компоновка PBAR.OBJ с модулем CBAR.OBJ                                
     -----------------------------------------------------------------          
                                                                                
          Приведенная ниже команда на компоновку связывает  PBAR.OBJ с          
     предварительно откомпилированным модулем Турбо Си CBAR.OBJ.  Ком-          
     понентами этой команды являются:                                           
                                                                                
          - объектные модули Турбо Пролога INIT.OBJ и PBAR.OBJ                  
          - объектный модуль Турбо Си CBAR.OBJ                                  
          - таблица идентификаторов PBAR.SYM и выходной файл                    
            BARCHART.EXE (выполняемый файл)                                     
          - библиотеки PROLOG.LIB и CL.LIB                                      
                                                                                
          Команда на компоновку имеет вид:                                      

                         - 229,230 -
                                                                                
                                                                                
                                                                                
     tlink init pbar cbar pbar.sym,barchart,,prolog+cl                          
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                Р е з ю м е                                                     
     -----------------------------------------------------------------          
                                                                                
          На этих четырех примерах мы показали, как компоновать модули          
     Турбо Пролога  с  вашими программами Турбо Си.  Если вы - опытный          
     программист, работающий на Турбо Прологе, и желаете узнать больше          
     о программировании  на  Си,  то мы рекомендуем вам ознакомиться с          
     главами 3 и 7 этого руководства.  Если же вы -  опытный  програм-          
     мист,  работающий на Си,  и желаете преобрести информацию о Турбо          
     Прологе,  мы рекомендуем вам учебники по Турбо Прологу, такие как          
     Using Turbo Prolog by P.Robinson (McGraw-Hill).                            
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 231,232 -
                                                                                
                             ГЛАВА 11.                                          
                                                                                
                    РУКОВОДСТВО  ПО  ЯЗЫКУ  ТУРБО  СИ                           
     -----------------------------------------------------------------          
                                                                                
          Наиболее популярным первоисточником  по  языку  Си  является          
     книга Б.Кернигана и Д.Ритчи "Язык программирования Си" (далее для          
     краткости K&R").  Данная книга не определяет полный  стандарт  Си          
     (эта задача  решена американским национальным институтом стандар-          
     тов ANSI).  В K&R, с другой стороны, дается минимальный стандарт,          
     так что  программа,  соответствующая этому стандарту,  может быть          
     откомпилирована любым транслятором Си, поддерживающим определения          
     K&R.                                                                       
                                                                                
          Турбо Си поддерживает не только определения,  данные K&R, но          
     и большинство расширений стандарта ANSI.  Поэтому,  на самом деле          
     Турбо Си - это улучшенный и расширенный язык Си с дополнением но-          
     вых возможностей,  большей мощностью и гибкостью по  отношению  к          
     стандарту. Мы не имеем возможности  перепечатать  здесь  K&R  или          
     стандарт ANSI,  вместо этого раскажем вам о расширениях определе-          
     ний K&R, отмечая какие из них соответствуют стандарту ANSI, а ка-          
     кие являются нашими собственными.                                          
                                                                                
                                                                                
                                                                                
                                                                                
      * прим. пер. - переведена на русский язык (г.Москва,                      
                     "Финансы и статистика", 1985 г.).                          
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 233,234 -
                                                                                
               В этой части...                                                  
     -----------------------------------------------------------------          
                                                                                
          Для облегчения работы с руководством мы следуем  (более  или          
     менее) содержанию  Приложения А K&R, которое называется "Справоч-          
     ное руководство по Си". Не на все разделы этого приложения приво-          
     дятся ссылки,   некоторые мы пропускаем.  Вы можете сделать вывод          
     что в них содержатся только незначительные отличия между  опреде-          
     лениями Турбо Си и K&R.  Кроме того, для большего понимания неко-          
     торых расширений ANSI и Турбо Си,  мы приводим необходимую инфор-          
     мацию в   том  же  порядке,   что  и в ANSI стандарте Си,  а не в          
     порядке, принятом в K&R.                                                   
                                                                                
                                                                                
                                                                                
               Комментарии (K&R 2.1)                                            
     -----------------------------------------------------------------          
                                                                                
          По определению K&R  не  разрешается  использовать  вложенные          
     комментарии. Например, конструкция:                                        
                                                                                
     /* Попытка прокомментировать myfunc() */                                   
                                                                                
     /*                                                                         
       myfunc()                                                                 
       {                                                                        
          printf("Это моя функция\n}   /* просто строка */                      
       }                                                                        
     */                                                                         
                                                                                
     будет интерпретироваться как только один комментарий,  оканчиваю-          
     щийся справа после фразы "просто строка". Оставшаяся скобка и ко-          
     нец  комментария  вызовут  синтаксическую  ошибку.  По  умолчанию          
      Турбо  Си не разрешает вложенные комментарии имеет                        
     Турбо Си не разрешает вложенные комментарии, хотя  вы можете кор-          
     ректно откомпилировать  (такую как показано)  программу с вложен-          
     ными  комментариями,  используя  опцию компилятора -c (либо с по-          
     мощью  конструкции Nested Comments... ON (разрешить вложение ком-          
     ментариев) в  O/C/Source меню).  Для  обеспечения большей мобиль-          
     ности правильнее отмечать код, который должен быть прокомментиро-          
     ван, директивами #if 0 и #endif.                                           
                                                                                
          Комментарии заменяются  одним  символом пробела после макро-          
     расширения. В  других реализациях комментарии  уничтожаются  пол-          
     ностью или иногда используется передача лексем.  Смотрите "Замена          

                         - 235,236 -
                                                                                
     лексем" в этой главе.                                                      
                                                                                
                                                                                
               Идентификаторы (K&R 2.2)                                         
     -----------------------------------------------------------------          
                                                                                
          Идентификаторы - это просто те имена, которые вы даете пере-          
     менным, функциям, типам  данных или другим объектам, определенным          
     пользователями.  В Си идентификаторы могут включать буквы (A...Z,          
     a...z)  и  цифры (0...9), а также символ подчеркивания.  Турбо Си          
     также разрешает вам использовать знак доллара ($). Конечно, иден-          
     тификатор может начинаться только с буквы или символа (_).                 
                                                                                
          Регистр (верхний или нижний) имеет значение: другими словами          
     идентификаторы indx и Indx различны.  В Турбо Си внутри программы          
     значащими являются первые 32 символа идентификатора ;  однако  вы          
     можете изменить это число с помощью опции компилятора -i#,  где #          
     является числом значащих символов.  (Это определяется в меню  оп-          
     ций O/C/S/Identifier Length (Длина Идентификатора) .)                      
                                                                                
                                                                                
          Первые 32 символа являются  значащими также и для глобальных          
                                                                                
     идентификаторов, берущихся  из других модулей.  Однако, вы имеете          
     опцию разрешающую определять или нет чувствительность к регистрам          
     этих  идентификаторов, используя опцию Case sensitive link..ON из          
     подменю Options/Linker или /c опцию компоновщика TLINK, запускае-          
     мого с командной строки.  Но отметим, конечно, что идентификаторы          
     типа  pascal  никогда не чувствительны к регистру во время компо-          
     новки.                                                                     
                                                                                
                                                                                
                                                                                
               Ключевые  слова (K&R 2.3)                                        
     -----------------------------------------------------------------          
                                                                                
          В таблице  8.1  приведены ключевые слова,  зарезервированные          
     Турбо Си,  которые не должны использоваться в  качестве  названий          
     идентификаторов. Предшествующее    им сокращение AN соответствует          
     ANSI расширениям K&R, а TC - расширениям Турбо Си. Ключевые слова          
     entry и fortran,  упомянутые в K&R, не используются и не резерви-          
     руются в Турбо Си.                                                         
                                                                                
                                                                                
                                                                                

                         - 237,238 -
                                                                                
                                                                                
          Таблица 11.1: Ключевые слова зарезервированные Турбо Си.              
     -----------------------------------------------------------------          
                                                                                
      TC  asm          extern        return      TC  _cs     TC  _DH            
          auto     TC  far           short       TC  _ds     TC  _DL            
          break        float     AN  signed      TC  _es     TC  _DX            
          case         for           sizeof      TC  _ss     TC  _BP            
      TC  cdecl        goto          static      TC  _AH     TC  _DI            
          char     TC  huge          struct      TC  _AL     TC  _SI            
      AN  const        if            switch      TC  _AX     TC  _SP            
          continue     int           typedef     TC  _BH                        
          default  TC  interrupt     union       TC  _BL                        
          do           long          unsigned    TC  _BX                        
          double       near      AN  void        TC  _CH                        
          else     TC  pascal    AN  volatile    TC  _CL                        
      AN  enum         register      while       TC  _CX                        
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
               Константы (K&R 2.4)                                              
     -----------------------------------------------------------------          
                                                                                
          Турбо Си поддерживает все типы констант, определенные в K&R,          
     с некоторыми расширениями.                                                 
                                                                                
                                                                                
                                                                                
          Целые константы (K&R 2.4.1)                                           
          ---------------------------                                           
                                                                                
          Допускается использование  десятичных  констант  в диапазоне          
     0...4294967295. (Отрицательные  константы рассматриваются  просто          
     как беззнаковые,    к которым применен унарный оператор "минус".)          
     Восьмиричные и шестнадцатиричные константы также допустимы.                
                                                                                
          Суффикс L (или l), присоединенный к любой константе, сделает          
     ее представление типа long.  Аналогично,  суффикс U (или u), дает          
     представление unsigned.  Константа становится unsigned long, если          
     ее значение превышает 65535,  независимо от используемого основа-          
     ния. Примечание: можно использовать оба (L, и U) суффикса для од-          
     ной и той же константы.                                                    

                         - 239,240 -
                                                                                
                                                                                
                                                                                
          В таблице 11.2  обобщены представления констант по всем трем          
     основаниям.                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Таблица 11.2. Целые константы Турбо Си без L или U.                   
     -----------------------------------------------------------------          
                                                                                
     __________________десятичные константы________________________             
                                                                                
               0-32767              int                                         
           32767-2147483647         long                                        
      2147483648-4294967295         unsigned long                               
               > 4294967295         будет переполнение без                      
                                    предупреждения;результат константы          
                                    будет представлен младшими битами           
                                    фактического значения                       
                                                                                
     __________________восьмиричные константы______________________             
                                                                                
                00-077777           int                                         
           0100000-0177777          unsigned int                                
          01000000-017777777777     long                                        
     0100000000000-0377777777777    unsigned long                               
                 > 0377777777777    будет переполнение (как описано             
                                    выше)                                       
                                                                                

                         - 241,242 -
                                                                                
     _________________шестнадцатиричные константы__________________             
                                                                                
            0x0000-0x7FFF           int                                         
            0x8000-0xFFFF           unsigned int                                
           0x10000-0x7FFFFFFF       long                                        
        0x80000000-0xFFFFFFFF       unsigned long                               
                 > 0xFFFFFFFF       будет переполнение (как описано             
                                    выше)                                       
     -----------------------------------------------------------------          
                                                                                
                                                                                
                                                                                
          Символьные константы (K&R 2.4.3)                                      
          --------------------------------                                      
                                                                                
          Турбо Си  поддерживает  двухсимвольные константы,  например,          
     'An', '\n\t'  и '\007\007'.  Эти константы имеют 16-битное предс-          
     тавление типа int,  причем первый символ находится в младшем бай-          
     те, а второй в старшем. Запомните, что такие константы не перено-          
     симы в другие компиляторы Си.                                              
                                                                                
          Односимвольные константы, такие как 'A', '\t' и '\007' также          
                                                                                
     имеют 16 битное представление типа int.  В  этом  случае  младший          
     байт является  сигналом переполнения в старшем байте; т.е.,  если          
     десятичное значение больше чем 127,  то старший байт устанавлива-          
     ется в -1 [=0xFF].  Это может быть запрещено объявлением, что тип          
     char по  умолчанию  является незначащим.  Для этого  используется          
     опция компилятора -k или конструкция Default char type...Unsigned          
     в подменю Options/Compiler/Source, делающая старший байт нулевым,          
     не считаясь со значением младшего байта.                                   
                                                                                
         Турбо Си поддерживает ANSI расширение, допускающее шестнадца-          
     тиричное представление кодов символов, например, '\x1F','\x82' и           
     так далее. Кроме того допустима запись x и X, а также использова-          
     ние от одной до трех цифр.                                                 
                                                                                
         Турбо  Си также поддерживает другие ANSI расширения из списка          
     разрешенных escape (эскейп)-последовательностей.  Escape-последо-          
     вательности представляют собой значения, которыми засылаются сим-          
     вольные  или строковые константы,перед которым находится обратный          
     слеш (\). В таблице 11.3 приведен список всех разрешенных  после-          
     довательностей, а те что отмечены звездочкой (*) являются  допол-          
     нением к списку, приведенному в K&R.                                       
                                                                                

                         - 243,244 -
                                                                                
          Таблица 11.3. Escape последовательности Турбо Си                      
     -----------------------------------------------------------------          
     Последовательность  Значение  Символ     Что делает                        
     -----------------------------------------------------------------          
                                                                                
     * \a                  0x07     BEL       Звуковой сигнал                   
       \b                  0x08     BS        <-- (Забой)                       
       \f                  0x0C     FF        Перевод страницы                  
       \n                  0x0A     LF        Перевод строки                    
       \r                  0x0D     CR        Возврат каретки                   
       \t                  0x09     HT        Горизонтальная табуляция          
     * \v                  0x0B     VT        Вертикальная табуляция            
       \\                  0x5c     \         Обратный слеш                     
       \'                  0x2c     '         Апостроф                          
     * \"                  0x22     "         Двойная кавычка                   
     * \?                  0x3F     ?         Вопросительный знак               
       \DDD                       любой       DDD = от 1 до 3                   
                                              восьмиричных цифр                 
     * \xHHH               0xHHH  любой       HHH = от 1 до 3                   
                                              шестнадцатиричнх цифр             
                                                                                
     ----------------------------------------------------------------           
                                                                                
     * Расширение ANSI к K&R                                                    
                                                                                
          Примечание: поскольку Турбо Си допускает двусимвольные конс-          
     танты, может возникнуть двусмысленность, если восьмиричная escape          
     последовательность из менее чем трех цифр предшествует цифре.   В          
     таких случаях,  Турбо Си будет ожидать, что следующий символ при-          
     надлежит escape последовательности, кроме случая, когда символ не          
     допускается для данного типа чисел. Например, поскольку цифры 8 и          
     9 не восьмиричные и не разрешены, то константа \258 будет интерп-          
     ретирована как   двухсимвольная константа,  состоящая из символов          
     \25 и 8.                                                                   
                                                                                
                                                                                
                                                                                
          Константы с плавающей точкой (K&R 2.4.4)                              
          ----------------------------------------                              
                                                                                
          Все константы  с  плавающей  точкой,   определенные  как тип          
     double, описаны в K&R. Однако, вы можете сделать константу с пла-          
     вающей точкой и типа float, добавив к ней суффикс F.                       
                                                                                
                                                                                

                         - 245,246 -
                                                                                
               Строки (K&R 2.5)                                                 
     -----------------------------------------------------------------          
                                                                                
          В соответствии с K&R строковые константы состоят обязательно          
     из одной строки,  имеющей конструкцию:  двойные кавычки,   текст,          
     двойные  кавычки ("как здесь"). Для продолжения символьной после-          
     довательности на новой строке  вы  должны  использовать  обратный          
     слеш.                                                                      
                                                                                
          В Турбо Си разрешается использовать многостроковые  элементы          
     в строковых константах,  которые могут потребоваться для конкате-          
     нации (соединения) строк. Так, например, вы можете сделать следу-          
     ющее:                                                                      
                                                                                
     main()                                                                     
     {                                                                          
          char   *p;                                                            
                                                                                
          p = "Это пример того, как Турбо Си"                                   
              " будет автоматически\nвыполнять конкатенацию"                    
              " ваших очень длинных строк,\nделая наглядным"                    
              " общий вид программ."                                            
                                                                                
                                                                                
          puts(p);                                                              
     }                                                                          
                                                                                
          Вот результат работы программы:                                       
                                                                                
     Это пример того, как Турбо Си будет автоматически                          
     выполнять конкатенацию ваших очень длинных строк,                          
     делая наглядным общий вид программ.                                        
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 247,248 -
                                                                                
                                                                                
              Зависимость от аппаратных средств (K&R 2.6)                       
     -----------------------------------------------------------------          
                                                                                
          В K&R признается,  что размер и числовой  диапазон  основных          
     типов данных   (со всеми вариациями)  очень зависит от конкретной          
     архитектуры компьютера.  Это справедливо для Турбо Си и для боль-          
     шинства  других  компиляторов  Си. В таблице 11.4 приведен список          
     размеров и соответствующих диапазонов для различных типов  данных          
     в Турбо Си.                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Таблица 11.4. Типы данных, размеры и диапазоны в Турбо Си             
     -----------------------------------------------------------------          
        Тип             Размер (в битах)        Диапазон                        
     -----------------------------------------------------------------          
                                                                                
      unsigned char           8                   0 - 255                       
      char                    8                -128 - 127                       
      enum                    16             -32768 - 32767                     
      unsigned short          16                  0 - 65535                     
      short                   16             -32768 - 32767                     
      unsigned int            16                  0 - 65535                     
      int                     16             -32768 - 32767                     
      unsigned long           32                  0 - 4294967295                
      long                    32        -2147483648 - 2147483647                
      float                   32            3.4E-38 - 3.4E+38                   
      double                  64           1.7E-308 - 1.7E+308                  
      long double             80          3.4E-4932 - 1.1E+4932                 
      pointer                 16     (указатели near,_cs,_ds,_es,_ss)           
      pointer                 32     (указатели far, huge)                      
                                                                                
                                                                                
                                                                                

                         - 249,250 -
                                                                                
              Преобразования (K&R 6)                                            
     -----------------------------------------------------------------          
                                                                                
          Турбо Си поддерживает стандартные механизмы по  автоматичес-          
     кому преобразованию одного типа данных в другой. Следующие разде-          
     лы поясняют дополнения к K&R или раскрывают специфические тонкос-          
     ти данной реализации.                                                      
                                                                                
                                                                                
                                                                                
          Char, int и enum (K&R 6.1)                                            
     -----------------------------------------------------------------          
                                                                                
          Преобразование символьной  константы к целому типу имеет ре-          
     зультатом 16-битное значение,  поскольку и одно,  и двусимвольные          
     константы представляются   16-битным значением (см.  K&R 2.4.3.).          
     Результат преобразования символьного объекта (такого как перемен-          
     ная) к    целочисленному  объекту автоматически получает знаковое          
     расширение, если вы не сделали по умолчанию тип char беззнаковым,          
     используя при  компиляции опцию -k.  Объекты типа signed char при          
     преобразовании их в int всегда  используют  знаковое  расширение;          
     объекты типа   unsigned  char всегда устанавливают старший байт в          
                                                                                
     ноль.                                                                      
                                                                                
          Значения типа enum преобразуются в int без модификации; ана-          
     логично значения, имеющие тип int, могут преобразовываться в зна-          
     чения перечислимого типа enum, а символы преобразуются в int зна-          
     чения и обратно.                                                           
                                                                                
                                                                                
                                                                                
          Указатели (K&R 6.4)                                                   
     -----------------------------------------------------------------          
                                                                                
          В Турбо  Си различные указатели вашей программы,  могут быть          
     различных размеров,  в зависимости от используемой модели  памяти          
     или используемого модификатора типа указатель. Например, когда вы          
     компилируете программу, используя конкретную модель памяти, адре-          
     суемые модификаторы (near, far, huge, _cs, _ds, _es, _ss) в вашем          
     исходном тексте могут изменять размер указателя,  заданный данной          
     моделью памяти.                                                            
                                                                                
          Указатель должен  быть  объявлен  как указатель на некоторый          
     конкретный тип,  даже если данный тип - void (который в  действи-          

                         - 251,252 -
                                                                                
     тельности означает указатель на ничего). Однако, будучи объявлен-          
     ным, такой указатель может указывать на объект любого другого ти-          
     па. Турбо Си позволяет переназначать указатели, но компилятор бу-          
     дет предупреждать о случаях переназначения, если только предвари-          
     тельно этот  указатель не был объявлен как указатель на тип void.          
     Однако, указатели  на типы данных не могут быть  преобразованы  в          
     указатели на функции, и наоборот.                                          
                                                                                
                                                                                
                                                                                
          Арифметические преобразования (K&R 6.6)                               
     -----------------------------------------------------------------          
                                                                                
          В K&R  определяются  обычные  арифметические преобразования,          
     определяющие, что произойдет с любыми величинами, использованными          
     в арифметических выражениях (операнд,  оператор,  операнд). Здесь          
     приведены шаги, используемые Турбо Си, для преобразования операн-          
     дов в арифметических выражениях:                                           
                                                                                
        1. Любой  не integer и не double тип преобразуется в соответс-          
     твии  с таблицой 11.5. После этого любые два значения, объединен-          
     ные оператором, являются целыми - int (включая  long  и  unsigned          
                                                                                
                                                                                
     модификаторы) или с плавающей точкой - double.                             
                                                                                
        2. Если какой-либо операнд имеет тип double, то другой операнд          
     тоже преобразуется в double.                                               
                                                                                
        3. В  случае,  если один из операндов имеет тип unsigned long,          
     то другой операнд тоже преобразуется в unsigned long.                      
                                                                                
        4. В случае,  если один из операндов имеет тип long, то другой          
     операнд тоже преобразуется в long.                                         
                                                                                
        5. В случае,  если какой-либо из операндов имеет тип unsigned,          
     то другой операнд тоже преобразуется в unsigned.                           
                                                                                
        6. В последнем варианте, оба операнда являются типа int.                
                                                                                
          Результат выражения определяется типами обоих операндов.              
                                                                                
                                                                                
                                                                                
                                                                                

                         - 253,254 -
                                                                                
                                                                                
        Таблица 11.5: Методы, используемые для определения результата           
                       в арифметических  преобразованиях.                       
     -----------------------------------------------------------------          
         Тип         Преобразуется в          Метод                             
     -----------------------------------------------------------------          
                                                                                
      char               int                 со знаком                          
      unsigned char      int         нулевой старший байт (всегда)              
      signed char        int                 со знаком (всегда)                 
      short              int         если unsigned, то unsigned int             
      enum               int                 тоже значение                      
      float              double      мантисса дополняется нолями                
     -----------------------------------------------------------------          
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
               Операторы (K&R  раздел 7.2)                                      
     -----------------------------------------------------------------          
                                                                                
          Турбо Си  поддерживает унарный оператор + , K&R - нет.  Этот          
     оператор  неэффективен, но необходимо предвидеть симметрию с про-          
     тивоположным оператором. Стараясь создать эффективно  компилируе-          
     мые  выражения, Турбо Си обычно выполняет перегруппировку выраже-          
     ния с переупорядочиванием коммутативных операторов (таких как * и          
     бинарный +). Однако, Турбо Си не будет переупорядочивать  выраже-          
     ния  с  плавающей  точкой. То есть можете использовать скобки для          
     выделения одних преобразований в выражениях  с  плавающей  точкой          
     перед  другими. Например, если a, b, c и f имеют тип float, тогда          
     выражение                                                                  
                                                                                
               f = a + (b + c);                                                 
                                                                                
     заставит  сначала  оценить  значение  (b+c),  а  затем  прибавить          
     результат к a.                                                             
                                                                                
                                                                                
                                                                                
                                                                                

                         - 255,256 -
                                                                                
                                                                                
               Спецификаторы типов и модификаторы (K&R 8.2)                     
     -----------------------------------------------------------------          
                                                                                
          Турбо Си поддерживает следующие основные типы, отсутствующие          
     у K&R:                                                                     
                                                                                
           - unsigned char                                                      
           - unsigned short                                                     
           - unsigned long                                                      
           - long double                                                        
           - enumeration                                                        
           - void                                                               
                                                                                
          Типы  int и short совпадают в Турбо Си; оба занимают 16 бит.          
     Смотрите "Hardware Specifics" (аппаратно-архитектурную специфику)          
     для более детального понимания о воплощении различных типов.               
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Тип enum                                                              
     -----------------------------------------------------------------          
                                                                                
          Турбо Си  поддерживает все перечислимые типы ANSI стандарта.          
     Перечислимый тип данных  используется  для  описания  дискретного          
     множества целых значений. Например, вы можете объявить следующее:          
                                                                                
          enum days { sun, mon, tues, wed, thur, fri, sat };                    
                                                                                
          Имена, перечисленные  в  days,  являются целыми константами:          
     первая (sun) автоматически установлена в ноль, а каждая следующая          
     имеет значение на единицу больше, чем предыдущая (mon = 1, tues=2          
     и т.д.).  Однако,  вы можете присвоить константам и  определенные          
     значения; следующие имена,  без конкретных значений, будут тогда,          
     как и раньше,  иметь значения предыдущих элементов с  увеличением          
     на единицу. Например,                                                      
                                                                                
          enum coins {penny=1, nickle=5, dime=10, quarter=25};                  
                                                                                
          Переменной перечислимого  типа  может  быть  присвоено любое          
     значение типа int - проверка типа не производится.                         
                                                                                

                         - 257,258 -
                                                                                
          Тип void                                                              
     -----------------------------------------------------------------          
                                                                                
          У K&R каждая функция возвращает значение; если тип не объяв-          
     лен, то  функция принадлежит типу int.  В Турбо Си поддерживается          
     тип void,  определенный в ANSI стандарте. Он используется для яв-          
     ного описания функций, не возвращающих значений. Аналогично, пус-          
     той список параметров может быть объявлен зарезервированным  сло-          
     вом void. Например,                                                        
                                                                                
      void putmsg(void)                                                         
      {                                                                         
           printf("Hello, world\n");                                            
      }                                                                         
                                                                                
      main()                                                                    
      {                                                                         
           putmsg();                                                            
      }                                                                         
                                                                                
          Вы можете  преобразовывать  выражение к типу void,  для того          
     чтобы явно указать,  что значение, возвращаемое функцией, игнори-          
                                                                                
                                                                                
     руется. Например,   если вы хотите приостановить выполнение прог-          
     раммы до тех пор пока пользователь не нажмет какую-либо  клавишу,          
     то можете написать:                                                        
                                                                                
          (void) getch();                                                       
                                                                                
          Наконец, вы  можете  объявить указатель на объект типа void.          
     Данный указатель не будет указателем на ничто; а будет указателем          
     на любой тип данных, причем конкретный тип этих данных знать нео-          
     бязательно. Вы  можете присваивать любой указатель указателю типа          
     void (и наоборот)  без приведения типов.  Однако вы не можете ис-          
     пользовать оператор косвенной адресации (*) с указателем void, т.          
     к. используемый тип неопределен.                                           
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 259,260 -
                                                                                
                                                                                
          Модификатор signed                                                    
     -----------------------------------------------------------------          
                                                                                
          Кроме указанных у K&R трех типов модификаторов - long, short          
     и unsigned - Турбо Си поддерживает еще  три:   signed,   const  и          
     volatile (все определены в ANSI стандарте).                                
                                                                                
          Модификатор signed противоположен unsigned и явно указывает,          
     что величина со знаком.  Данный модификатор используется  преиму-          
     щественно для документирования и придания законченного вида прог-          
     раммам. Однако,   если вы компилируете программу,   используя  по          
     умолчанию беззнаковый тип char (вместо знакового),  то нужно  ис-          
     пользовать модификатор signed, для того чтобы определить перемен-          
     ную или функцию типа signed char.  Модификатор signed,  использо-          
     ванный сам по себе, означает signed int, также как и unsigned оз-          
     начает unsigned int.                                                       
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Модификатор const                                                     
     -----------------------------------------------------------------          
                                                                                
          Модификатор const,  по определению стандарта ANSI, не допус-          
     кает каких бы то ни было переопределений собственных значений или          
     других косвенных изменений,  таких как увеличение или уменьшение.          
     Указатель типа const не может быть модифицирован,  в  отличии  от          
     самого объекта,   который он определяет.  Замечание:  модификатор          
     const, используемый сам по себе, эквивалентен const int. Рассмот-          
     рим следующие примеры:                                                     
                                                                                
          const float pi          = 3.1415926;                                  
          const       maxint      = 32767;                                      
          char *const str         = "Hello, world";  /* Указатель типа          
                                                              const */          
          char const  *str2       = "Hello, world";  /* Указатель на            
                                                           строку типа          
                                                              const */          
                                                                                
          Приведенные ниже утверждения недопустимы:                             
                                                                                
          pi  = 3.0;              /* Присвоение значения константе */           

                         - 261,262 -
                                                                                
                                                                                
          i   = maxint--;                  /* Уменьшение константы */           
          str = "Hi, there!";          /* Переназначение указателя */           
                                                                                
          Заметим,  однако, что вызов функции strcpy(str,"Hi, there!")          
     допустим,  поскольку  выполняет  посимвольное  копирование строки          
     символов  "Hi, there!" в область памяти, определяемую str.                 
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Модификатор volatile                                                  
     -----------------------------------------------------------------          
                                                                                
          Модификатор volatile,   также  определенный стандартом ANSI,          
     почти полная противоположность const.  Он указывает,  что  объект          
     может быть изменен не только вами, а также и чем-нибудь вне вашей          
     программы, например, программой прерываний или портом ввода/выво-          
     да. Объявление объекта как volatile предупреждает компилятор, что          
     не нужно делать предположений относительно значения  объекта,   в          
     тот момент когда оцениваются содержащие его выражения,  т.к. зна-          
     чение может (теоретически)  измениться в любое время. Кроме того,          
     volatile запрещает также компилятору использовать вместо перемен-          
     ных регистровые переменные.                                                
                                                                                
                                                                                
          volatile int ticks;                                                   
          interrupt timer();                                                    
          {                                                                     
               ticks++;                                                         
          }                                                                     
                                                                                
          wait (int interval)                                                   

                         - 263,264 -
                                                                                
                                                                                
          {                                                                     
               ticks = 0;                                                       
               while(ticks < interval);        /* ничего не делать */           
          }                                                                     
                                                                                
          Эти программы (иммитирующие таймер, связанный с прерываниями          
     от аппаратных часов) будут реализовывать временную задержку в со-          
     ответствии со значением, указанным в аргументе interval. Заметим,          
     что компилятор   с высоким уровнем оптимизации может не загрузить          
     значение ticks внутри цикла while, т.к. цикл не изменяет значения          
     ticks.                                                                     
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Модификаторы cdecl и pascal                                           
     -----------------------------------------------------------------          
                                                                                
          Турбо Си позволяет вызывать из  вашей  программы  программы,          
     написанные на других языках, и наоборот. При смешивании языков вы          
     должны умело обращаться с двумя важными объектами:  идентификато-          
     рами и передаваемыми параметрами.                                          
                                                                                
          При компиляции программы Турбо Си,  все глобальные идентифи-          
     каторы программы,  т.е.  имена функций и  глобальные  переменные,          
     сохраняются в объектном коде для последующей компоновки. По умол-          
     чанию эти идентификаторы записываются в оригинальном виде, (т.е в          
     соответствии с  состоянием регистра - заглавный,  строковый или и          
     тот, и другой). Кроме того, символ подчеркивание (_) предшествует          
     идентификатору, если    вы  не  использовали  опцию  -u(Generated          
     underbars...OFF).                                                          
                                                                                
          Аналогично,  все внешние  идентификаторы, объявленные вами в          
     программе,  остаются в  том  же  самом  формате.  Компоновщик (по          
     умолчанию) различает регистры клавиатуры, поэтому идентификаторы,          
     используемые в различных исходных файлах, должны точно соответст-          
     вовать и по орфографии, и по регистрам клавиатуры.                         

                         - 265,266 -
                                                                                
                                                                                
          pascal                                                                
     -----------------------------------------------------------------          
                                                                                
          В определенных ситуациях,  например, при использовании кода,          
     написанного на  других  языках,  описанный  выше метод сохранения          
     имен, применяемый по умолчанию, может вызвать ряд проблем.                 
                                                                                
          Турбо Си  дает  вам путь для обхода этих проблем.  Вы можете          
     объявить любой идентификатор как идентификатор типа pascal.   Это          
     означает, что идентификатор преобразуется к верхнему регистру и к          
     нему не добавляется символ подчеркивания. (Если идентификатор яв-          
     ляется функцией,  то данное правило распространяется и на переда-          
     ваемые параметры; см.  "Модификаторы типа функций" для более  де-          
     тального понимания.)   При этом используемый вами в исходном коде          
     регистр не учитывается,  т.к.  на  этапе  компоновки  применяется          
     только верхний регистр.                                                    
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          cdecl                                                                 
     -----------------------------------------------------------------          
                                                                                
          Установив при компиляции опцию -p (соответствующую стандарт-          
     ному Паскалю),  вы можете сделать все глобальные идентификаторы в          
     исходном файле типа pascal.  Однако затем вы можете захотеть ука-          
     зать, что  некоторые идентификаторы чувствительны  к  регистру  и          
     впереди имеют знак подчеркивания, особенно, если это Си идентифи-          
     каторы из другого файла.                                                   
                                                                                
          В этом случае вы можете сделать объявление этих  идентифика-          
     торов как cdecl,  (что также будет влиять и на передачу в функцию          
     параметров).                                                               
                                                                                
          Вы можете заметить,  например,  что все функции в заголовках          
     файлов (stdio.h и др.) имеют тип cdecl. Это дает возможность про-          
     вести компоновку с библиотечными программами, если вы используете          
     опцию -p при компиляции.                                                   
                                                                                
          Для  выяснения деталей  смотрите  раздел  10.1.1  K&R в этой          
     главе, а также главу 12.                                                   
                                                                                

                         - 267,268 -
                                                                                
          Модификаторы near, far и huge                                         
     -----------------------------------------------------------------          
                                                                                
          Турбо Си имеет три модификатора, воздействующих на косвенный          
     оператор (*),  и,  тем самым, модифицирующих указатели на данные.          
     Речь идет о near, far и huge. Назначение этих ключевых слов более          
     детально объясняется  в главе 12, а здесь дается лишь краткий об-          
     зор.                                                                       
                                                                                
          Турбо Си позволяет использовать при компиляции одну из  нес-          
     кольких моделей памяти. Модель, которую вы используете, определя-          
     ет (среди прочих деталей) внутренний формат указателей на данные.          
     Если вы  используете малую модель памяти (tiny,  small,  medium),          
     все указатели имеют длину только 16 бит и задают смещение относи-          
     тельно регистра сегмента данных (DS). Если вы используете большую          
     модель (compact, large, huge), все указатели на данные имеют дли-          
     ну 32 бита и задают адрес сегмента и смещение.                             
                                                                                
          Иногда, когда  используется модель данных одного размера,  у          
     вас может возникнуть желание объявить указатель  с  размером  или          
     форматом другим чем у используемого по умолчанию.  Вы можете сде-          
     лать это с помощью модификаторов near, far и huge.                         
                                                                                
                                                                                
          Указатель типа near имеет размер 16 бит; он использует теку-          
     щее содержимое регистра сегмента данных (DS)  для определения ад-          
     реса сегмента.   По  умолчанию  он используется для малых моделей          
     данных. При  использовании указателей типа  near,   данные  вашей          
     программы ограничены размером 64 K текущего сегмента данных.               
                                                                                
          Указатель типа  far  имеет размер 32 бита и содержит как ад-          
     рес, так и смещение. По умолчанию он используется для больших мо-          
     делей. При   использовании указателей типа far допускаются ссылки          
     на данные в пределах адресуемого  пространства  1  Мб  процессора          
     Intel 8088/8086.                                                           
                                                                                
          Указатель типа huge имеет размер 32 бита и аналогично преды-          
     дущему, содержит  адрес сегмента и смещение, однако, в отличии от          
     указателей типа far,  указатель huge всегда поддерживается норма-          
     лизованным. Более детально он рассматривается в главе 12, а здесь          
     дается отличие от указателей типа far:                                     
                                                                                
        # Операторы отношения (==, !=, <, >, <=, >=) работают коррект-          
     но с указателями типа huge; но не с указателями типа far.                  
                                                                                

                         - 269,270 -
                                                                                
                                                                                
        # Любые арифметические операции над указателем  huge  воздейс-          
     твуют как на адрес сегмента,  так и на смещение (из-за нормализа-          
     ции); при использовании far указателей - воздействие  распростра-          
     няется только на смещение.                                                 
                                                                                
        # Заданный  указатель типа huge может быть увеличен в пределах          
     1 Мб адресного пространства; указатель типа far,  соответственно,          
     будет циклически переходить на начало сегмента в 64К.                      
                                                                                
        # При  использовании  указателей  типа huge требуется дополни-          
     тельное время, т.к. программы нормализации должны вызываться пос-          
     ле выполнения любой арифметической операции над указателями.               
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
               Структуры  и  объединения (K&R раздел 8.5)                       
     -----------------------------------------------------------------          
                                                                                
          Турбо Си поддерживает реализацию структур и объединений сог-          
     ласно K&R,  а также обеспечивает следующие дополнительные возмож-          
     ности.                                                                     
                                                                                
                                                                                
                                                                                
          Выравнивание слов                                                     
     -----------------------------------------------------------------          
                                                                                
          Если вы используете опцию -a (Alignment...Word) при компиля-          
     ции, Турбо Си будет заполнять структуру (или объединение) байтами          
     так, как это требуется для выравнивания слов. В результате:                
                                                                                
        # Структура будет начинаться с границы слова (четный адрес).            
                                                                                
        # Любой  элемент  с не-char типом, будет иметь четное смещение          
     от начала структуры.                                                       
                                                                                
        # Если  необходимо, в конец будет добавлен байт  для  гарантии          

                         - 271,272 -
                                                                                
     того, что полная структура содержит четное число байт.                     
                                                                                
                                                                                
                                                                                
          Поля бит                                                              
     -----------------------------------------------------------------          
                                                                                
          В Турбо Си поле бит  может  иметь  тип  либо  signed,   либо          
     unsigned int и может занимать от 1 до 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  |                    
          |___________|___|___________|______________|_____|                    
                                                                                
                                                                                
          Поля целого  типа хранятся в одной из двух форм; самый левый          
     бит - знаковый бит.  Например, поле бит типа signed int шириной 1          
     бит может   хранить  только значение -1 или 0,  и любое ненулевое          
     значение будет интерпретироваться как -1.                                  
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 273,274 -
                                                                                
          Операторы (K&R 9)                                                     
     -----------------------------------------------------------------          
                                                                                
          В Турбо Си реализованы все операторы,  описанные в K&R,  без          
     исключений и модификаций.                                                  
                                                                                
                                                                                
                                                                                
               Определения внешних функций (K&R 10.1)                           
     -----------------------------------------------------------------          
                                                                                
          В Турбо Си описание extern,  заданное внутри функции,  имеет          
     действие в пределах данного блока.  Описание не  будет  распозна-          
     ваться вне блока,  в котором оно определено. Однако, Турбо Си бу-          
     дет запоминать описания, для того чтобы сопоставить их с последу-          
     ющими описаниями тех же самых объектов.                                    
                                                                                
          Турбо Си поддерживает большинство предложенных ANSI расшире-          
     ний к определениям функций K&R,  включая,  в частности,  дополни-          
     тельные модификаторы  функций и прототипы функций.  Турбо Си под-          
     держивает также несколько собственных  расширений  и  определений          
     функций, таких как функции типа interrupt (прерывание).                    
                                                                                
          Модификаторы типа функции (K&R 10.1.1)                                
     -----------------------------------------------------------------          
                                                                                
          В дополнение к external и static,  Турбо Си поддерживает ряд          
     модификаторов, специфицирующих  описания функций:  pascal, cdecl,          
     interrupt, near, far и huge.                                               
                                                                                
                                                                                
                                                                                
          Модификатор функции pascal                                            
          --------------------------                                            
                                                                                
          Модификатор pascal,  используемый в Турбо Си,   предназначен          
     для функций (и указателей на функции),  которые используют приня-          
     тую в Паскале последовательность передачи параметров.  Это позво-          
     ляет писать на языке Си функции, которые могут быть вызванными из          
     программ, написанных  на других языках; а также обращаться из ва-          
     ших Си программ к внешним программам, написанным на языках отлич-          
     ных от Си.  Имя функции преобразуется к верхнему  регистру,   для          
     правильной работы компоновщика.                                            
                                                                                
          Примечание: использование   опции  компилятора  -p  (Calling          

                         - 275,276 -
                                                                                
     convention...Pascal) будет  вызывать обращение со всеми функциями          
     (и указателями на эти функции)  как если бы они были типа pascal.          
     Кроме того, функции, объявленные типа pascal, могут вызываться из          
     Си программ, также как и Си программа может быть вызвана из функ-          
     ции, имеющей  тип pascal. Например, если вы объявили и откомпили-          
     ровали следующую функцию:                                                  
                                                                                
          pascal putnums(unt i, int j, int k)                                   
          {                                                                     
             printf("And the answers are: %d, %d и %d\n",i,j,k);                
          }                                                                     
                                                                                
     другая Си программа может затем компоноваться с ней  и обращаться          
     к ней, используя описание:                                                 
                                                                                
          pascal putnums(int i, int j, int k);                                  
                                                                                
          main()                                                                
          {                                                                     
             putnums(1,4,9);                                                    
          }                                                                     
                                                                                
                                                                                
          Функции типа pascal не могут  иметь различное число аргумен-          
     тов, как,  например, функция printf. По этой причине вы не можете          
     использовать  эллипсис   (...)   (т.е.  опускать  подразумеваемый          
     параметр)  в  определении функции  типа  pascal.  (См. "Прототипы          
     функций"  для понимания  использования  элипсиса  при определении          
     функции с различным числом аргументов.)                                    
                                                                                
                                                                                
                                                                                
          Модификатор функции cdecl                                             
          -------------------------                                             
                                                                                
          Модификатор cdecl также является специфичным для  Турбо  Си.          
     Как и модификатор pascal,  он используется с функциями или указа-          
     телями на функции. Его действие отменяет директиву компилятора -p          
     и разрешает   вызывать функции как обычные функции Си.  Например,          
     если вы при компиляции вышеприведенной программы установили опцию          
     -p, но  захотели использовать printf, то должны поступить следую-          
     щим образом:                                                               
                                                                                
          extern cdecl printf();                                                
          putnums(int i, int j, int k);                                         

                         - 277,278 -
                                                                                
                                                                                
          cdecl main()                                                          
          {                                                                     
              putnums(1,4,9);                                                   
          }                                                                     
                                                                                
          putnums(int i, int j, int k)                                          
          {                                                                     
              printf("And the answers are: %d, %d и %d\n",i,j,k);               
          }                                                                     
                                                                                
          Если программа  компилируется  с опцией -p,  то все функции,          
     используемые из библиотеки времени выполнения,  необходимо объяв-          
     лять как   cdecl.  Если вы посмотрите файлы заголовков (такие как          
     STDIO.H), то  увидете, что каждая функция явно описана как cdecl,          
     т.е. заранее подготовлена к этому. Заметьте, что главная програм-          
     ма main должна быть также объявлена как cdecl, поскольку действие          
     стартового кода программы, написанного на Си, всегда начинается c          
     вызова модуля main.                                                        
                                                                                
                                                                                
                                                                                
                                                                                
          Модификатор функции interrupt                                         
          -----------------------------                                         
                                                                                
          Модификатор interrupt также является специфическим для Турбо          
     Си. Функции interrupt специально введены для использования с век-          
     торами прерываний процессоров типа 8086/8088. Турбо Си будет ком-          
     пилировать функцию типа interrupt в дополнительную функцию,  вход          
     и выход которой сохраняется в регистрах AX,  BX,  CX, DX, SI, DI,          
     ES и DS.  Другие регистры:  BP,  SP,  SS, CS и IP сохраняются как          
     часть последовательности  Си вызова или как часть самой обработки          
     прерывания. Рассмотрим  пример типичного определения функции типа          
     interrupt.                                                                 
                                                                                
          void interrupt myhandler()                                            
          {                                                                     
               . . .                                                            
          }                                                                     
                                                                                
          Вы можете объявлять  функции  прерываний  как  функции  типа          
     void. Функции прерываний могут объявляться для любой модели памя-          
     ти. Для всех моделей, исключая huge, в регистр DS заносится прог-          
     раммный сегмент данных.  Для модели huge в DS заносится модульный          

                         - 279,280 -
                                                                                
     сегмент данных.                                                            
                                                                                
                                                                                
          Модификаторы функций near, far и huge                                 
          -------------------------------------                                 
                                                                                
          Модификаторы  near, far, и huge специфичны для Турбо Си. Они          
     могут комбинироваться с модификаторами cdecl или pascal, но не  с          
     interrupt.  Не-interrupt  функции  могут быть объявлены как near,          
     far, или huge. Они по умолчанию устанавливаются для данной модели          
     памяти. Near-функции используют near-вызовы, а far- или huge-фун-          
     кции используют far-вызовы инструкций.  В  моделях  памяти  tiny,          
     small,  и  compact  специально неоговоренные функции по умолчанию          
     имеют тип near. В моделях medium и large неоговоренные функции по          
     умолчанию имеют тип far. В модели памяти huge  по  умолчанию  ис-          
     пользуется тип huge. Функции типа huge и far одинаковы за тем ис-          
     ключением  что  в регистр DS заноситься адрес сегмента данных ис-          
     ходного модуля когда вызывается huge-функция  и  он  сбрасывается          
     для far-функции. Функции типа huge обычно используются когда неб-          
     ходимо организовать интерфейс с кодом на языке ассемблера который          
     не может использовать некоторую память занятую из Турбо Си.                
                                                                                
                                                                                
                                                                                
                                                                                
          Прототипы функций (K&R 10.1.2)                                        
          ------------------------------                                        
                                                                                
          При объявлении функций в K&R допускается  только указание ее          
     имени,  типа и скобок без параметров.  Параметры  (если они есть)          
     объявляются только во время явного определения самой функции.              
                                                                                
          ANSI стандарт и Турбо Си  разрешают  использовать  прототипы          
     функций для объявления функции.  Эти объявления включают информа-          
     цию о параметрах функции. Компилятор использует данную информацию          
     для проверки вызовов функций на соответствие данных,  а также для          
     преобразования аргументов к требуемому типу. Рассмотрим следующий          
     фрагмент программы:                                                        
                                                                                
          long lmax(long v1, long v2);                                          
                                                                                
          main()                                                                
          {                                                                     
                int limit = 32;                                                 
                char ch   = 'A';                                                

                         - 281,282 -
                                                                                
                long mval;                                                      
                                                                                
                mval = lmax(limit,ch);                                          
          }                                                                     
                                                                                
          Используя прототип  функции  для  lmax,  эта программа будет          
     преобразовывать параметры limit и ch к типу long, используя стан-          
     дартные правила  преобразования,  прежде чем они будут помещены в          
     стек для обращения к lmax. При отсутствии прототипа функции пара-          
     метры limit и ch были бы помещены в стек, соответственно, как це-          
     лое значение и символ; в этом случае в lmax передавались бы пара-          
     метры, не  совпадающие по размеру и содержанию с ожидаемыми.  Это          
     ведет к возникновению проблем.  В то время как Си в K&R не выпол-          
     няет никакого контроля типа параметров или их числа,  использова-          
     ние прототипов   функций очень помогает выявлять "жучки" и другие          
     ошибки программистов.                                                      
                                                                                
          Прототипы функций  также помогают при документировании прог-          
     рамм. Например,   функция strcpy имеет два  параметра:   исходную          
     строку и  выходную строку.  Вопрос - как их распознать?  Прототип          
     функции                                                                    
                                                                                
                                                                                
          char  *strcpy(char *dest, char *source);                              
                                                                                
     делает это ясным.  Если в файле заголовка имеются прототипы функ-          
     ций, то вы можете распечатать этот файл и получить большую инфор-          
     мацию необходимую для написания программы,  вызывающей эти  функ-          
     ции.                                                                       
                                                                                
          Описание функции с включенным  в  скобки единственным словом          
     void означает, что функция совсем не имеет аргументов                      
                                                                                
             f(void)                                                            
                                                                                
          В противном случае, в скобках указывается список параметров,          
     разделенных запятыми. Так, объявление может быть сделано в виде:           
                                                                                
            func(int *, long);                                                  
                                                                                
     или с включением идентификаторов, как ниже:                                
                                                                                
                                                                                
            func(int *count, long total);                                       
                                                                                

                         - 283,284 -
                                                                                
          В обоих случаях,  указанных выше, функция func принимает два          
     параметра: указатель  на тип int,  названный count, и целую пере-          
     менную типа long,  названную total.  Включение  идентификатора  в          
     объявление имеет  смысл лишь для вывода его в диагностическом со-          
     общении, в случае возникновения несоответствия типа параметров.            
                                                                                
          Прототип обычно определяет функцию как функцию,  принимающую          
     фиксированное число параметров.  Для Си функций, принимающих раз-          
     личное число параметров (таких, как printf), пропотип функции мо-          
     жет заканчиваться элипсисом (...), например                                
                                                                                
          f(int *count, long total,...)                                         
                                                                                
          У прототипов такого вида фиксированные параметры проверяются          
     во время компиляции,  а опущенные передаются,  как при отсутствии          
     прототипа.                                                                 
                                                                                
          Рассмотрим несколько  примеров объявления функций и прототи-          
     пов.                                                                       
                                                                                
          int f();        /* Функция возвращает величину типа int без           
                            информации о параметрах. Это классический           
                                                                                
                                                         стиль K&R */           
                                                                                
          int f(void);    /* Функция возвращает значение типа int, */           
                                        /* параметры не передаются */           
                                                                                
          int p(int,long); /* Функция возвращает целое, а получает */           
                                                 /* два параметра; */           
                            /* первый имеет тип int, второй - long */           
                                                                                
          int pascal q(void); /* Функция типа pascal, возвращающая */           
                                 /* целое; параметры не передаются */           
                                                                                
          char far * s(char *source, int kind); /* Функция возвра- */           
                              /* щает указатель типа far на строку,*/           
                 /*а получает два параметра: типа char* и типа int */           
                                                                                
          int printf(char *format,...);      /* Функция возвращает */           
                           /* значение типа int, получая указатель */           
                            /* на фиксированный параметр типа char */           
                   /* и любое количество дополнительных параметров */           
                                              /* неизвестного типа */           
                                                                                

                         - 285,286 -
                                                                                
          int (*fp)(int);    /* Указатель на функцию, возвращающую */           
                                /* целое и получающую единственный */           
                                                 /* целый параметр */           
                                                                                
          Ниже приводятся правила, регламентирующие работу с модифика-          
     торами языка и формальными параметрами в  вызовах  функций  Турбо          
     Си, как использующих прототипы, так и не использующих их:                  
                                                                                
     Правило #1. Модификаторы языка для описания функций должны совпа-          
                 дать с   модификаторами,  используемыми в объявлении           
                 функции, для всех обращений к функции.                         
                                                                                
     Правило #2. Функция  может изменять значения формальных  парамет-          
                 ров, но   это не оказывает какого-либо воздействия на          
                 значения действительных аргументов в вызывающей прог-          
                 рамме, за исключением функций прерывания. Для большей          
                 информации смотрите "Функции прерывания" в главе 12.           
                                                                                
          Если прототип функции не объявлен предварительно,  то  Турбо          
     Си преобразует   целочисленные  аргументы при обращении к функции          
     согласно правилам, приведенным в разделе "Арифметические преобра-          
     зования". Если прототип объявлен,  то Турбо Си преобразует задан-          
                                                                                
     ные аргуметы к типу, назначенному для параметров.                          
                                                                                
          Если прототип функции включает элипсис (...),  то  Турбо  Си          
     преобразует все   заданные  аргументы  функции как в любом другом          
     прототипе (до элипсиса).  Компилятор будет расширять любые  аргу-          
     менты, заданные после фиксированного числа параметров по нормаль-          
     ным правилам для аргументов функций без прототипов.                        
                                                                                
          Если есть прототип, то число аргументов должно быть соответ-          
     ственным (за исключением случая,  когда  в  прототипе использован          
     элипсис).  Типы должны  быть  совместимы  только  по размеру, для          
     того  чтобы  корректно  производились  преобразования  типов.  Вы          
     всегда  должны  использовать  явные  преобразования  аргументов к          
     типу, допустимому для прототипа функции.                                   
                                                                                
          Следующий пример прояснит данные замечания:                           
                                                                                
        int strcmp(char *s1, char *s2);       /* Полный прототип */             
        int *strcpy();                          /* Нет прототипа */             
        int samp1(float, int, ...);            /* Полный прототип */            
                                                                                
        samp2()                                                                 

                         - 287,288 -
                                                                                
        {                                                                       
        char *sx, *cp;                                                          
        double z;                                                               
        long a;                                                                 
        float q;                                                                
                                                                                
        if (strcmp(sx, cp))      /* 1. Верно */                                 
            strcpy(sx,  cp,  44);/* 2. Верно, но не переносимо из               
                                                          Турбо Си */           
                                                                                
        samp1(3, a, q);          /* 3. Корректно */                             
        strcpy(cp);              /* 4. Ошибка при выполнении */                 
        samp1(2);                /* 5. Ошибка при компиляции */                 
                                                                                
        }                                                                       
                                                                                
          Пять вызовов (каждый с комментарием)  примера  демонстрируют          
     различные варианты вызовов функций и прототипов.                           
                                                                                
          В вызове No 1 использование функции strcmp явно соответству-          
     ет прототипу, что справедливо для всех случаев.                            
                                                                                
                                                                                
          В вызове No 2  обращение  к  strcpy  имеет  лишний  аргумент          
     (strcpy определена  для двух аргументов,  а не для трех).  В этом          
     случае Турбо Си теряет небольшое количество времени и создает код          
     для помещения лишнего аргумента в стек.  Это, однако, не является          
     синтаксической ошибкой, поскольку компилятор не знает о числе ар-          
     гументов strcpy. Такой вызов не допустим для других компиляторов.          
                                                                                
          В вызове  No 3  прототип требует,  чтобы первый аргумент для          
     samp1 был преобразован к float,  а второй - к int. Компилятор вы-          
     даст предупреждение   о  возможной потере значащих цифр поскольку          
     при преобразовании типа long к типу int отбрасываются старшие би-          
     ты. (Вы можете избавиться от такого предупреждения, если зададите          
     явное преобразование к целому.) Третий аргумент, q, соответствует          
     элипсису в   прототипе.   Он преобразуется к типу double согласно          
     обычному арифметическому преобразованию.  Вызов полностью коррек-          
     тен.                                                                       
                                                                                
          В вызове  No 4 снова вызывается strcpy,  но число аргументов          
     слишком мало. Это вызовет ошибку при выполнении программы. Компи-          
     лятор будет   молчать  (если  даже число параметров отличается от          
     числа параметров в предыдущем вызове той же функции),  т.к.   для          
     strcpy не определен прототип функции.                                      

                         - 289,290 -
                                                                                
                                                                                
                                                                                
          В вызове  No 5 функции samp1 задано слишком мало аргументов.          
     Т.к. samp1  требует минимум два аргумента, этот оператор является          
     ошибочным. Компилятор    выдаст сообщение о том,  что в вызове не          
     хватает аргументов.                                                        
                                                                                
          Важное замечание: если ваш прототип функции не соответствует          
     действительному определению функции,  то Турбо Си обнаружит это в          
     том и только в том случае,  если это определение находится в  том          
     же файле,  что и прототип. Если вы создаете библиотеку программ с          
     передаваемым набором прототипов (файлом include),  то  вы  должны          
     учитывать включение  файла с прототипами во время компиляции биб-          
     лиотеки, с  целью выявления любого противоречия между прототипами          
     и действительными определениями функций.                                   
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
               Правила видимости (K&R 11)                                       
     -----------------------------------------------------------------          
                                                                                
          Турбо Си разрешает более свободное обращение с неуникальными          
     идентификаторами, чем того требует K&R.  В Турбо Си различают че-          
     тыре класса идентификаторов:                                               
                                                                                
          Переменные, имена   новых   типов,   описываемых  с  помощью          
     typedef, и  перечисленные члены должны  быть  уникальными  внутри          
     блока, в котором они описаны. Идентификаторы, объявленные внешни-          
     ми, должны быть уникальными среди переменных, описанных как внеш-          
     ние.                                                                       
                                                                                
          Имена структур,  объединений и перечислений должны быть уни-          
     кальными внутри блока,  в котором они описаны. Эти имена, описан-          
     ные вне пределов какой-либо функции,  должны быть уникальны среди          
     всех соответствующих имен, описанных как внешние.                          
                                                                                
          Имена членов структуры и  объединения должны быть  уникальны          
     в структуре или объединении, в которых они описаны. Не существует          
     никаких ограничений на тип или смещение для  членов с одинаковыми          
     именами в различных структурах.                                            

                         - 291,292 -
                                                                                
                                                                                
                                                                                
          Метки  на  которые  ссылаются   операторы  goto  должны быть          
     уникальными внутри функции, в которой они определены.                      
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
               Команды управления трансляцией (K&R 12)                          
     -----------------------------------------------------------------          
                                                                                
          Турбо Си поддерживает все управляющие команды, описанные в K          
     &R. Этими командами являются директивы препроцессора - строки ис-          
     ходной программы,  начинающиеся со знака #,  за которым или перед          
     которым может следовать символ пробела или табуляции.                      
                                                                                
                                                                                
                                                                                
               Замена лексем (K&R 12.1)                                         
     -----------------------------------------------------------------          
                                                                                
          Турбо Си поддерживает определения K&R для  #define и  #undef          
     со следующими дополнениями:                                                
                                                                                
          1. Ниже приведенные идентификаторы не должны  встречаться  в          
     директивах #define и #undef:                                               
                                                                                
          _STDC_                                                                
          _FILE_                                                                
          _LINE_                                                                

                         - 293,294 -
                                                                                
          _DATE_                                                                
          _TIME_                                                                
                                                                                
          2. Две лексемы могут быть помещены вместе в макроопределение          
     с разделением их знаками ## (плюс необязательные пробелы с каждой          
     стороны). Препроцессор  удаляет пробелы и ##, а также комбинирует          
     разделенные лексемы.  Это может быть  использовано  для  создания          
     идентификаторов, например, задав:                                          
                                                                                
          #define VAR(i,j)          (i ## j)                                    
                                                                                
     при  VAR(x,6)  обращение  вызовет  подстановку  x6.  Это заменяет          
     иногда употребляемую, но не переносимую запись (i/**/j).                   
                                                                                
          3. Вложенные макросы  в  строке  макроопределения  сработают          
     лишь тогда,   когда сработает сам макрос,  а не при его описании.          
     Это больше касается вложенных макросов #undef.                             
                                                                                
          4. Символ #,  помещаемый перед макроаргументом,  указанном в          
     последовательности, преобразует аргумент в строку. При макроподс-          
     тановке производится замена #<формальный аргумент> на  "<действи-          
     тельный аргумент>". Так, при задании макроопределения                      
                                                                                
                                                                                
       #define TRACE(flag) printf(#flag "= %d\n", flag)                         
                                                                                
     следующий фрагмент текста программы                                        
                                                                                
          highval = 1024;                                                       
          TRACE(highval);                                                       
                                                                                
     преобразуется в:                                                           
                                                                                
          highval = 1024;                                                       
          printf("highval" "= %d\n", highval);                                  
                                                                                
     который, в переводе, становиться                                           
                                                                                
          highval = 1024;                                                       
          printf("higval=%d\n",hidhval);                                        
                                                                                
                                                                                
          5. В  отличие от других реализаций,  Турбо Си не подставляет          
     макроаргументы внутри строк и символьных констант.                         
                                                                                

                         - 295,296 -
                                                                                
               Включение файла (K&R 12.2)                                       
     -----------------------------------------------------------------          
                                                                                
          В Турбо Си директива #include реализована согласно K&R, но с          
     некоторыми дополнительными   особенностями.  Если препроцессор не          
     нашел include файл в каталоге, установленном по умолчанию, (пред-          
     полагается, что  вы используете запись include "filename"), тогда          
     он ищет каталоги, заданные опцией компилятора -I. Если вы исполь-          
     зуете форму   #include <ИмяФайла>,  тогда ищутся только каталоги,          
     заданные опцией -I.  (Каталоги,  перечисленные в  меню  по  опции          
     O/Environment/Include, эквивалентны   маршруту,  указанному с по-          
     мощью опции -I pathname в командной строке.                                
                                                                                
          Вы можете задать маршрут к #include,  через граничные разде-          
     лители, используя макрорасширение. Если следующая после ключевого          
     слова строка  начинается с идентификатора,  то препроцессор прос-          
     матривает текст для макроса.  Однако, если строка заключена в ка-          
     вычки или в острые скобки,  то Турбо Си не будет проверять ее для          
     распознавания макроса. Например,                                           
                                                                                
          #define myinclude     "c:\tc\include\mystuff.h"                       
                                                                                
                                                                                
                                                                                
          #include myinclude                                                    
                                                                                
          #include "myinclude.h"                                                
                                                                                
          1-ый  #include  оператор заставит препроцессор просматривать          
     C:\TC\INCLUDE\MYSTUFF.H,   тогда   как   2-ой   вызовет  просмотр          
     MYINCLUDE.H в каталоге по умолчанию.                                       
                                                                                
          Кроме того,   вам  нельзя  использовать объединение литерных          
     строк и вставку лексем в макросе, который используется в операто-          
     ре include. Макрорасширение должно создавать текст, который чита-          
     ется как нормальная #include директива.                                    
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 297,298 -
                                                                                
               Условная компиляция (K&R 12.3)                                   
     -----------------------------------------------------------------          
                                                                                
          Турбо Си поддерживает определение условной компиляции K&R  с          
     помощью замены   соответствующих  строк  на пустые.  Игнорируемые          
     строки начинаются с директив #if,  #ifdef, #ifndef, #else, #elif,          
     и #endif,  также как и все некомпилируемые строки, являющиеся ре-          
     зультатом этих директив. Все директивы условной компиляции должны          
     заканчиваться в  исходной программе или include файле,  в которых          
     они начались.                                                              
                                                                                
          Турбо Си поддерживает также оператор  ANSI  defined(символ).          
     Значение 1 (true)  присваивается,  если символ был предварительно          
     определен (с использованием #define)  и затем не был  отменен  (с          
     использованием undef);     в  противном  случае  присваивается  0          
     (false). Так, директива                                                    
                                                                                
          #if defined(mysym)                                                    
                                                                                
     адекватна директиве                                                        
                                                                                
          #ifdef mysym                                                          
                                                                                
                                                                                
                                                                                
          Преимущество в том,  что можно повторно использовать defined          
     в сложном выражении, стоящем за директивой #if:                            
                                                                                
          #if defined(mysym) || defined(yoursym)                                
                                                                                
          Наконец, Турбо Си (в отличие от ANSI) позволяет использовать          
     оператор sizeof  в выражениях препроцессора.  Так вы можете напи-          
     сать следующее:                                                            
                                                                                
          #if (sizeof(void *) == 2)                                             
          #define SDATA                                                         
          #else                                                                 
          #define LDATA                                                         
          #endif                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 299,300 -
                                                                                
               Управление строками (K&R 12.4)                                   
     -----------------------------------------------------------------          
                                                                                
          Турбо Си поддерживает определение #line,  данное в K&R. Рас-          
     ширения макросов в #line такие же, как и в #include.                       
                                                                                
                                                                                
                                                                                
               Директива error (ANSI Си 3.8.5)                                  
     -----------------------------------------------------------------          
                                                                                
          Турбо Си поддерживает директиву #error,  которая упоминается          
     (но не определена в полной мере) в ANSI-стандарте. Ее формат:              
                                                                                
          #error errmsg                                                         
                                                                                
     и вызывает сообщение                                                       
                                                                                
          Fatal: filename line# Error directive: errmsg                         
                                                                                
       (Фатальная ошибка: Имяфайла строка# Ошибка в директиве: errmsg)          
                                                                                
                                                                                
          У  программистов  принято  включать эту директиву в условный          
     препроцессор, что захватывает некоторые  нежелательные  состояния          
     во  время  компиляции.  При благоприятном исходе это состояние не          
     будет true.  В случае, если это состояние true, вы захотите напе-          
     чатать сообщение об ошибке и остановить компиляцию. Вы вставляете          
     директиву #error внутрь условия, для которого true является неже-          
     лательным исходом.  Например,  предполагается,  что #define MYVAL          
     принимает значения либо 0 либо 1. Вы можете тестировать на некор-          
     ректность значение MYVAL, если включите в ваш исходный код следу-          
     ющее условие:                                                              
                                                                                
                                                                                
          #if (MYVAL != 0 && MYVAL != 1)                                        
                                                                                
          #error MYVAL должен быть определен только либо 0 либо 1               
                                                                                
          #endef                                                                
                                                                                
                                                                                
          Препроцессор просматривает текст,  уничтожая комментарии,  а          
     на экран   выводит  оставшийся  текст без просмотра для выявления          
     макросов.                                                                  

                         - 301,302 -
                                                                                
               Директива pragma (ANSI Си 3.8.6)                                 
     -----------------------------------------------------------------          
                                                                                
          Турбо Си поддерживает директиву  #pragma,   которая  (как  и          
     error), неясно  определяется в стандарте ANSI.  Ее целью является          
     разрешить специализированные директивы по форме:                           
                                                                                
          #pragma <имя директивы>                                               
                                                                                
          С помощью #pragma Турбо Си может определить любые директивы,          
     которые ему   требуются,   без вмешательства других компиляторов,          
     поддерживающих #pragma.  Почему? Потому что, по определению, если          
     компилятор не опознал имя директивы,  то он  игнорирует директиву          
     #pragma.                                                                   
                                                                                
                                                                                
                                                                                
          #pragma inline                                                        
          --------------                                                        
                                                                                
          Турбо Си распознает три директивы #pragma. Первая:                    
                                                                                
                                                                                
          #pragma inline                                                        
                                                                                
          Эта директива эквивалентна опции компилятора -B. Она сообща-          
     ет компилятору о том,  что в программе присутствуют ассемблеровс-          
     кие команды  (см.  главу 12).  Наилучшее ее расположение - начало          
     файла, т.к. компилятор самоперезапускается с опцией -B сразу, как          
     только встретится #pragma inline.  На самом деле, вы можете опус-          
     тить и опцию -B,  и директиву #pragma inline, т.к. компилятор все          
     равно самоперезапускается,    как  только встретит asm операторы;          
     целью этой опции и директивы является экономия  времени  компиля-          
     ции.                                                                       
                                                                                
                                                                                
                                                                                
          #pragma warn                                                          
          ------------                                                          
                                                                                
          Вторая #pragma директива - это                                        
                                                                                
          #pragma warn                                                          
                                                                                
          Данная директива  позволяет  не принимать во внимание специ-          

                         - 303,304 -
                                                                                
     альную опцию командной строки  -wxxx  (или  спецификацию  Display          
     warnings...On ).                                                           
                                                                                
          Например, если  исходный текст программы содержит директивы:          
                                                                                
          #pragma warn +xxx                                                     
          #pragma warn -yyy                                                     
          #pragma warn .zzz                                                     
                                                                                
          то  xxx включит вывод пользователю предупреждений (если даже          
     в  подменю O/C/Errors/  она  была выключена);  yyy выключит вывод          
     сообщений;  а  zzz восстановит  первоначальное  значение, которое          
     было в начале компиляции файла.                                            
                                                                                
          Полный список трехзначных аббревиатур и  предупреждений, ко-          
     торые они включают  и выключают,  приведен в Приложении  Справоч-          
     ного Руководства по Турбо Си.                                              
                                                                                
                                                                                
          #pragma-директива saveregs                                            
                                                                                
          Эта pragma-директива гарантирует что huge-функции не изменят          
                                                                                
                                                                                
     значения любых регистров введенных до этого. Эта директива иногда          
     нужна для организации интерфейса с языком  ассемблера.  Директива          
     может быть размещена непосредственно перед определяемой функцией.          
     Тогда она будет применена только к этой функции.                           
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 305,306 -
                                                                                
               Директива null (ANSI Си 3.7)                                     
     ----------------------------------------------------------------           
                                                                                
          Ради завершенности, ANSI-стандарт и Турбо Си опознают пустую          
     директиву,  состоящую из строки со знаком #. Эта директива всегда          
     игнорируется.                                                              
                                                                                
                                                                                
                                                                                
               Встроенные макроимена (ANSI Си 3.8.8)                            
     ----------------------------------------------------------------           
                                                                                
          ANSI стандарт требует чтобы в  реализации  было 5 встроенных          
     макросов.  Турбо Си применяет все  5.  Отметим, что каждый из них          
     начинается и оканчивается символами подчеркивания (__).                    
                                                                                
     _LINE_ Номер обрабатываемой строки исходной программы -  десятич-          
            ная константа.    Первая строка исходного файла определена          
            как 1.                                                              
                                                                                
     _FILE_ Имя обрабатываемого исходного файла - строка символов.              
            Данное макроопределение изменяется всякий раз,  при  обра-          
                                                                                
            ботке компилятором директивы #include или директивы #line,          
            или по окончании включаемого файла.                                 
                                                                                
     _DATE_ Дата начала обработки текущего исходного  файла  -  строка          
            символов.                                                           
                                                                                
            Каждое вхождение  _DATE_ в заданном файле гарантирует одно          
            значение, независимо  от продолжительности обработки. Дата          
            имеет формат   mmm  dd yyyy,  где mmm - месяц (Jan,  Feb и          
            т.д.), dd  - число текущего месяца (1...31; в первой пози-          
            ции dd ставится пробел,  если число меньше 10), yyyy - год          
            (1988, 1989 и т.д.).                                                
                                                                                
     _TIME_ Время начала обработки текущего исходного файла препроцес-          
            сором - строка символов.                                            
                                                                                
            Каждое вхождение  _TIME_ в заданном файле гарантирует одно          
            значение, независимо от продолжительности обработки. Время          
            имеет формат hh:mm:ss, где hh - час (00...23), mm - минуты          
            (00...59), ss - секунды (00...59).                                  
                                                                                
     _STDC_ Константа,  равная 1,  если вы компилируете с (-A)  флагом          

                         - 307,308 -
                                                                                
                                                                                
            (ANSI keywords only...ON), устанавливающим совместимость с          
            ANSI стандартом; иначе макроопределение не определено.              
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
               Встроенные макросы Турбо Си                                      
     -----------------------------------------------------------------          
                                                                                
          Для вашего использования препроцессор  Турбо  Си  определяет          
     несколько дополнительных макросов. Также, как для макросов, пред-          
     писанных стандартом ANSI, каждый из них начинается и оканчивается          
     двумя символами подчеркивания.                                             
                                                                                
     _TURBOC_  Выдает номер текущей версии Турбо Си - шеснадцатиричная          
               константа. Версия 1.0 представляется как 0x0100; версия          
               1.2 - как 0x0102 и т.д.                                          
                                                                                
     _PASCAL_  Определяет наличие  флага  -p;  устанавливается в целую          
               константу, равную  1,  если используется флаг -p; иначе          
               не определяется.                                                 
                                                                                
     _MSDOS_   Целая константа, равная 1, для всех компиляторов                 
                                                                                
     _CDECL_   Сигнализирует о том,   что  флаг  -p  не  использовался          
               (Calling convention...C); устанавливает целую  констан-          
               ту, равную  1,  если флаг -p не использовался; иначе не          
               определяется.                                                    

                         - 309,310 -
                                                                                
                                                                                
          Следующие 6 макросов зависят от выбранной для компиляции мо-          
     дели памяти.  Для заданной компиляции определяется только одна из          
     них; остальные (по-определению)  не определяются.  Например, если          
     вы компилируете с малой моделью,  то определяется _SMALL_,  а ос-          
     тальные - нет; поэтому директива #if defined(_SMALL_) будет иметь          
     значение true (истина),  в то время как #if defined(_HUGE_)  (или          
     любая другая)  будет иметь значение false (ложь).  Действительное          
     значение для любого определенного макроса равно 1.                         
                                                                                
     _TINY_       Опция выбора крохотной модели памяти                          
                                                                                
     _SMALL_      Опция выбора малой модели памяти                              
                                                                                
     _MEDIUM_     Опция выбора средней модели памяти                            
                                                                                
     _COMPACT_    Опция выбора компактной модели памяти                         
                                                                                
     _LARGE_      Опция выбора большой модели памяти                            
                                                                                
     _HUGE_       Опция выбора огромной модели памяти                           
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Анахронизмы (K&R 17)                                                  
     -----------------------------------------------------------------          
                                                                                
          Никаких из  упомянутых  в K&R анахронизмов в Турбо Си не су-          
     ществует.                                                                  
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 311,312 -
                                                                                
                           Г Л А В А  12                                        
                           -------------                                        
                                                                                
                   УГЛУБЛЕННЫЙ КУРС ПО ТУРБО СИ                                 
     -----------------------------------------------------------------          
                                                                                
          Рады видеть вас здесь.                                                
                                                                                
          Эта глава охватывает три основные темы. Во-первых, рассказы-          
     вает о моделях памяти - от крохотной до огромной. Далее - о выбо-          
     ре нужной модели, исходя из конкретной задачи. Затем - о  возмож-          
     ности  смешивания языков программирования. Вы с этим уже частично          
     знакомы по Главе 10, которая объясняет связь  Турбо  Си  с  Турбо          
     Прологом.  Здесь же рассматривается смешивание с другими языками,          
     включая Паскаль и ассемблер.  После чего предлагается три аспекта          
     низкоуровневого программирования на Турбо Си:  встроенный код ас-          
     семблера,  псевдопеременные и обработка прерываний.  В заключение          
     рассмотрены  особенности  работы  с  числами  с плавающей точкой.          
     Итак, начнем.                                                              
                                                                                
                                                                                
                                                                                
                                                                                
               Модели памяти                                                    
     -----------------------------------------------------------------          
                                                                                
          Что  такое  модели памяти, почему вас должен беспокоить этот          
     вопрос? Для того чтобы ответить на него, вы  должны  разобраться,          
     на какой вычислительной установке работаете. Ее основной вычисли-          
     тельной  единицей должен быть микропроцессор (CPU), принадлежащий          
     к семейству Intel iAPX86, т.е. 8086, 8088, 80186, 80286. Далее мы          
     будем рассматривать 8086.                                                  
                                                                                
                                                                                
                                                                                
          Регистры микропроцессора 8086.                                        
     -----------------------------------------------------------------          
                                                                                
              Регистры общего назначения                                        
                                                                                
                г=========T=========¬                                           
            AX  ¦   AH    ¦   AL    ¦ Аккумулятор                               
                ¦=========+=========¦                                           
            BX  ¦   BH    ¦   BL    ¦ База                                      
                ¦=========+=========¦                                           

                         - 313,314 -
                                                                                
                                                                                
            CX  ¦   CH    ¦   CL    ¦ Счетчик                                   
                ¦=========+=========¦                                           
            DX  ¦   DH    ¦   DL    ¦ Данные                                    
                L=========¦=========-                                           
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
            Сегментные адресные регистры                                        
                                                                                
                г===================¬                                           
            CS  ¦                   ¦ Сегмент кода                              
                ¦===================¦                                           
            DS  ¦                   ¦ Сегмент данных                            
                ¦===================¦                                           
            SS  ¦                   ¦ Сегмент стека                             
                ¦===================¦                                           
            ES  ¦                   ¦ Дополнительный сегмент                    
                L===================-        (данных)                           
                                                                                
                Cпециальные регистры                                            
                                                                                
                г===================¬                                           
            SP  ¦                   ¦ Указатель стека                           
                ¦===================¦                                           
            BP  ¦                   ¦ Указатель базы                            
                ¦===================¦                                           
            SI  ¦                   ¦ Индекс источника                          
                ¦===================¦                                           
            DI  ¦                   ¦ Индекс получателя                         

                         - 315,316 -
                                                                                
                                                                                
                L===================-                                           
                                                                                
                  Рис. 12.1. Регистры 8086.                                     
                                                                                
          На  рис.12.1 показаны регистры микропроцессора 8086 с крат-           
     ким описанием назначения каждого из них. Есть еще 2 регистра: IP           
     (программный счетчик) и регистр флажков, но Турбо Си не имеет  к           
     ним доступа, поэтому они здесь не показаны.                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
               Регистры общего назначения.                                      
     -----------------------------------------------------------------          
                                                                                
          Регистры общего назначения чаще всего используют для  хране-          
     ния и обработки данных. Каждый из них имеет некоторые специальные          
     функции, которые выполняет только он. Например:                            
                                                                                
          - многие математические операции могут быть выполнены только          
            с использованием AX;                                                
          - BX может быть использован для хранения смещения в  удален-          
            ном указателе (far-указателе);                                      
          - CX используется в некоторых командах  цикла микропроцессо-          
            ра 8086 (LOOP-командах);                                            
          - DX используется определенными командами для хранения  дан-          
            ных.                                                                
                                                                                
          Но имеется много операций,  в которых участвуют все эти  ре-          
     гистры;  в этих случаях вы можете свободно выбирать любой из них.          
                                                                                
                                                                                
                                                                                
                                                                                

                         - 317,318 -
                                                                                
               Сегментные регистры.                                             
     -----------------------------------------------------------------          
                                                                                
          Cегментные регистры  содержат  начальный  адрес каждого из 4          
     сегментов. Как  описано в следующем разделе, для того чтобы полу-          
     чить правильный 20-битный адрес этого сегмента, 16-битная величи-          
     на в сегментном регистре сдвигается влево на 4  бита  (умножается          
     на 16).                                                                    
                                                                                
                                                                                
                                                                                
               Регистры специального назначения.                                
     -----------------------------------------------------------------          
                                                                                
          Микропроцессор 8086  также  имеет несколько регистров специ-          
     ального назначения:                                                        
                                                                                
     - регистры SI и DI могут часто использоваться аналогично  регист-          
       рам общего назначения.  Кроме того, их используют как индексные          
       регистры.  В Турбо Си их также используют как регистровые пере-          
       менные;                                                                  
                                                                                
                                                                                
     - регистр SP указывает на текущую вершину стека и является смеще-          
       нием относительно стекового сегмента;                                    
                                                                                
     - регистр BP является вторичным указателем стека,  обычно исполь-          
       зуется для индексирования при поиске параметров, передаваемых в          
       стеке.                                                                   
                                                                                
          Базовый индексный регистр (BP) используется  в  функциях  Си          
     как базовый адрес для аргументов и автоматических переменных. Па-          
     раметры имеют положительные смещения относительно BP, которые су-          
     щественно зависят от модели памяти и числа регистров, сохраняемых          
     при вызове функциий. BP всегда указывает на сохраненную ранее ве-          
     личину BP. Функции, которые не имеют параметров и не имеют в объ-          
     явлении  аргументов,  не должны вообще использовать или сохранять          
     BP.                                                                        
                                                                                
          Автоматические переменные имеют  отрицательное  смещение  от          
     BP, первая такая переменная имеет наибольшее отрицательное смеще-          
     ние.                                                                       
                                                                                
                                                                                
                                                                                

                         - 319,320 -
                                                                                
               Сегментация памяти.                                              
     ----------------------------------------------------------------           
                                                                                
          Микропроцессор 8086 имеет сегментированную архитектуру памя-          
     ти. Он  обеспечивает общее адресное пространство величиной 1  Мб,          
     однако может прямо адресовать только 64К.  Логическую единицу па-          
     мяти размером 64К называют сегментом,  отсюда и название "сегмен-          
     тированная архитектура памяти".                                            
                                                                                
          Теперь возникают  вопросы:   сколько различных сегментов су-          
     ществует, где они располагаются и как 8086 понимает, где они рас-          
     положены?                                                                  
                                                                                
          -  8086  работает  с 4 различными сегментами: кода, данных,           
     стека и дополнительных данных.Кодовый сегмент содержит  машинную           
     программу; сегмент данных - данные; стековый сегмент - стек; до-           
     полнительный  сегмент  обычно используют для дополнительных дан-           
     ных;                                                                       
                                                                                
          - 8086 имеет 4 16-битных сегментных регистра, называемых CS,          
     DS,  SS и ES; они указывают на сегменты кода, данных, стека и до-          
     полнительных данных соответственно;                                        
                                                                                
                                                                                
          - сегмент может быть расположен  в  любом  месте  памяти.По           
     причине, которая будет рассмотрена далее, сегмент всегда начина-           
     ется с адреса, кратного 16 (в десятичной системе).                         
                                                                                
                                                                                
                                                                                
               Вычисление адреса                                                
     -----------------------------------------------------------------          
                                                                                
          Итак, каким же образом 8086 использует эти сегментные регис-          
     тры для вычисления адреса? Полный  адрес  8086  получает  из  2-х          
     16-битных  величин:  адреса сегмента и смещения. Предположим, что          
     адрес сегмента данных - величина в DS регистре - 2F84  (HEX-код),          
     и  вы  хотите  вычислить реальный адрес некоторых данных, которые          
     имеют смещение 0532 (HEX-код) от начала сегмента данных. Как  это          
     делается?                                                                  
                                                                                
          Вычисление адреса  происходит  следующим  образом:  величина          
     сегментного регистра сдвигается на 4  бита  (1  разряд  HEX-кода)          
     влево  и затем складывается со смещением. Полученная в результате          
     20-битная величина есть реальный адрес данных,  что  иллюстрирует          

                         - 321,322 -
                                                                                
     следующая запись:                                                          
                                                                                
          DS регистр (сдвинутый) 0010 1111 1000 0100 0000 = 2F840               
          смещение                    0000 0101 0011 0010 = 00532               
          адрес                  0010 1111 1101 0111 0010 = 2FD72               
                                                                                
          Начальный  адрес  сегмента  - всегда 20-битное число, однако          
     сегментный регистр содержит только 16 бит. Т.о., младшие  4  бита          
     всегда  предполагаются равными нулю. Это означает, как мы уже от-          
     мечали, что сегменты могут начинаться только с  адреса,  кратного          
     16-и, т.е. с адреса, в котором младшие 4 бита (младшая HEX-цифра)          
     нулевые.                                                                   
                                                                                
          T.о.,  если   DS-регистр  содержит величину 2F84, то сегмент          
     данных реально начинается с адреса 2F840. Как отмечалось, отрезок          
     памяти из 16 байт называется параграфом, т.е. этот сегмент всегда          
     начинается на границе параграфа.  Стандартно адрес представляется          
     в форме "сегмент:смещение".  Например,  рассмотренный выше  адрес          
     будет записан как 2F84:0532. Заметим, что т.к. смещение может ме-          
     няться,  приведенная пара "сегмент:смещение" не единственна.  Все          
     следующие адреса указывают на одну и ту же ячейку памяти:                  
                                                                                
                                                                                
     0000:0123                                                                  
     0002:0103                                                                  
     0008:00А3                                                                  
     0010:0023                                                                  
     0012:0003                                                                  
                                                                                
          И  еще:  сегменты могут перекрываться. Например, все четыре           
     сегмента могут начинаться с одного и того же адреса, что означа-           
     ет, что ваша программа не будет иметь больше чем  64К  адресного           
     пространства, в котором находятся ваша программа, данные и стек.           
                                                                                
                                                                                
                                                                                
               Указатели типа NEAR, FAR И HUGE                                  
     -----------------------------------------------------------------          
                                                                                
          Какие указатели применяют в моделях памяти в Турбо Си? Очень          
     многие. Тип  указателей, используемых для программы и данных, по           
     умолчанию определяется типом модели памяти, выбранной вами. Одна-          
     ко вы можете принудительно объявить указатель (или функцию), име-          
     ющий специфический тип, невзирая на модель, которая используется.          
     Указатели бывают  3-х типов:  ближние или near (16 бит),  дальние          

                         - 323,324 -
                                                                                
     или far (32 бита) и огромные или huge (также 32 бита). Рассмотрим          
     каждый из них.                                                             
                                                                                
                                                                                
                                                                                
               Указатели типа NEAR                                              
     -----------------------------------------------------------------          
                                                                                
          16-битный (near) указатель используется совместно с одним из          
     сегментных  регистров  для вычисления реального адреса. Например,          
     реальный адрес функции получается путем сложения 16-битной  вели-          
     чины указателя и функции с содержимым программного сегмента (CS),          
     сдвинутым  влево.  Аналогично,  near-указатель  к данным содержит          
     смещение относительно адреса в  регистре  сегмента  данных  (DS).          
     Ближние указатели просты в использовании, т.к. все арифметические          
     операции (такие,  как сложение) можно выполнять,  не  заботясь  о          
     сегменте.                                                                  
                                                                                
                                                                                
                                                                                
               Указатели типа FAR                                               
     -----------------------------------------------------------------          
                                                                                
                                                                                
          Указатель типа far (32-битный) содержит не только смещение в          
     сегменте, а также (как другую 16-битную величину) адрес сегмента,          
     который  затем сдвигается влево и добавляется к смещению. Исполь-          
     зуя far-указатели, вы можете иметь множественные программные сег-          
     менты, что позволяет создавать программы величиной более 64К. Та-          
     ким же образом far-указатели могут адресовать данные,  занимающие          
     более 64К памяти.                                                          
                                                                                
          Когда вы используете far-указатели к данным, вы должны знать          
     о потенциальных проблемах в манипулировании указателями. Как объ-          
     яснялось в разделе "Вычисление адреса",  вы  можете  иметь  много          
     различных  пар  "сегмент:смещение",  указывающих на один и тот же          
     адрес.  Например,  все  far-  указатели  0000:0120,  0010:0020  и          
     0012:0000 указывают на один и тот же 20-битный адрес. Однако, ес-          
     ли вы имеете 3 различных переменных far-указателя - a, b и c, со-          
     держащие эти 3 величины соответственно,  то все следующие выраже-          
     ния будут работать неправильно:                                            
                                                                                
     if (a==b) ...                                                              
     if (b==c) ...                                                              
     if (a==c) ...                                                              

                         - 325,326 -
                                                                                
                                                                                
          Указанная проблема появляется, когда вы хотите сравнить far-          
     указатели, используя операторы >, >=, <, <=. В этих случаях толь-          
     ко смещение (как беззнаковое) может использоваться в целях  срав-          
     нения.  Предположим, что а, b и c имеют значения, описанные выше;          
     тогда следующие выражения будут верны:                                     
                                                                                
     if (a>b) ...                                                               
     if (b>c) ...                                                               
     if (a>c) ...                                                               
                                                                                
          Операторы равенства  (==)   и  неравенства  (!=)  используют          
     32-битную величину как unsigned long (беззнаковую длину),а не как          
     полный адрес памяти. Операторы сравнения ( >, >=, < и <=) исполь-          
     зуют только смещение.                                                      
                                                                                
          Для операторов == и != необходимы все 32 бита для того, что-          
     бы   можно   было   произвести   сравнение   с   NULL  указателем          
     (0000:0000).В противном случае, если для  проверки  равенства  вы          
     используете только смещение, то любой указатель со смещением 0000          
     будет равен NULL указателю, а это не является тем, что вы хотите.          
                                                                                
                                                                                
          Еще  вам необходимо знать следующее. Если вы прибавляете ве-          
     личину к far-указателю,  то изменяется только смещение.  Если ре-          
     зультат сложения превышает величину FFFF (это максимально возмож-          
     ная величина смещения),  то указатель делает циклический  переход          
     назад, к началу сегмента.  Например,  результатом прибавления 1 к          
     5031:FFFF, будет 5031:0000 (а не 6031:0000).  Аналогично, если вы          
     вычитаете 1 из 5031:0000, то получите 5031:FFFF (а не 5030:000F).          
                                                                                
          Если вы хотите сравнить указатели,  то надежнее использовать          
     или near-указатели, которые всегда используют один и тот же адрес          
     сегмента, или huge-указатели, описание которых следует далее.              
                                                                                
                                                                                
                                                                                
               Указатели типа HUGE                                              
     -----------------------------------------------------------------          
                                                                                
          Указатели типа huge (также как и far)  имеют 32-битную длину          
     и содержат как адрес сегмента,  так и смещение. Однако, в отличие          
     от far,  они нормализованы с целью решения проблем, рассмотренных          
     в предыдущем разделе.                                                      
                                                                                

                         - 327,328 -
                                                                                
          Что такое указатель типа huge? Это 32-битный указатель,  ко-          
     торый имеет наибольшую из возможных величину адреса сегмента. Так          
     как сегмент всегда начинается с адреса, кратного 16 (или 10h), то          
     это  означает,  что  смещение может принимать значения от 0 до 15          
     (от 0h до Fh).                                                             
                                                                                
          Как нормализовать указатель? Очень просто: переведите его  в          
     20-битный  адрес,  затем используйте правые 4 бита для смещения и          
     левые  16  бит  для  адреса  сегмента.  Например,  дан  указатель          
     2F84:0532; мы переведем его в абсолютный адрес 2FD72, который за-          
     тем нормализуется в 2FD7:0002. Приведем несколько указателей и их          
     нормализованные значения:                                                  
                                                                                
     0000:0123    0012:0003                                                     
     0040:0056    0045:0006                                                     
     500D:9407    594D:0007                                                     
     7418:D03F    811B:000F                                                     
                                                                                
          Теперь  вы  знаете,  что указатели типа huge всегда хранятся          
     нормализованными. Почему это важно? Потому, что для любого данно-          
     го адреса  памяти  имеется  только  один возможный указатель типа          
     huge - пара "сегмент:смещение".  Это означает,  что операторы  >,          
                                                                                
     >=, <, <= дадут правильный ответ для любого указателя типа huge.           
                                                                                
          Дополнительно к этому, все операторы >, >=, <, <= используют          
     полную 32-битную величину указателя типа huge.  Нормализация  га-          
     рантирует, что их результат всегда будет корректным.                       
                                                                                
          И, наконец, благодаря нормализации, смещение в указателе ти-          
     па huge автоматически делает циклический переход,  при достижении          
     величины 16,  но,  в отличие от far, сегмент также увеличивается.          
     Например,  если вы увеличиваете  811B:000F,  то  результат  будет          
     811C:0000;  аналогично, если вы уменьшаете 811C:0000, то получите          
     811B:000F. Эта особенность указателей типа huge позволяет манипу-          
     лировать структурами данных свыше 64К.                                     
                                                                                
          Однако при использовании указателей типа huge возрастают до-          
     полнительные расходы. Арифметические операции над указателями ти-          
     па huge выполняются с вызовом специальных подпрограмм,  и поэтому          
     они выполняются гораздо медленнее,  чем над указателями типа near          
     и far.                                                                     
                                                                                
                                                                                
                                                                                

                         - 329,330 -
                                                                                
               Шесть моделей памяти в Турбо Си                                  
     -----------------------------------------------------------------          
                                                                                
          Турбо Си позволяет вам избежать накладных расходов, исключая          
     те случаи,  когда вы сами этого не хотите.  Существует шесть раз-          
     личных моделей памяти, которые вы можете выбирать: крохотная (на-          
     именьшая),  малая, средняя, компактная, большая и огромная. Какую          
     из них выбрать, зависит от ваших требований. Вот краткая характе-          
     ристика каждой из них:                                                     
                                                                                
          Крохотная                                                             
          ---------                                                             
          Как вы  можете предположить,  это самая маленькая из моделей          
     памяти. Все четыре сегментных регистра (DS,CS,SS,ES) указывают на          
     один  и тот же адрес,  поэтому вы имеете всего 64К для всех прог-          
     рамм,  данных и  массивов.  В  этом  случае  используются  только          
     near-указатели.  Используйте  эту  модель,  когда у вас маленькая          
     оперативная память.  Программы с крохотной моделью  памяти  могут          
     быть переведены в .COM формат.                                             
                                                                                
          Малая                                                                 
          -----                                                                 
                                                                                
          Программный сегмент  и сегмент данных различны и не перекры-          
     ваются, поэтому у вас есть 64К для программ и 64К для статических          
     данных. Сегменты стека и дополнительные сегменты данных начинают-          
     ся с того же адреса,  что и сегмент данных. В этом случае исполь-          
     зуются только near-указатели.  Это наилучшая модель для большинс-          
     тва реализаций.                                                            
                                                                                
          Средняя                                                               
          -------                                                               
          Far-указатели используются для программ, но не для данных. В          
     результате статические данные ограничены 64К,  но программа может          
     иметь величину до 1М.  Эта модель является наилучшей для  больших          
     программ, которые не хранят в памяти больших обьемов данных.               
                                                                                
          Компактная                                                            
          ----------                                                            
          Прямо противоположна средней, т.е. far-указатели используют-          
     ся для данных,  а не для программ. Программы ограничиваются вели-          
     чиной в 64К,  а данные (но не статические) могут быть до 1Мб. Эта          
     модель наиболее удобна, если ваша программа маленькая, но вам не-          
     обходимо адресовать большие объемы данных.                                 
                                                                                

                         - 331,332 -
                                                                                
                                                                                
          Большая                                                               
          -------                                                               
          Far-указатели используются как для программы, так и для дан-          
     ных. И программа, и данные занимают область до 1М; эта модель не-          
     обходима только для очень больших программных продуктов.                   
                                                                                
          Огромная                                                              
          --------                                                              
          Far-указатели используются для программы и для данных. Турбо          
     Cи обычно ограничивает величину статических данных до 64К; огром-          
     ная  модель памяти снимает это ограничение,  позволяя статическим          
     данным занимать пространство более 64К.                                    
                                                                                
          Иллюстрации (Рис.  12.2-12.7) показывают, как в микропроцес-          
     соре 8086 реализуется 6 моделей памяти Турбо Си.                           
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
            Сегментные                                   Размер                 
             регистры:                     Нач.адрес.   сегмента:               
         CS,DS,SS -->г==============================¬                           
                   г ¦_TEXT class 'CODE'Программа   ¦ ¬                         
                   ¦ ¦==============================¦ ¦                         
           DGROUP =¦ ¦_DATA class 'DATA'иниц.данные ¦ ¦                         
                   ¦ ¦==============================¦ ¦                         
                   L ¦_BSS class 'BSS'не иниц,данные¦ ¦                         
                     ¦==============================¦ ¦= до 64К                 
                     ¦              ХИП             ¦ ¦                         
                     ¦==============================¦ ¦                         
                     ¦      Свободная область       ¦ ¦                         
          SР(ТOS) -->¦==============================¦ ¦                         
      Стартовый SP-->¦             СТЕК             ¦ -                         
                     L==============================-                           
                                        Конеч.адрес.                            
                                                                                
             Рис. 12.2. Крохотная модель сегментации памяти                     
                                                                                
                                                                                
                                                                                
                                                                                

                         - 333,334 -
                                                                                
            Сегментные                                  Размер                  
             регистры:                                 сегмента:                
                                           Нач.адрес.                           
               CS -->г==============================¬                           
                     ¦_TEXT class 'CODE' программа  ¦ до 64К                    
            DS,SS -->¦==============================¦                           
                   г ¦_DATA class 'DATA'иниц.данные ¦ ¬                         
           DGROUР =¦ ¦==============================¦ ¦                         
                   L ¦_BSS class 'BSS'не иниц.данные¦ ¦                         
                     ¦==============================¦ ¦                         
                     ¦              ХИП             ¦ ¦= до 64К                 
                     ¦==============================¦ ¦                         
                     ¦      Свободная область       ¦ ¦                         
          SP(TOS) -->¦==============================¦ ¦                         
                     ¦             СТЕК             ¦ -                         
      Стартовый SP-->¦==============================¦ до конца                  
                     ¦           FAR ХИП            ¦ памяти                    
                     ¦==============================¦                           
                     ¦      Свободная область       ¦                           
                     L==============================-                           
                                        Конеч.адрес.                            
                                                                                
                                                                                
                                                                                
             Рис.12.3. Малая модель сегментации памяти                          
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 335,336 -
                                                                                
                              Вложенные                                         
                               sfiles                                           
                             -----------¬(CS-указатель одновременно             
                             ¦sfileA    ¦только на 1 sfile)                     
                       CS -->¦sfileB    ¦                                       
                           --+...       ¦                                       
            Сегментные     ¦ ¦sfileZ    ¦                Размер                 
             регистры:     ¦ L-----------  Нач.адрес.   сегмента:               
               CS -->г=====¦==========================¬                         
                     ¦-----+¬                         ¦каждый sfile             
                     ¦¦sfile¦_TEXTclass'CODE'программа¦до 64К                   
                     ¦L------                         ¦                         
            DS,SS -->¦================================¦                         
                   г ¦_DATA class 'DATA' иниц.данные  ¦ ¬                       
                   ¦ ¦================================¦ ¦                       
                   ¦ ¦_BSS class 'BSS' не иниц.данные ¦ ¦                       
                   ¦ ¦================================¦ ¦                       
           DGROUР =¦ ¦              ХИП               ¦ ¦= до 64К               
                   ¦ ¦================================¦ ¦                       
                   ¦ ¦      Свободная область         ¦ ¦                       
           SP(TOS)-->¦================================¦ ¦                       
                   L ¦             СТЕК               ¦ -                       
                                                                                
                                                                                
      Стартовый SP-->¦================================¦ до конца                
                     ¦            FAR ХИП             ¦  памяти                 
                     ¦================================¦                         
                     ¦      Свободная область         ¦                         
                     L================================-                         
                                           Конеч.адрес.                         
                                                                                
                                                                                
              Рис. 12.4. Средняя модель сегментации памяти                      
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 337,338 -
                                                                                
            Сегментные                                Размер                    
              регистры:                              сегмента:                  
                                          Нач.адрес.                            
            CS,DS -->г==============================¬                           
                     ¦_TEXT class 'CODE'Программа   ¦ до 64К                    
                     ¦==============================¦                           
                   г ¦_DATA class 'DATA'иниц.данные ¦ ¬                         
           DGROUP =¦ ¦==============================¦ ¦= до 64К                 
                   L ¦_BSS CLASS 'BSS' 'иниц.данные ¦ -                         
               SS -->¦==============================¦                           
                     ¦      Свободная область       ¦                           
           SP(TOS)-->¦==============================¦                           
                     ¦            СТЕК              ¦ до 64К                    
      Стартовый SP-->¦==============================¦ до конца                  
                     ¦             ХИП              ¦ памяти                    
                     ¦==============================¦                           
                     ¦      Свободная область       ¦                           
                     L==============================-                           
                                         Конеч.адрес.                           
                                                                                
                                                                                
             Рис.12.5. Компактная модель сегментации памяти                     
                                                                                
                              Вложенные                                         
                               sfiles                                           
                             -----------¬(CS-указатель одновременно             
                             ¦sfileA    ¦ только на 1 sfile)                    
                       CS -->¦sfileB    ¦                                       
                           --+...       ¦                                       
       Сегментные          ¦ ¦sfileZ    ¦                  Размер               
        регистры:          ¦ L-----------  Нач.адрес.     сегмента:             
               CS -->г=====¦==========================¬                         
                     ¦-----+¬                         ¦ Каждый sfile            
                     ¦¦sfile¦_TEXTclass'CODE'программа¦ до 64К                  
                     ¦L------                         ¦                         
               DS -->¦================================¦                         
                   г ¦_DATA class 'DATA'иниц.данные   ¦ ¬                       
           DGROUP =¦ ¦================================¦ ¦ до 64К                
                   L ¦_BSS class 'BSS'не иниц.данные  ¦ -                       
               SS -->¦================================¦                         
                     ¦      Свободная область         ¦                         
           SP(TOS)-->¦================================¦                         
                     ¦              СТЕК              ¦ до 64К                  
      Стартовый SP-->¦================================¦                         
                     ¦              ХИП               ¦ до конца                

                         - 339,340 -
                                                                                
                                                                                
                     ¦================================¦ памяти                  
                     ¦      Свободная область         ¦                         
                     L================================-                         
                                            Конеч.адрес.                        
                                                                                
                                                                                
             Рис.12.6. Большая модель сегментации памяти                        
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                              Вложенные                                         
                               sfiles                                           
                             -----------¬(CS-указатель одновременно             
                             ¦sfileA    ¦ только на 1 sfile)                    
                        CS-->¦sfileB    ¦                                       
                             ¦...       ¦                                       
      Сегментные           --+sfileZ    ¦                Размер                 
       регистры:           ¦ L-----------               сегмента:               
                           ¦                Нач.адрес.                          
               CS -->г=====¦==========================¬                         
                     ¦-----+¬                         ¦ каждый sfile            
                     ¦¦sfile¦_TEXTclass'CODE'программа¦ до 64К                  
                     ¦L------                         ¦                         
               DS -->¦================================¦                         
                     ¦------¬                         ¦ каждый sfile            
                     ¦¦sfile¦_DATAclass'DATA'иниц.дан.¦ до 64К                  
                     ¦LT-----                         ¦                         
               SS -->¦=¦==============================¦                         
                     ¦ ¦   Свободная область          ¦                         
           SP(TOS)-->¦=¦==============================¦                         
                     ¦ ¦           СТЕК               ¦ до 64К                  
      Стартовый SP-->¦=¦==============================¦ до конца                

                         - 341,342 -
                                                                                
                     ¦ ¦            ХИП               ¦ памяти                  
                     ¦=¦==============================¦                         
                     ¦ ¦   Свободная область          ¦                         
                     L=¦==============================-                         
                       ¦ -----------¬      Конеч.адрес.                         
                       L-+sfileA    ¦                                           
                   DS -->¦sfileB    ¦(DS-указатель одновременно                 
                         ¦...       ¦ только на 1 sfile)                        
                         ¦sfileZ    ¦                                           
                         L-----------                                           
                          Вложенные                                             
                           sfiles                                               
                                                                                
                                                                                
             Рис.12.7. Огромная модель сегментации памяти                       
                                                                                
          Таблица 12.1 обобщает информацию о различных моделях и пока-          
     зывает их в сравнении.  Модели группируют в зависимости от  того,          
     какова  у них величина программы и данных,  т.е.  маленькая (64К)          
     или большая (1М).  Эти группы соответствуют колонкам и столбцам в          
     таблице 12.1. Так, например, крохотную, малую и компактную модели          
     называют "моделями с малыми программами" потому,  что, по умолча-          
                                                                                
                                                                                
     нию,  указатели  в таких программах только near.  Аналогично ком-          
     пактную,  большую и огромную модели называют "моделями с большими          
     данными"  потому,  что,  по  умолчанию,  указатели к данным в них          
     только far.  Заметим, что это также верно для огромной модели: по          
     умолчанию,  указатели  к данным только far,  а не нормализованные          
     (huge).  Если вы хотите использовать huge-указатели к данным,  то          
     вы должны предварительно объявить их как huge.                             
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 343,344 -
                                                                                
                                                                                
     =================================================================          
     РАЗМЕР ДАННЫХ                  РАЗМЕР ПРОГРАММЫ                            
                   ===================================================          
                           64К                        1М                        
     =================================================================          
                                                                                
         64К       Крохотная(данные и програм-                                  
                   ма перекрываются;                                            
                   общий размер 64К)                                            
                                                                                
                   Малая (не перекрываются;   Средняя                           
                   общий размер 128К)         (малые данные,большая             
                                              программа)                        
                                                                                
         1М        Компактная                 Большая                           
                   (большие данные,малая      (большие данные и                 
                   программа)                 программы)                        
                                                                                
                                              Огромные     (аналогично          
                                              большой,  только  данные          
                                              больше 64К )                      
                                                                                
                                                                                
     =================================================================          
                                                                                
                      Таблица 12.1. Модели памяти.                              
                                                                                
          Важное замечание. Когда вы компилируете модуль (указывая ис-          
     ходный файл,  содержащий несколько функций), результирующая прог-          
     рамма не может быть более 64К, т.к. все функции должны включаться          
     в один программный сегмент.  Это верно,  даже если вы используете          
     модели с большими программами (среднюю, большую и огромную). Если          
     ваш модуль больше величины одного программного сегмента (64К), то          
     вы должны разделить его на разные исходные программные файлы, от-          
     компилировать  каждый  файл  в  отдельности и затем объединить их          
     вместе.  Проще говоря, хотя огромная модель и позволяет статичес-          
     ким  данным занимать объем больше,  чем 64К,  в каждом модуле все          
     равно должно быть меньше, чем 64К.                                         
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 345,346 -
                                                                                
              Порядок программирования смешанных моделей памяти:                
              модификация типа адресации                                        
     -----------------------------------------------------------------          
                                                                                
          Турбо Си вводит семь новых ключевых  слов,  отсутствующих  в          
     стандартном (Керниган и Ритчи или ANSI) Си: near, far, huge, _cs,          
     _ds,  _es, _ss. С некоторыми ограничениями и предупреждениями они          
     могут  использоваться  как  модификаторы  указателей (в отдельных          
     случаях функций).                                                          
                                                                                
          В Турбо Си вы можете модифицировать функции и указатели, ис-          
     пользуя ключевые слова near,  far или huge.  Ранее в этом разделе          
     мы объясняли суть near-,  far- и huge- указателей к данным. Near-          
     функции  вызываются  ближними  запросами и заканчиваются ближними          
     возвратами. Far-функции,  соответственно,  вызываются удаленно  и          
     завершаются удаленными возвратами. Huge-функции подобны far-функ-          
     циям,  но,  в отличие от них,  huge могут изменять содержимое ре-          
     гистра DS.                                                                 
                                                                                
          Есть также  четыре специальных near-указателя к данным: _cs,          
     _ds, _es, _ss. Это 16-битные указатели, которые специфически свя-          
     заны с соответствующими сегментными регистрами. Например, если вы          
                                                                                
                                                                                
     объявите указатель                                                         
                                                                                
          char _ss *p ;                                                         
                                                                                
     то p будет содержать 16-битное смещение в стековом сегменте.               
                                                                                
          Функции и указатели в программе по умолчанию устанавливаются          
     near или far в зависимости от выбранной вами модели памяти.  Если          
     функция или указатель near, то она автоматически связывается либо          
     с CS, либо с DS регистром.                                                 
                                                                                
         Таблица 12.2 показывает, как это происходит. Заметим, что ве-          
     личина  указателя  соответствует  или  работе в рамках 64К (near,          
     внутри сегмента), или работе в адресном пространстве до 1М  (far,          
     имеет свой собственный адрес сегмента)                                     
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 347,348 -
                                                                                
                                                                                
                                                                                
        ==========================================================              
        Модель памяти     Указатель к функции   Указатель к данным              
        ==========================================================              
                                                                                
        Крохотная          near,_cs             near,_ds                        
        Малая              near,_cs             near,_ds                        
        Компактная         far                  near,_ds                        
        Средняя            near,_cs             far                             
        Большая            far                  far                             
        Огромная           far                  far                             
        ==========================================================              
                                                                                
                   Таблица 12.2. Результирующие указатели                       
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
               Объявление функций как NEAR или FAR                              
     -----------------------------------------------------------------          
                                                                                
         Иногда вам может захотеть  (или  понадобиться)  отменить  тип          
     функции,  принимаемый по умолчанию для вашей модели памяти, пока-          
     занной в таблице  12.1.  Например,  предположим,  вы  используете          
     большую  модель  памяти,  но имеете в вашей программе рекурсивную          
     (самовызывающуюся) функцию, такую как                                      
                                                                                
     double power (double x, int exp)                                           
     {                                                                          
          if (exp <= 0)                                                         
            return(0);                                                          
          else                                                                  
            return(x*power(x,exp-1));                                           
     }                                                                          
                                                                                
          Power  постоянно  вызывает сама себя. Это делается с помощью          
     удаленного вызова, что требует большего  размера  стека  и  более          
     длительных  циклов. Объявив power как near, вы  снимете некоторые          
     расходы, заставляя все вызовы к этой функции быть ближними:                
                                                                                

                         - 349,350 -
                                                                                
          double near power (double x, int exp)                                 
                                                                                
          Такое описание гарантирует, что power будет вызываема только          
     из того программного сегмента,  в котором она оттранслирована,  и          
     все ее вызовы будут near.                                                  
                                                                                
          Это означает,  что  если  вы используете большую программную          
     модель (среднюю,  большую или огромную), вы можете вызывать power          
     только в том модуле, где она определена. Другие модули имеют свои          
     собственные программные сегменты и, следовательно, не могут вызы-          
     вать  near-функции  в  других модулях.  Более того,  near-функция          
     должна быть определена или объявлена до первого ее использования,          
     иначе  компилятор  не сможет понять,  что необходимо генерировать          
     near-вызов.                                                                
                                                                                
          Наоборот, объявление функции far означает, что и возврат бу-          
     дет far. В малых программных моделях far-функции должны быть объ-          
     явлены или определены до первого их использования,  что обеспечи-          
     вает их запуск через far-вызов.                                            
                                                                                
          Вернемся к примеру с power.  Теперь понятно также, что необ-          
     ходимо объявить power как статическую,  т.к. она будет вызываться          
                                                                                
                                                                                
     только внутри текущего модуля. В этом случае, если она будет ста-          
     тической, ее имя станет недоступно любой функции, находящейся вне          
     модуля.  Так как power всегда задействует фиксированное число ар-          
     гументов,  вы можете еще больше ее оптимизировать, объявив ее как          
     pascal, а именно:                                                          
                                                                                
         static double near pascal power (double x, int exp)                    
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 351,352 -
                                                                                
               Объявление указателей как NEAR, FAR или HUGE                     
     -----------------------------------------------------------------          
                                                                                
          Вы уже видели,  зачем вам может понадобиться объявлять функ-          
     ции для различных моделей памяти,  применяемых в программе. А за-          
     чем может понадобиться делать то же самое с указателями?  По при-          
     чинам,  что  уже  описанны  выше:  во  избежание  необоснованного          
     расширения (объявив near, когда по умолчанию должно быть far) или          
     чтобы  обратиться к чему-либо,  находящемуся вне допустимого сег-          
     мента (объявив far или  huge,  когда  по  умолчанию  должно  быть          
     near).                                                                     
                                                                                
          Встречаются, конечно,  потенциальные  ловушки  в  присвоении          
     функциям и указателям типов,  отличных от принятых по  умолчанию.          
     Например,  предположим, вы имеете следующую малую модель програм-          
     мы:                                                                        
                                                                                
     void myputs(s)                                                             
     char *s;                                                                   
     {                                                                          
          int i;                                                                
          for (i = 0; s[i] != 0; putc(s[i]);                                    
                                                                                
     }                                                                          
                                                                                
     main()                                                                     
     {                                                                          
          char near *mystr;                                                     
                                                                                
          mystr = "Hello, world\n";                                             
          myputs(mystr);                                                        
     {                                                                          
                                                                                
          Эта программа  работает  прекрасно,   а объявление mystr как          
     near излишне,  т.к.  все указатели,  как программы, так и данных,          
     будут near.                                                                
                                                                                
          Но что  будет,  если вы перекомпилируете эту программу,  ис-          
     пользуя компактную (или большую,  или  огромную)  модель  памяти?          
     Указатель mystr в main будет все еще near (это по-прежнему 16-би-          
     товый указатель).  Однако указатель s в myputs теперь  far,  т.к.          
     это устанавливается по умолчанию.  Это означает, что myputs будет          
     выбирать 2 слова из стека,  создавая far-указатель,  и полученный          
     адрес будет, несомненно, другим, чем у mystr.                              
                                                                                

                         - 353,354 -
                                                                                
          Как вам решить эту проблему? Решается она следующим образом:          
     myputs объявляется в стиле современного Си, т.е.:                          
                                                                                
     void myputs(char *s);                                                      
     {                                                                          
          /*body of myputs*/                                                    
     }                                                                          
                                                                                
          Теперь, когда  Турбо Си компилирует вашу программу,  ему из-          
     вестно, что  myputs предполагает указатель на char,  и,  т.к.  вы          
     компилируете в рамках большой модели,  указатель должен быть far.          
     Поэтому Турбо Си будет помещать регистр сегмента данных  (DS)   в          
     стек совместно  с 16-битной величиной mystr,  формируя far-указа-          
     тель.                                                                      
                                                                                
          Как быть в обратном случае:  параметры для myputs  объявлены          
     как far и компилируются для малой модели памяти?  Без использова-          
     ния прототипа функции вы столкнетесь с проблемами,  т.к. main бу-          
     дет помещать в стек как смещение, так и адрес сегмента, но myputs          
     предполагает только смещение.  При определении прототипа  функции          
     main будет помещать в стек только смещение.                                
                                                                                
                                                                                
                                                                                
          Вывод: если  вы  собираетесь  использовать  явное объявление          
     указателей как near или far,  то надежнее всего использовать про-          
     тотипы функций для любых функций,  где вы планируете их использо-          
     вать.                                                                      
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 355,356 -
                                                                                
                                                                                
          Способ указания на данный сегмент: Offset адрес (смещение)            
     -----------------------------------------------------------------          
                                                                                
          Каким образом вы можете указатель типа far разместить в дан-          
     ной ячейке памяти (специальном сегменте - offset адресе)?  Вы мо-          
     жете воспользоваться встроенной библиотечной подпрограммой MK_FP,          
     которая  выбирает сегмент и смещение и возвращает far- указатель.          
     Например:                                                                  
                                                                                
          MK_FP(segment_value, offset_value)                                    
                                                                                
          Данный far-указатель (fp) может быть и сегментом FP_SEG(fp),          
     и смещением FP_OFF(fp). Для более детального ознакомления с этими          
     тремя библиотечными подпрограммами Турбо Си следует  обратиться к          
     "Справочному Руководству по Турбо Си".                                     
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
           Построение простых операторов объявления                             
     -----------------------------------------------------------------          
                                                                                
          Операторы объявления в Си вы используете для объявления  ти-          
     пов функций,  переменных,  указателей и данных.  Си позволяет вам          
     строить комплексные операторы объявления.  В этом разделе  даются          
     некоторые примеры операторов объявления, которые могут помочь вам          
     в их построении (и прочтении); будет также показано, как избежать          
     некоторые ловушки.                                                         
                                                                                
          Традиционно программирование  на  Си  дает возможность сразу          
     строить полный оператор объявления,  вставляя  при  необходимости          
     определения.  К сожалению, это делает программы сложными для чте-          
     ния (и написания).                                                         
                                                                                
          Например, рассматриваемые в таблице 12.3 операторы  объявле-          
     ния предполагают, что вы компилируете в рамках малой модели памя-          
     ти (малая программа, малые данные).                                        
                                                                                
     =================================================================          
          int f1();                      Функция, возвращающая целое.           
                                                                                

                         - 357,358 -
                                                                                
          int *p1;                       Указатель на целое.                    
                                                                                
          int *f2();                     Функция, возвращающая указа-           
                                         тель на целое.                         
                                                                                
          int far *p2;                   Far- указатель на целое.               
                                                                                
          int far *f3();                 Near- функция, возвращающая            
                                         far- указатель на целое.               
                                                                                
          int *  far  f4();              Far-функция,  возвращающая             
                                         near-указатель на целое.               
                                                                                
          int (*fp1) (int);              Указатель на функцию, возвра-          
                                         щающую целое   и  принимающую          
                                         целое.                                 
                                                                                
          int (*fp2) (int *ip);          Указатель на функцию, возвра-          
                                         щающую  целое  и  принимающую          
                                         указатель на целое.                    
                                                                                
          int (far *fp3)(int far *ip)    Far-указатель на функцию,воз-          
                                                                                
                                         вращающую целое и принимающую          
                                         far-указатель на целое.                
                                                                                
                                                                                
          int (far *list[5]) (int far *ip);                                     
                                         Массив из 5 far-указателей на          
                                         функции, возвращающие целое и          
                                         принимающие far- указатели на          
                                         целое.                                 
                                                                                
          int (far *gopher(int (far *fp[5])(int far *ip)))(int far *ip);        
                                                                                
                                         Near-функция, принимающая мас-         
                                         сив из 5 far-указателей на фу-         
                                         нкции,  возвращающие  целое  и         
                                         принимающие  far- указатели на         
                                         целое, и  возвращающая один из         
                                         этих указателей                        
     ==================================================================         
                                                                                
               Таблица 12.3. Объявление указателей без typedef                  
                                                                                

                         - 359,360 -
                                                                                
          Здесь представлены все допустимые операторы объявления в по-          
     рядке возрастания трудности их восприятия.  Однако,  благоразумно          
     используя  typedef,  вы  можете  усилить четкость восприятия этих          
     операторов.                                                                
                                                                                
          Ниже представлены те же операторы, записанные с  использова-          
     нием typedef.                                                              
                                                                                
     =================================================================          
          int (f1);                      Функция, возвращающая целое.           
                                                                                
          typedef int *intptr;                                                  
          intptr p1;                     Указатель на целое.                    
          intptr f2();                   Функция, возвращающая указа-           
                                         тель на целое.                         
                                                                                
          typedef int far *farptr;                                              
          farptr p2;                     Far-указатель на целое.                
          farptr f3();                   Near-функция,  возвращающая            
                                         far-указатель на целое.                
          intptr far f4();               Far-функция,  возвращающая             
                                         near-указатель на целое.               
                                                                                
                                                                                
          typedef int (*fncptr1)(int);                                          
          fncptr1 fp1;                   Указатель  на  функцию, воз-           
                                         вращающую целое и  принимаю-           
                                         щую целое.                             
                                                                                
          typedef int (*fncptr2)(intptr);                                       
          fncptr2 fp2;                   Указатель  на  функцию, воз-           
                                         вращающую  целое и принимаю-           
                                         щую  указатель  на  целое.             
                                                                                
          typedef int (far *ffptr)(far ptr);                                    
          ffptr fp3;                     Far - указатель  на  функцию,          
                                         вовращающую целое и  принима-          
                                         ющую far-указатель на целое.           
                                                                                
          typedef ffptr ffplist[5];                                             
          ffplist list;                  Массив  из  5 far- указателей          
                                         на  функции, возвращающие це-          
                                         лое и принимающие  far-указа-          
                                         ли на целое.                           
                                                                                

                         - 361,362 -
                                                                                
                                                                                
          ffptr gopher(ffplist);         Near-функция,принимающая мас-          
                                         сив  из 5  far-указателей  на          
                                         функции, возвращающие целое и          
                                         принимающие far-указатели  на          
                                         целое, и возвращающая один из          
                                         этих указателей.                       
     =================================================================          
                                                                                
            Таблица 12.4. Объявление указателей при помощи typedef              
                                                                                
          Как видите,  есть  значительная разница в наглядности и чет-          
     кости восприятия между объявлением gopher при  помощи  typedef  и          
     предыдущим способом объявления.  При грамотном использовании опе-          
     раторов типа typedef и прототипов функций вам легче будет состав-          
     лять, отлаживать и сопровождать ваши программы.                            
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                    Использование библиотечных файлов                           
     ----------------------------------------------------------------           
                                                                                
          Турбо Си предлагает версию стандартных библиотечных подпрог-          
     рамм для каждой из шести моделей памяти.  Применяясь для работы в          
     интегрированной среде (TC), Турбо Си достаточно хорошо приспособ-          
     лен для компоновки требуемых библиотек в нужном порядке в зависи-          
     мости от выбранной вами модели.  Кроме того,  будучи применен как          
     автономный  компилятор (TCC),  Турбо Си достаточно хорошо приспо-          
     соблен для автоматической компоновки.                                      
                                                                                
          Однако, если вы используете непосредственно TLINK (компонов-          
     щик Турбо  Си) как автономный редактор связей,  то вам необходимо          
     точно определить, какие библиотеки использовать. Если вы не соби-          
     раетесь  использовать все 6 моделей памяти,  то вам нужно перепи-          
     сать на рабочий или жесткий диск лишь файлы  используемой  модели          
     (моделей).                                                                 
                                                                                
                                                                                
                                                                                
                                                                                

                         - 363,364 -
                                                                                
                                                                                
          Ниже приведен список библиотечных файлов для  каждой  модели          
     памяти.                                                                    
                                                                                
     =================================================================          
          Крохотная              COT.OBJ, MATHS.LIB, CS.LIB                     
          Малая                  COS.OBJ, MATHS.LIB, CS.LIB                     
          Компактная             COС.OBJ, MATHC.LIB, CC.LIB                     
          Средняя                COM.OBJ, MATHM.LIB, CM.LIB                     
          Большая                COL.OBJ, MATHL.LIB, CL.LIB                     
          Огромная               COH.OBJ, MATHH.LIB, CH.LIB                     
     =================================================================          
                                                                                
          Заметим, что крохотная и малая модели используют одни  и  те          
     же  библиотеки,  но  имеют  разные  стартовые  файлы  (COT.OBJ  и          
     COS.OBJ). Кроме того, если ваша система имеет математический соп-          
     роцессор 8087/80287, то вам понадобится файл FP87.LIB; если же вы          
     хотите эмулировать 8087/80287, то вам нужен файл EMU.LIB.                  
                                                                                
          Приведем несколько примеров командных строк TLINK:                    
                                                                                
          tlink c0m a b c, prog, mprog,fp87 mathm cm                            
                                                                                
          tlink c0c d e f, plan, mplan, emu mathc cc                            
                                                                                
          Первая из  командных  строк выполнит программу,  имя которой          
     PROG.EXE, включающую в себя библиотеки средней модели и скомпоно-          
     ванную  в  ней  библиотеку  поддержки 8087/80287.  Вторая вызовет          
     программу PLAN.EXE,  скомпилированную в  виде  компактной  модели          
     программы,  эмулирующей  подпрограммы 8087/80287 с плавающей точ-          
     кой, если нельзя воспользоваться сопроцессором.                            
                                                                                
          Замечание: Порядок элементов в командной строке очень важен.          
     Модуль  (C0x.OBJ)  всегда  должен стоять на первом месте.  Список          
     включенных библиотек должен иметь установленный порядок:                   
                                                                                
           - ваши собственные библиотеки (если они имеются);                    
                                                                                
           - FP87.LIB или EMU.LIB,  следующие за MATHx.LIB (необходимо          
             только при использовании плавающей точки);                         
                                                                                
           - Cx.LIB  (стандартный файл библиотеки поддержки Турбо Си).          
     (Буква x в модулях C0x, MATHx и Cx заменяется на букву, определя-          
     ющую модель памяти: t,s,m,c,l или h).                                      
                                                                                

                         - 365,366 -
                                                                                
                                                                                
                                                                                
               Компоновка смешанных модулей                                     
     -----------------------------------------------------------------          
                                                                                
          Предположим, что вы скомпилировали  один  модуль,  используя          
     малую модель памяти, а другой - используя большую модель, а затем          
     решили объединить их. Что произойдет?                                      
                                                                                
          Файлы легко объединятся вместе,  но вы столкнетесь с пробле-          
     мами,  аналогичными  описанным  в разделе "Объявление функций как          
     NEAR или FAR".  Если функция в малом модуле  вызывает  функцию  в          
     большом модуле,  то это происходит при помощи near-вызова,  кото-          
     рый,  вероятно, будет выполнен неверно. Более того, вы можете на-          
     толкнуться на проблемы,  описанные в разделе "Объявление указате-          
     лей как NEAR,  FAR или HUGE",  поскольку функция в  малом  модуле          
     предполагает передачу и получение near-указателей, в то время как          
     функция в большом модуле - far-указателей.                                 
                                                                                
          Решение  этих проблем заключается опять-таки в использовании          
     прототипов функций. Предположим, что вы размещаете myputs  в  его          
     же  модуле  и компилируете его в большой модели памяти. Тогда вам          
                                                                                
     надо создать заголовок файла MYPUTS.H (или какое-либо другое  имя          
     с  расширением  ".H"), в котором бы содержался следующий прототип          
     функции:                                                                   
                                                                                
          void far myputs(char far *s);                                         
                                                                                
          Теперь,если  вы  размещаете main в его собственном модуле (с          
     именем MYMAIN.C), делайте это следующим образом:                           
                                                                                
          #include                                                     
          #include "myputs.h"                                                   
                                                                                
          main()                                                                
          {                                                                     
            char near *mystr;                                                   
                                                                                
            mystr = "Hello, world\n";                                           
            myputs(mystr);                                                      
          }                                                                     
                                                                                
          Когда  вы компилируете эту программу, Турбо Си читает прото-          
     тип функции из MYPUTS.H и воспринимает его как far-функцию, пред-          

                         - 367,368 -
                                                                                
     полагающую наличие far-указателя. Поэтому он сформирует  надлежа-          
     щую  программу, даже если она скомпилирована с использованием ма-          
     лой модели памяти.                                                         
                                                                                
          Наконец, как же поступать, если вам надо выполнить компонов-          
     ку  с библиотечными подпрограммами? Лучше всего в этом случае ис-          
     пользовать одну из библиотек больших моделей, объявив все ее ком-          
     поненты как far.  Для этого скопируйте каждый заголовочный  файл,          
     который  вы обычно используете (такой как STDIO.H) и переименуйте          
     соответствующим образом копии (например, FSTDIO.H).                        
                                                                                
          После этого  отредактируйте  каждый  скопированный  прототип          
     функции таким образом, чтобы он был однозначно far, например:              
                                                                                
          int far cdecl printf(char far * format, ...);                         
                                                                                
          В этом  случае  будут не только выполняться far-вызовы подп-          
     рограмм,  но и передавемые указатели так же будут far.  Модифици-          
     руйте вашу программу таким образом, чтобы она включала в себя но-          
     вый файл заголовка:                                                        
                                                                                
         #include                                                     
                                                                                
                                                                                
                                                                                
         main()                                                                 
         {                                                                      
           char near *mystr;                                                    
           mystr = "Htllo, world\n";                                            
           printf(mystr);                                                       
         }                                                                      
                                                                                
          Скомпилируйте вашу  программу  в  TCC,  затем  скомпонуйте в          
     TLINK,  специфицируя библиотеку большой модели, например, CL.LIB.          
     Смешение моделей - очень скрупулезное дело, но оно может быть вы-          
     полнено. Однако при наличии неточностей вас ожидают большие труд-          
     ности.                                                                     
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 369,370 -
                                                                                
                 Программирование с совмещением языков                          
     -----------------------------------------------------------------          
                                                                                
          Турбо Си позволяет вашим программам в Си  вызывать  подпрог-          
     раммы, написанные на других языках и, наоборот, программам, напи-          
     санным на других языках,  вызывать ваши подпрограммы в Си. В этой          
     главе разъясняется, насколько легко совмещается Турбо Си с други-          
     ми языками и как обеспечиваются эти интерфейсы.                            
                                                                                
          Вначале  остановимся на двух главных типах последовательнос-          
     тей передачи параметров, а затем продемонстрируем способ  написа-          
     ния модуля на ассемблере.                                                  
                                                                                
                                                                                
          Последовательности передачи параметров типа Си и Паскаль              
     -----------------------------------------------------------------          
                                                                                
          В Турбо Си предусмотрены 2 метода передачи параметров  функ-          
     циям.  Один из них - стандартный метод Си, который будет рассмот-          
     рен первым, второй - типа Паскаль.                                         
                                                                                
                                                                                
                                                                                
          Последовательность передачи параметров типа Си                        
     -----------------------------------------------------------------          
                                                                                
          Предположим, что вами объявлен следующий прототип функции:            
                                                                                
          void funca(int p1, int p2, int p3);                                   
                                                                                
          По умолчанию, Турбо Си использует последовательность переда-          
     чи параметров типа Си, называемую также Си-соглашением по вызову.          
     При вызове указанной функции (funca) параметры заносятся  в  стек          
     справа налево (p3,  p2, p1); следом за ними заносится адрес возв-          
     рата. Так, если вы сделали вызов                                           
                                                                                
          main()                                                                
          {                                                                     
             int i,j;                                                           
             long k;                                                            
             ...                                                                
             i = 5; j = 7; k = 0x1407AA;                                        
             funca(i,j,k);                                                      
             ...                                                                
          }                                                                     

                         - 371,372 -
                                                                                
                                                                                
          Стек будет выглядеть следующим образом (непосредственно  пе-          
     ред занесением адреса возврата):                                           
                                                                                
          SP + 06:  0014                                                        
          SP + 04:  07AA   k = p3                                               
          SP + 02:  0007   j = p2                                               
          SP:       0005   i = p1                                               
                                                                                
          (Памятуя о том,  что стек в 8086 возрастает от старших адре-          
     сов памяти к младшим, можно утверждать, что i в данный момент на-          
     ходится на вершине стека).  Вызванной подпрограмме нет  необходи-          
     мости  (да это и невозможно в данном случае) точно знать, сколько          
     параметров уже помещено в стеке.  Предполагается, что все необхо-          
     димые параметры там уже помещены.                                          
                                                                                
          И еще очень важно то, что вызываемая подпрограмма не удаляет          
     из стека параметры,  так как это  сделает  вызывающая  программа.          
     Например,  программа на ассемблере, созданная транслятором из ис-          
     ходной программы на Си, для главной функции выглядит так:                  
                                                                                
          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]              ; Push старший полубайт k           
          push word ptr [bp-4]              ; Push младший полубайт k           
          push word ptr [bp-6]              ; Push j                            
          push word ptr [bp-8]              ; Push i                            
          call near ptr funca               ; вызов funca (push addr)           
          add sp,8                          ; восстановить стек                 
                                                                                
          Обратим внимание на последнюю команду:  add sp,8. Транслятор          
     знает, сколько параметров помещено в стеке в данный момент; кроме          
     того он знает, что адрес возврата, помещенный в стек во время вы-          
     зова funca, уже вынут из него командой ret в конце funca.                  
                                                                                
                                                                                
                                                                                
           Последовательность передачи параметров типа Паскаль                  
     -----------------------------------------------------------------          
                                                                                
          Другой метод  передачи  параметров  - стандартный метод типа          
     Паскаль (известный также как Паскаль-соглашение  по  вызову).  Но          

                         - 373,374 -
                                                                                
     нельзя  сказать,  что вы можете вызывать функции Турбо Паскаля из          
     Турбо Си: это неверно. Рассматриваемая последовательность заносит          
     параметры слева направо;  таким образом, если funca объявлена как          
                                                                                
          void pascal funca (int p1, int p2, int p3 );                          
                                                                                
     то при  вызове этой функции параметры в стек заносятся слева нап-          
     раво (p1, p2, p3), а следом за ними в стек помещается адрес возв-          
     рата. Таким образом, если вы даете вызов                                   
                                                                                
          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)          
     знает,  сколько параметров передано и, соответственно, помещено в          
     стек.  Другими словами,  подпрограмма вызова funca на  ассемблере          
     теперь выглядит так:                                                       
                                                                                
          push  word  ptr[bp-8]             ; Push i                            
          push  word ptr [bp-6]             ; Push j                            
          push  word ptr [bp-2]             ; Push старший байт k               
          push  word ptr [bp-4]             ; Push младший байт k               
          call  near ptr funca              ; вызов funca (Push addr)           
                                                                                
          Заметим, что здесь нет команды add sp,8 после вызова. Вместо          
     этого funca использует команду ret 8  при  завершении  для  того,          

                         - 375,376 -
                                                                                
     чтобы  очистить  стек  перед  возвратом к main.  По умолчанию все          
     функции,  написанные на Турбо Си, используют метод передачи пара-          
     метров типа Си. Исключение составляет случай, когда вы используе-          
     те -р опцию транслятора (соглашение по  вызову...Паскаль),  когда          
     все функции используют метод типа Паскаль. В этой ситуации вы мо-          
     жете заставить нужную функцию применить метод передачи параметров          
     типа Си с помощью модификатора cdecl:                                      
                                                                                
          void cdecl funca (int p1, int p2, int p3);                            
                                                                                
          Этот  оператор  игнорирует -p опцию.                                  
                                                                                
          Зачем вообще нужно Паскаль-соглашение по вызову? По трем ос-          
     новным соображениям:                                                       
                                                                                
          - Вы можете быть вызваны подпрограммой, написаной на ассемб-          
     лере, которая использует данное соглашение по вызову.                      
                                                                                
          - Вы можете быть вызваны подпрограммами,  написаными на дру-          
     гих языках.                                                                
                                                                                
          - Вызов создаваемой программы будет менее  громоздким,  пос-          
                                                                                
     кольку теперь не нужно очищать стек после ее окончания.                    
                                                                                
          Какие проблемы   могут  возникнуть  при  использовании  Пас-          
     каль-соглашения по вызову?                                                 
                                                                                
          Во-первых, оно не так сильно,  как Си-соглашение по  вызову.          
     Вы не можете передать переменное число параметров (что возможно в          
     случае Си-соглашения),  т.к. вызванная подпрограмма должна знать,          
     сколько параметров заносится в стек и, соответственно, извлекает-          
     ся из него.  Передача слишком малого или слишком  большого  коли-          
     чества  параметров  будет приводить к серьезным трудностям,  в то          
     время как при Си-соглашении это не  даст  нежелательного  эффекта          
     (за исключением, возможно, неверных ответов).                              
                                                                                
          Во-вторых, если  вы используете -р опцию транслятора,  то вы          
     должны обязательно  включить  файлы  заголовков  для  стандартных          
     Си-функций, которые вы вызываете. Это необходимо потому, что если          
     вы этого не сделаете,  Турбо Си будет использовать Паскаль-согла-          
     шение для вызова каждой из этих функций,  и ваша программа навер-          
     няка потерпит крах, потому что:                                            
                                                                                
          1) параметры будут в неверном порядке;                                

                         - 377,378 -
                                                                                
                                                                                
                                                                                
          2) ничем не будет очищаться стек.                                     
                                                                                
          Файлы заголовков  объявляют  каждую  из  таких  функций  как          
     cdecl,  поэтому, если вы включете их, транслятор увидит это и бу-          
     дет использовать Си-соглашение по стеку. Однако, т.к. идентифика-          
     торы cdecl дополняются символом подчеркивания, в отличие от иден-          
     тификаторов Pascal,  то  при  компоновке  вы  получите  множество          
     ошибок, если не установите режим Generate undebars "Off". Потом у          
     вас будут большие неприятности.                                            
                                                                                
          Заключение: если вы собираетесь использовать  Паскаль-согла-          
     шение по  вызову в программе Турбо Си,  то убедитесь,  что каждая          
     функция явно объявлена как cdecl или pascal.  Это очень полезно в          
     случае использования опции компиляции о требовании наличия прото-          
     типа, т.к. в этом случае вы можете быть уверены, что каждая функ-          
     ция вызывается строго в соответствии с ее прототипом.                      
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                    Интерфейс с языком ассемблера                               
     -----------------------------------------------------------------          
                                                                                
          Теперь вы знаете, как работает каждое соглашение по вызову и          
     что в этом случае делает компилятор Турбо Си.  Что же вам необхо-          
     димо делать в вызываемой подпрограмме?  Расмотрим это на  примере          
     написанной на ассемблере подпрограммы,  которую можно вызывать из          
     Турбо Си.                                                                  
                                                                                
          Замечание: в этом разделе мы подразумеваем,  что вы  знаете,          
     как  писать  программы на языке ассемблера для 8086 и как опреде-          
     лять сегменты, константы данных и т.п. Если вы не знакомы с этими          
     вопросами,  то  можете  ознакомиться  с  ними  в  Turbo Assembler          
     Reference Guide.                                                           
                                                                                
                                                                                
                                                                                
               Порядок вызова ассемблера из Турбо Си                            
     -----------------------------------------------------------------          
                                                                                
          Вы  можете  написать подпрограммы на языке ассемблера в виде          
     модулей и скомпоновать их с вашими программами на Си. Однако име-          

                         - 379,380 -
                                                                                
                                                                                
     ются определенные соглашения, которым вы должны следовать:                 
                                                                                
          1) убедиться, что компоновщик может получить необходимую ин-          
             формацию;                                                          
          2) убедиться,  что формат файла согласуется с  типом  модели          
             памяти, используемой в вашей программе на Си.                      
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
          Ниже приведена общая схема программы:                                 
                                                                                
     =================================================================          
       Идентификатор      Имя                  Имя файла                        
     =================================================================          
                  SEGMENT            BYTE PUBLIC 'CODE'                   
                        ASSUME             CS:, DS:                 
                        <........... code segment ............>                 
                  ENDS                                                    
                                                                                
                  GROUP              _DATA,_BSS                           
                  SEGMENT            WORD PUBLIC 'DATA'                   
                        <..... initialized data segment .....>                  
                  ENDS                                                    
          _BSS          SEGMENT            WORD PUBLIC 'BSS'                    
                        <..... initialized data segment .....>                  
          _BSS          ENDS                                                    
                                                                                
                        END                                                     
     =================================================================          
                                                                                
          Индентификаторы  ,  и   в этой схеме имеют          

                         - 381,382 -
                                                                                
                                                                                
     специальные заменители, зависяшие от используемой модели  памяти.          
     Таблица 12.5 показывает, какие надо использовать в каждой модели.          
     Filename  в этой таблице - имя модуля, которое будет использовано          
     в директиве NAME и индентификаторах замещения.                             
                                                                                
          Заметим, что  в огромной модели памяти нет сегмента _BSS,  и          
     определение групп полностью игнорируется.  Вообще _BSS необязате-          
     лен, его определяют только при необходимости использования.                
                                                                                
          Лучший способ создать шаблон на языке ассемблера - скомпили-          
     ровать пустую программу в .ASM (используя опцию ТСС -S)  и  прос-          
     мотреть созданную ассемблерную программу.                                  
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
     =================================================================          
       Модель    Идентификаторы замещения   Указатели программ и данных         
     =================================================================          
     Крохотная,    =_TEXT                Code: DW _TEXT:xxx               
     Малая         =_DATA                Data: DW DGROUP:xxx              
                   =_DGROUP                                               
                                                                                
     Компактная    =_TEXT                Code: DW _TEXT:xxx               
                   =_DATA                Data: DD DGROUP:xxx              
                   =DGROUP                                                
     Средняя       =filname_TEXT         Code:DDxxx                       
                   =_DATA                Data: DW DGROUP:xxx              
                   =DGROUP                                                
                                                                                
     Большая       =filname_TEXT         Code: DDxxx                      
                   =_DATA                Data: DD DGROUP:xxx              
                   =DGROUP                                                
     Огромная      =filname_TEXT         Code: DDxxx                      
                   =filname_DATA         Data: DDxxx                      
                   =filname_DATA                                          
     =================================================================          

                         - 383,384 -
                                                                                
                                                                                
                                                                                
          Таблица 12.5. Идентификаторы замещения и модели памяти                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
              Определение констант данных и переменных                          
     -----------------------------------------------------------------          
                                                                                
          Выбор модели памяти влияет также на способ определения конс-          
     тант данных, являющихся указателями к программе, данным или к то-          
     му и другому сразу. Табл.12.5 показывает, как будут выглядеть,эти          
     указатели. Здесь ххх - адрес, являющийся указателем.                       
                                                                                
          Обратите внимание на то,  что одни указатели используют  ко-          
     манду  DW (резервировать слово),  в то время как другие - DD (ре-          
     зервировать двойное слово), что определяет размер результирующего          
     указателя.  Числовые и текстовые константы определяются как обыч-          
     но.                                                                        
                                                                                
          Переменные определяются так же, как константы. Если вы хоти-          
     те получить переменные, которые не инициализированы определенными          
     значениями,  вы можете объявить их в _BSS-сегменте, поставив воп-          
     росительный знак (?) там, куда вы потом поместите переменные.              
                                                                                
                                                                                
                                                                                
                                                                                

                         - 385,386 -
                                                                                
            Определение глобальных и внешних идентификаторов                    
     -----------------------------------------------------------------          
                                                                                
          Создание вами модуля не принесет большого успеха до тех пор,          
     пока ваша программа на Турбо Си не будет знать, какие функции она          
     может вызывать и на какие переменные может ссылаться. Кроме того,          
     вам  может потребоваться возможность вызывать функции Турбо Си из          
     подпрограмм,  написанных на ассемблере или ссылаться на  перемен-          
     ные, объявленные в Турбо Си.                                               
                                                                                
          Чтобы освоить  эти  возможности,  вам  нужно  разобраться  в          
     трансляторе и компоновщике Турбо Си.  Когда вы объявляете внешний          
     идентификатор,  транслятор  автоматически  ставит  перед ним знак          
     подчеркивания (_) до его сохранения в объектном модуле. Это озна-          
     чает,  что  вы  должны  поставить  знак подчеркивания перед любым          
     идентификатором в вашем ассемблеровском модуле, к которому вы хо-          
     тите получить доступ из программы на Си.  К Паскаль-идентификато-          
     рам обращение идет иначе, чем к идентификаторам Си, - они исполь-          
     зуют   только   верхний   регистр   и   не   снабжаются  символом          
     подчеркивания.                                                             
                                                                                
          Знак подчеркивания для идентификаторов Си  необязателен,  он          
                                                                                
     устанавливается  по умолчанию(on). Он может быть отключен (off) с          
     помощью опции командной строки -u-. Однако, если  вы  используете          
     стандартные библиотеки Турбо Си, то столкнетесь с проблемами, ес-          
     ли  только  вы  не переделали эти библиотеки. (Чтобы сделать это,          
     вам понадобится другой продукт Турбо Си - исходный текст программ          
     для  библиотек  поддержки;   контактируйте   с   фирмой   Borland          
     International для получения более подробной информации.)                   
                                                                                
          Если любая asm-программа в исходном файле ссылается на любой          
     Си-идентификатор  (данные  или  функции), то такие идентификаторы          
     должны начинаться со знака подчеркивания.                                  
                                                                                
          Турбо Ассемблер  (TASM) не чувствителен к регистру,  другими          
     словами,  когда вы транслируете программу с языка ассемблера,  то          
     все  идентификаторы сохраняются только в заглавных символах.  Ис-          
     пользование в TASM опции /mx переводит его в режим  "чувствитель-          
     ности регистра" для внешних указателей.  Компоновщик Турбо Си де-          
     лает то же  самое  с  помощью  идентификатора  extern,  т.о.  эти          
     моменты хорошо совпадают.  Вы можете заметить, что в наших приме-          
     рах ключевые слова и директивы записаны в верхнем регистре, а все          
     другие  идентификаторы и коды команд - в нижнем (строчными симво-          
     лами);  это совпадает со стилем,  предлагаемым в  руководстве  по          

                         - 387,388 -
                                                                                
     TASM. Однако, вы вольны использовать все заглавные (или строчные)          
     или любые смешанные идентификаторы, как вам удобно.                        
                                                                                
          Для того чтобы сделать идентификаторы (имена  подпрограмм  и          
     переменных) видимыми извне вашего ассемблерного модуля, вам нужно          
     объявить их как PUBLIC.                                                    
                                                                                
          Так, например,  если вы написали модуль, имеющий целые функ-          
     ции  max  и  min и целые переменные MAXINT,lastmax и lastmin,  вы          
     должны вставить в него оператор                                            
                                                                                
          PUBLIC _max,_min                                                      
                                                                                
     в ваш программный сегмент, а операторы                                     
                                                                                
          PUBLIC  _MAXINT, _lastmax, _lastmin                                   
          _MAXINT  DW 32767                                                     
          _lastmin DW 0                                                         
          _lastmax DW 0                                                         
                                                                                
     в сегмент данных.                                                          
                                                                                
                                                                                
                    Порядок вызова Турбо Си из .ASM                             
     -----------------------------------------------------------------          
                                                                                
          Для того чтобы созданный вами модуль на ассемблере мог обра-          
     щаться к функциям и переменным,  объявленным в программе на Турбо          
     Си, используйте оператор EXTRN.                                            
                                                                                
                                                                                
                        Указатели на функции                                    
     -----------------------------------------------------------------          
                                                                                
          Чтобы вызывать Си-функцию иэ подпрограммы, написанной на ас-          
     семблере, вы должны объявить ее в своем модуле с помощью операто-          
     ра                                                                         
                                                                                
          EXTRN  :                                                
                                                                                
          где  - имя функции,   означает near или far, в          
     зависимости от того,  какой является Си-функция (near  или  far).          
     Если   - near,  то оператор EXTRN должен появиться в прог-          
     раммном сегменте вашего модуля;  если - far, то EXTRN должен поя-          
     виться вне любого сегмента.  Итак, вы получите следующий оператор          

                         - 389,390 -
                                                                                
     в программном сегменте:                                                    
                                                                                
          EXTRN _myCfunc1 : near, _myCfunc2 : far                               
                                                                                
     позволяющий вам вызывать _myCfunc1 и _myCfunc2 из ваших  подпрог-          
     рамм на ассемблере.                                                        
                                                                                
                                                                                
                         Указатели на данные                                    
     -----------------------------------------------------------------          
                                                                                
          Чтобы ссылаться  на  переменные,  вы должны поместить внутрь          
     сегмента данных соответствующий оператор  (или  операторы)  EXTRN          
     следующим образом:                                                         
                                                                                
          EXTRN  :                                                 
                                                                                
     где  есть имя переменной, а  указывает на ее размер.          
                                                                                
          Возможные значения :                                            
                                                                                
          - BYTE (1 байт)                                                       
                                                                                
          - WORD (2 байта)                                                      
          - DWORD (4 байта)                                                     
          - QWORD (8 байт)                                                      
          - TBYTE (10 байт)                                                     
                                                                                
          Размеры элементов  массивов  должны  соответствовать .          
     При объявлении структуры в  качестве    нужно  использовать          
     размер наиболее часто встречающегося в структуре элемента.                 
                                                                                
          Т.о., если ваша Си-программа имела глобальные переменные              
                                                                                
          int i, jarray [10];                                                   
          char ch;                                                              
          long result;                                                          
                                                                                
     то вы  можете сделать их доступными в вашем модуле с помощью сле-          
     дующего оператора:                                                         
                                                                                
          EXTRN _i:WORD,_jarray:WORD,_ch:BYTE,_result:DWORD                     
                                                                                
          И последнее важное замечание. Если вы используете модель па-          
     мяти  huge,  то операторы EXTRN должны появляться вне любого сег-          

                         - 391,392 -
                                                                                
                                                                                
     мента. Это применимо как к процедурам, так и к переменным.                 
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                  Создание подпрограмм на ассемблере                            
     -----------------------------------------------------------------          
                                                                                
          Теперь вы знаете,  каким образом все должно быть определено,          
     и настало время ознакомиться,  как вообще записываются функции на          
     ассемблере.  Здесь необходимо рассмотреть несколько важных момен-          
     тов:  передача параметров,  возвращаемые значения и соглашения по          
     регистрам.                                                                 
                                                                                
          Предположим, вы хотите написать функцию min, которая, как вы          
     полагаете, имеет следующий прототип в Си:                                  
                                                                                
          int extern min(int v1, int v2);                                       
                                                                                
          Вы хотите, чтобы функция min вернула минимальную из двух пе-          
     реданных ей величин. В общем виде запись функции min будет:                
                                                                                
                 PUBLIC  _min                                                   
          _min   PROC    near                                                   
                  .                                                             
                  .                                                             
                  .                                                             

                         - 393,394 -
                                                                                
                                                                                
          _min   ENDP                                                           
                                                                                
          Это верно в предположении,  конечно,  что функция min  будет          
     near-функцией. Если это far-функция, то вы должны заменить far на          
     near.  Заметим,  что min снабжено знаком подчеркивания для  того,          
     чтобы компоновщик Турбо Си мог корректно установить связи.                 
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                         Передача параметров                                    
     -----------------------------------------------------------------          
                                                                                
          Ваша первая задача -  выбрать  используемое  в  подпрограмме          
     соглашение по передаче параметров; учитывая приведенные ранее со-          
     ображения о трудностях  использования  Паскаль-соглашения,  будем          
     избегать  его  и использовать Си-метод.  Это означает,  что когда          
     вызвана функция min, стек выглядит следующим образом:                      
                                                                                
          SP + 04: v2                                                           
          SP + 02: v1                                                           
          SP:      адрес возврата                                               
                                                                                
          Если вы хотите получить  параметры,  не  изменяя  содержания          
     стека,  то для зтого необходимо сохранить базовый указатель (BP),          
     затем занести стековый указатель (SP) в  базовый  и  использовать          
     его в качестве указателя на стек для извлечения из него необходи-          
     мых вам величин.  Заметим,  что после занесения BP в стек относи-          
     тельные смещения параметров возрастут на 2, т.к. в стек будут до-          
     бавлены 2 байта.                                                           
                                                                                
                                                                                

                         - 395,396 -
                                                                                
                                                                                
                  Управление возвращаемыми величинами                           
     ----------------------------------------------------------------           
                                                                                
          Ваша функция возвращает целую величину;  куда вы ее помести-          
     те?  Для 16-битных (2-байтных) величин (char,  shot,  int,  enum,          
     near-указатели) вы используете регистр AX; для 32-битных (4-байт-          
     ных),  включающих far- и huge-указатели - регистр DX для старшего          
     слова  (или  для  адреса  сегмента у указателей) и регистр AX для          
     младшего слова.                                                            
                                                                                
          Величины float, double и long double возвращаются в регистре          
     top-of-stack (TOS) процессора 8087/80287, ST(0); если использует-          
     ся эмулятор 8087/80287, то величина возвращается  в  TOS  регистр          
     эмулятора.                                                                 
                                                                                
          Величины структур  возвращаются  путем занесения их в стати-          
     ческую ячейку памяти и передачей указателя на эту  ячейку  (в  АХ          
     для моделей с малыми данными, в DX:АХ для моделей с большими дан-          
     ными).                                                                     
                                                                                
          Вызывающая функция должна затем скопировать эту  величину  в          
                                                                                
     любом  месте,  где она необходима. Структуры длиной в 1 и 2 байта          
     возвращаются в АХ (подобно  любой  обычной  целой),  а  4-байтные          
     структуры в АХ и DX.                                                       
                                                                                
          В примере с min вы имеете дело с 16-битной величиной, поэто-          
     му можете поместить ответ в АХ.                                            
                                                                                
          Ваша программа выглядит теперь так:                                   
                                                                                
               PUBLIC _min                                                      
          _min PROC near                                                        
               push bp                    ;  Сохранить bp в стеке               
               mov bp,sp                  ;  Загрузить sp в bp                  
               mov ax,[bp+4]              ;  Переместить v1 в ax                
               cmp ax,[bp+6]              ;  Cравнить v1 и v2                   
               jle exit                   ;  если v1 > v2                       
               mov ax,[bp+6]              ;  Загрузить в ax v2                  
          exit: pop bp                    ;  Восстановить bp                    
               ret                        ;  И возврат в Си                     
          _min ENDP                                                             
                                                                                
          А  что изменится, если вы объявите min как far-функциюЯ? Ос-          

                         - 397,398 -
                                                                                
     новное отличие в том, что стек при входе будет выглядеть так:              
                                                                                
          SP + 06:    v2                                                        
          SP + 04:    v1                                                        
          SP + 02:    сегмента возврата                                         
          SP:         смещение возврата                                         
                                                                                
          Это означает,  что смещение в стеке увеличилось на 2, т.к. в          
     стек внесены 2 дополнительных байта (сегмент возврата).  Far-вер-          
     сия вашей программы будет выглядеть так:                                   
                                                                                
               PUBLIC  _min                                                     
          _min PROC far                                                         
               push bp                    ;  Сохранить bp в стеке               
               mov bp,sp                  ;  Загрузить sp в bp                  
               mov ax,[bp+6]              ;  Переместить v1 в ax                
               cmp ax,[bp+8]              ;  Cравнить v1 и v2                   
               jle exit                   ;  если v1 > v2                       
               mov  ax,[bp+6]             ;  Загрузить в ax v2                  
          exit: pop bp                    ;  Восстановить bp                    
               ret                        ;  И возврат в Си                     
          _min ENDP                                                             
                                                                                
                                                                                
          Заметим, что все смещения для v1 и v2 увеличились на  2, что          
     отражает добавление байтов в стеке.                                        
                                                                                
          А что  если  из  каких-либо  соображений вы объявите min как          
     Паскаль-функцию? Это равносильно вашему решению использовать Пас-          
     каль-соглашение по передаче параметров.                                    
                                                                                
          Ваш стек  при  входе  будет выглядеть следующим образом (при          
     условии, что min является near-функцией):                                  
                                                                                
          SP + 04:   v1                                                         
          SP + 02:   v2                                                         
          SP:        смещение возврата                                          
                                                                                
          Дополнительно вы  должны  соблюдать  Паскаль-соглашения  для          
     идентификатора min: писать его заглавными буквами и без подчерки-          
     вания.                                                                     
                                                                                
          Кроме изменения порядка занесения параметров  v1  и  v2  это          
     соглашение  требует  от min очистки стека при ее завершении.  Это          
     делается указанием в команде RET количества  байтов, пропускаемых          

                         - 399,400 -
                                                                                
     в  стеке.  В нашем случае вы должны пропустить в стеке 4 дополни-          
     тельных байта, используемых для v1 и v2 (адрес возврата извлечет-          
     ся с помощью RET автоматически).                                           
                                                                                
          Теперь модифицированная подпрограмма будет выглядеть так:             
                                                                                
                РUBLIC   MIN                                                    
          MIN   PROC     near              ;  Паскаль-версия                    
                push     bp                ;  Сохранить bp в стеке              
                mov      bp,sp             ;  Загрузить sp в bp                 
                mov      ax,[bp+6]         ;  Переместить v1 в ax               
                cmp      ax,[bp+4]         ;  Cравнить v1 и v2                  
                jle      exit              ;  если v1 > v2                      
                mov      ax,[bp+4]         ;  Загрузить в ax v2                 
          exit: pop      bp                ;  Восстановить bp                   
                ret      4                 ;  Очистка стека и возврат           
          MIN   ENDP                                                            
                                                                                
          И последний пример, демонстрирующий, почему вам может потре-          
     боваться использовать Си-соглашение по передаче параметров. Пред-          
     положим, вы переопределили min следующим образом:                          
                                                                                
                                                                                
          int  extern  min (int count, int v1, int v2, ...);                    
                                                                                
          Теперь min может получать любое количество целых чисел и бу-          
     дет возвращать минимальное из них. Однако, т.к. min не имеет воз-          
     можности автоматически узнавать, сколько значений ей передано, то          
     первым ее параметром будет величина count,  указывающая,  сколько          
     величин следуют за ней.                                                    
                                                                                
          Например, вы можете записать это так:                                 
                                                                                
          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 имеет вид:                         

                         - 401,402 -
                                                                                
                                                                                
               PUBLIC _min                                                      
          _min PROC near                                                        
               push bp              ;  Сохранить bp в стеке                     
               mov bp,sp            ;  Загрузить sp в bp                        
               mov ax,0             ;  Занести в ax 0                           
               mov cx,[bp+4]        ;  Переместить count в cx                   
               cmp cx,ax            ;  Cравнить cx c 0                          
               jle exit             ;  если <= 0, то конец                      
               mov ax,[bp+4]        ;  Загрузить первую величину в ax           
               jmp ltest            ;  переход на цикл ;проверки                
      compare: cmp ax,[bp+6]        ;  Cравнить со след. величиной              
               jle ltest            ;  если след. величина меньше               
               mov ax,[bp+6]        ;  Загрузить в ах след. величину            
        ltest: add bp,2             ;  Перейти к след. величине                 
               loop compare         ;  затем в цикле назад                      
         exit: pop bp               ;  Восстановить bp                          
               ret                  ;  И возврат в Си                           
          _min ENDP                                                             
                                                                                
          Заметим, что эта версия правильно управляет всеми возможными          
     значениями count.                                                          
                                                                                
                                                                                
                                                                                
          - Если count <= 0, min возвращает 0;                                  
          - Если count = 1, min возвращает первую величину в списке;            
          - Если count >= 2, min организует цикл сравнения для  поиска          
            наименьшего значения в списке параметров.                           
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 403,404 -
                                                                                
                     Соглашения по регистрам                                    
     -----------------------------------------------------------------          
                                                                                
          В min  вы  использовали  различные  регистры  (BP,  SP,  AX,          
     BX,CX).  Возникает  вопрос о надежном их использовании.  Может ли          
     программа,  написанная на Турбо Си,  использовать любые регистры?          
     Оказывается, вы правильно написали эту функцию. Из всех использо-          
     ванных регистров предметом ваших забот был лишь  регистр  BP;  вы          
     сохранили  его в стеке при входе и восстановили затем при выходе.          
                                                                                
          Другие 2 регистра,  о которых вы должны побеспокоиться - это          
     SI и DI:  эти регистры Турбо Си использует для регистровых  пере-          
     менных.  Если вы используете их только в ассемблерной подпрограм-          
     ме, то их нужно сохранить (вероятно в стеке) при входе в подпрог-          
     рамму   и  восстановить  при  выходе  из  нее.  Однако,  если  вы          
     компилируете Турбо Си-программу с помощью опции -r- (Use register          
     variables - Off), то не нужно заботиться о сохранении SI и DI.             
                                                                                
          Замечание:  будьте  внимательны при использовании опции -r-.          
     Обратитесь к Приложению С "Справочного руководства по  Турбо  Си"          
     для   получения   более   полной  информации  об  опции  register          
     variables.                                                                 
                                                                                
                                                                                
                                                                                
          Регистры CS,DS,SS и ES могут иметь соответствующие значения,          
     зависящие от используемой модели памяти. Вот эти соотношения:              
                                                                                
          Крохотная           CS=DS=SS; ES=scratch (рабочий)                    
          Малая, Средняя      CS!=DS,DS=SS; ES=scratch                          
          Компактная, Большая CS!=DS!=SS;ES=scratch(один CS на модуль)          
          Огромная            CS!=DS!=SS;ES=scratch(по одному  CS и DS          
                              на модуль)                                        
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 405,406 -
                                                                                
                  Вызов Си-функций из .ASM подпрограмм                          
     -----------------------------------------------------------------          
                                                                                
          Конечно,можно использовать другой путь: вызвать подпрограммы          
     Си  из модуля,  написанного на ассемблере.  Но при этом вы должны          
     сделать Си-функцию видимой из модуля,  написанного на ассемблере.          
     Мы уже вкратце обсудили,  как это делается:  функция  объявляется          
     как  EXTRN,  независимо от того,  какого она типа - near или far.          
     Например, вы написали следующую Си-функцию:                                
                                                                                
          long docalc(int *fact1, int fact2, int fact3);                        
                                                                                
          Для простоты предположим,  что docalc - это Си-функция (т.е.          
     противоположна Паскаль-функции).  Имея в виду использование  кро-          
     хотной,  малой или компактной модели памяти, вы должны Си-функцию          
     в модуле, написанном на ассемблере, объявить следующим образом:            
                                                                                
          EXTRN _docalc:near                                                    
                                                                                
          Подобно этому, если вы использовали среднюю, большую или ог-          
     ромную модели памяти, то объявите функцию как _docalc:far.                 
                                                                                
                                                                                
          docalc определяется тремя параметрами:                                
          - адресом ячейки памяти - xval;                                       
          - значением, хранимым в ячейке - imax;                                
          - константой 421 (десятичной).                                        
                                                                                
          Предположим  также,  что  вам  нужно  сохранить  результат в          
     32-разрядной ячейке памяти, называемой ans. Эквивалентный вызов в          
     Си будет таким:                                                            
                                                                                
          ans = docalc (xval, imax, 421);                                       
                                                                                
          Вам необходимо занести в стек сначала 421, затем imax, после          
     этого адрес xval и,  наконец,  вызвать docalc. При возврате нужно          
     очистить стек, который увеличился на 6 дополнительных байт, и за-          
     тем занести ответ в ячейки памяти ans и ans+2.                             
                                                                                
          Программа будет иметь вид:                                            
                                                                                
          mov ax,421        ;  Занести в стек 421                               
     push ax                                                                    
     push imax              ;  Занести в стек imax                              
     lea ax,xval            ;  Занести в стек xval                              

                         - 407,408 -
                                                                                
     push ax                                                                    
     call _docalc           ;  Вызов функции docalc                             
     add sp,6               ;  Очистка стека                                    
     mov ans,ax             ;  Занести 32-разрядный результат в ans             
     mov ans+2,dx           ;  Включая старшее слово                            
                                                                                
          А что  если  docalc будет использовать Паскаль-соглашение по          
     передаче параметров? Тогда вам придется изменить порядок парамет-          
     ров на обратный,  и не надо будет беспокоиться о чистке стека при          
     возврате,  т.к. это должна сделать вызываемая подпрограмма. Кроме          
     того, вы должны соблюдать Паскаль-соглашение при написании docalc          
     (без символа подчеркивания (_) и в верхнем регистре).                      
                                                                                
          Оператор EXTRN будет таким:                                           
                                                                                
          EXTERN DOCALC:near                                                    
                                                                                
     а программа вызова docalc будет иметь вид:                                 
                                                                                
          lea ax,xval      ;  Занести в стек xval                               
          push ax                                                               
          push imax        ;  Занести в стек imax                               
                                                                                
                                                                                
          mov ax,421       ;  Занести в стек 421                                
          push ax                                                               
          call DOCALC      ;  Вызов функции docalc                              
          mov ans,ax       ;  Занести 32-разрядный результат в ans              
          mov ans+2,dx     ;  Включая старшее слово                             
                                                                                
          Вот и все, что вам необходимо знать, чтобы вы могли  устано-          
     вить связь между языками.                                                  
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 409,410 -
                                                                                
                  ПРОГРАММИРОВАНИЕ НА НИЗКОМ УРОВНЕ:                            
                псевдопеременные, встроенный ассемблер                          
                         и функции прерывания                                   
     -----------------------------------------------------------------          
                                                                                
          Предположим, что вы хотите выполнить  какие-то  действия  на          
     уровне  управления  машиной, но предпочитаете не испытывать труд-          
     ностей, связанных с созданием отдельного модуля на языке  ассемб-          
     лера.  Что поможет вам в этом случае? Турбо Си дает ответ на этот          
     вопрос (фактически три ответа): псевдопеременные, встроенный  ас-          
     семблер  и  функции прерывания. Прочтите эту главу, и вы поймете,          
     как каждый из них поможет выполнить вашу работу.                           
                                                                                
                                                                                
                                                                                
                             Псевдопеременные                                   
     -----------------------------------------------------------------          
                                                                                
          Процессор в вашей ЭВМ (8088/8086/80186/80286) имеет  опреде-          
     ленное число регистров (или специальных элементов хранения),  ко-          
     торые он использует для манипулирования величинами.  В каждом ре-          
     гистре   16  разрядов  (2  байта);  некоторые  из  них  выполняют          
                                                                                
     специальные функции,  хотя часть из них может быть использована и          
     в  качестве регистров общего назначения.  Вспомните "Модели памя-          
     ти", рассмотренные в начале этой главы, для подробного рассмотре-          
     ния этих регистов процессора.                                              
                                                                                
          Иногда (в низкоуровневом программировании) вам может понадо-          
     биться обращение непосредственно к этим регистрам из Си-программы          
     для:                                                                       
          - загрузки в них значений перед вызовом  системных  подпрог-          
     рамм;                                                                      
          - просмотра значений, содержащихся в них в настоящее время.           
                                                                                
          Турбо Си очень просто позволит вам обратиться к этим регист-          
     рам через псевдопеременные. Псевдопеременные - это просто иденти-          
     фикаторы,  которые  соответствуют  данным регистрам. Их можно ис-          
     пользовать по  аналогии  с  переменными  типа  unsigned  int  или          
     unsigned char.                                                             
                                                                                
          В таблице 12.6 приведен полный список псевдопеременных,  ко-          
     торые можно использовать,  их типы,  регистры,  которым они соот-          
     ветствуют, и для чего эти регистры обычно используются.                    
                                                                                

                         - 411,412 -
                                                                                
                                                                                
     =================================================================          
        Псевдо -                                  Назначение                    
      переменные        Тип          Регистр       регистра                     
     =================================================================          
                                                                                
         _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     Используется для                  
                                              регистровых переменных            
                                                                                
     =================================================================          
                                                                                
           Таблица 12.6. Псевдопеременные Турбо Си                              
                                                                                
          Для  чего  же вам может понадобиться прямое обращение к этим          
     переменным из Турбо Си ?                                                   
                                                                                

                         - 413,414 -
                                                                                
          Для занесения в регистр некоторого  значения  перед  вызовом          
     низкоуровневой подпрограммы. Например, можно вызвать определенную          
     подпрограмму из ПЗУ, используя INT (прерывание), но обязательно с          
     предварительным занесением  необходимой информации в определенные          
     регистры, например так:                                                    
                                                                                
     void readchar (unsigned char page, unsigned char *ch,                      
                                        unsigned char *attr);                   
     (                                                                          
        _AH = 8;           /*Код операции: прочитать знак, аттрибут */          
        _BH = page;        /* номер страницы дисплея*/                          
        geninterrupt(0*10) /* вызов прерывания INT 10h */                       
        *ch = _AL;         /* получение кода ASCII считанного знака */          
        *attr = _AH;       /* получение аттрибута считанного знака  */          
     )                                                                          
                                                                                
          Вы видите, что код операции и номер страницы дисплея переда-          
     ются в подпрограмму по INT 10h, а возвращаемые значения копируют-          
     ся в ch и attr.                                                            
                                                                                
          C псевдопеременными можно обращаться так,  будто это обычные          
     глобальные  переменные  соответствующего  типа  (unsigned int или          
                                                                                
     unsigned char).  Однако то, что они ссылаются на регистры процес-          
     сора, а не на ячейки, накладывает на их использование ряд ограни-          
     чений и соглашений:                                                        
                                                                                
          - вы не можете использовать  оператор адреса (&) совместно с          
     псевдопеременной, т.к. псевдопеременная не имеет адреса;                   
                                                                                
          - так  как  компилятор генерирует команды,  использующие ре-          
     гистры,  то нет никакой гарантии того, что значения, занесенные в          
     псевдопеременные,  будут храниться там длительное время. Это зна-          
     чит,  что вы должны заносить значения в регистры  непосредственно          
     перед  выполнением  запроса  и выбирать из них результат сразу же          
     после его  завершения,  как,  например,  в  предыдущем  случае  с          
     readchar.  Это  особенно  касается  регистров  общего  назначения          
     (AX,AH,AL и т.п.),  т.к.  компилятор свободно использует  их  как          
     временную память, и, кроме того, сам процессор изменяет их в про-          
     извольные моменты времени,  используя, например, CX для установки          
     организации  цикла  при выполнении операции сдвига,  или DX - для          
     хранения старшего слова при 16-битном умножении;                           
                                                                                
          - нет полной уверенности в том, что занесенная в регистр ве-          
     личина сохранится при выполнении вызова функции. Например:                 

                         - 415,416 -
                                                                                
                                                                                
                                                                                
          _CX = 18;                                                             
          myFunc();                                                             
          i = _CX ;                                                             
                                                                                
          Во время  вызова функции сохраняются не все регистры,  и нет          
     гарантии,  что i получит значение 18.  Надежно, до и после вызова          
     функции, величины хранятся лишь в регистрах _CS,_BP,_SI и _DI;             
                                                                                
          - необходимо  очень осторожно использовать некоторые регист-          
     ры,  т.к.  это может привести к неожиданным и неблагоприятным ре-          
     зультатам.  Например, прямое сохранение величин в _CS,_SS,_SP или          
     _BP может стать (и почти всегда будет) причиной  того,  что  ваша          
     программа будет работать неустойчиво,  т.к. код, создаваемый ком-          
     пилятором Турбо Си, использует эти регистры для других целей.              
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                  Использование встроенного ассемблера                          
     -----------------------------------------------------------------          
                                                                                
          Вы уже видели, как надо писать отдельные подпрограммы на ас-          
     семблере и компоновать их с программами на Турбо Си.  Кроме этого          
     Турбо Си позволяет использовать команды ассемблера непосредствен-          
     но в Си программе. Это называется встроенным ассемблером.                  
                                                                                
          Для того, чтобы использовать встроенный ассемблер в Си-прог-          
     рамме,  вы  должны использовать опцию компилятора -В.  Если вы не          
     используете эту опцию,  и компилятор встретит встроенный  ассемб-          
     лер,  то  он предупредит вас и закончит свою работу с требованием          
     запустить его снова с опцией -В.  Вы можете избежать этого с  по-          
     мощью введения в программу утверждения #pragma inline,  что заме-          
     нит вам опцию -В, когда компилятор встретит его.                           
                                                                                
          Вам нужно обязательно иметь копию Турбо Ассемблера(TASM). На          
     первом шаге компилятор создает файл с программой на языке ассемб-          
     лера, а затем вызывает TАSM, чтобы получить .OBJ файл.                     
                                                                                
          Вам  также  следует  знать  архитектуру и набор каманд 8086.          
     Кроме того, вам необходимо знать, как работают  команды,  которые          

                         - 417,418 -
                                                                                
     вы используете, как можно и как нельзя их применять.                       
                                                                                
          Если вы все это знаете,  то вам остается только использовать          
     ключевое слово asm для того, чтобы  вставлять команды встроенного          
     ассемблера. Это записывается в следующем виде:                             
                                                                                
          asm <код операции> <операнды> <; или символ новой строки>             
                                                                                
     где:                                                                       
          - <код операции> - допустимая команда 8086 (таблицы с допус-          
            тимыми командами приведены ниже);                                   
          - <операнды> - содержат операнды,  допустимые для данной ко-          
            манды,  и могут ссылаться на  Си-константы,  переменные  и          
            метки;                                                              
          - <; или символ новой строки> - точка с  запятой  или  новая          
            строка, указывающие на окончание оператора asm.                     
                                                                                
          Новый оператор asm может размещатся на той же строке, следуя          
     после точки с запятой,  но он не может продолжаться на  следующую          
     строку.                                                                    
                                                                                
          Точка с  запятой  не может быть использована для начала ком-          
                                                                                
     ментария (как это возможно в TASM).  Для того,  чтобы комментиро-          
     вать операторы asm, используют комментарии в стиле Си, т.е.                
                                                                                
       asm mov ax,ds;                  /*Этот комментарий правильный*/          
       asm pop ax; asm pop ds; asm iret;    /*Комментарий правильный*/          
       asm push ds;                    ЭТОТ КОММЕНТАРИЙ НЕПРАВИЛЬНЫЙ!!          
                                                                                
          Заметим, что последняя строка приводит к ошибке, т.к. комен-          
     тарий здесь некорректен.                                                   
                                                                                
          Пара <код операции> <операнды> переносится непосредственно в          
     программу на языке ассемблера, которую Турбо Си генерирует из ко-          
     манд Си.  Любые Си-идентификаторы заменяются соответствующими эк-          
     вивалентами на языке ассемблера.                                           
                                                                                
          Возможности встроенного ассемблера не равнозначны возможнос-          
     тям полного ассемблера,  так как многие ошибки не могут быть  не-          
     медленно обнаружены.  TASM будет ловить любые ошибки, которые мо-          
     гут  здесь   появиться.   Однако   TASM   не   может   распознать          
     местонахождение ошибок в часности потому, что место положения ко-          
     манды в Си-программе утеряно.                                              
                                                                                

                         - 419,420 -
                                                                                
          Каждый оператор asm считается командой Си. Например:                  
                                                                                
           myfunc()                                                             
          {                                                                     
             int i;                                                             
             int x;                                                             
             if (i > 0)                                                         
                asm mov x,4                                                     
             else                                                               
                i = 7;                                                          
          }                                                                     
                                                                                
          Эта конструкция допустима в Си-операторе  if.  Заметим,  что          
     нет  необходимости ставить точку с запятой после команды mov x,4.          
     Оператор asm является единственным оператором Си, который зависит          
     от  символа  новой строки.  Этого нет в остальной части языка Си,          
     однако, это соглашение принято для совместимости с основными ком-          
     пиляторами UNIX.                                                           
                                                                                
          Оператор ассемблера  может  быть использован как выполняемый          
     оператор внутри функции или как внешнее объявление  вне  функции.          
     Операторы ассемблера, расположенные вне функции, находятся в сег-          
                                                                                
     менте данных (DATA), а расположенные внутри функции - в программ-          
     ном сегменте (СODE).                                                       
                                                                                
          Приведем версию   встроенного  ассемблера  для  функции  min          
     (представленной ранее в этой главе в разделе "Управление  возвра-          
     щаемыми значениями").                                                      
                                                                                
          int min (int V1, int V2)                                              
          {                                                                     
            asm mov ax,V1                                                       
            asm cmp ax,V2                                                       
            asm jle minexit                                                     
            asm mov ax,V2                                                       
            minexit:                                                            
            return (_AX);                                                       
          }                                                                     
                                                                                
          Этот пример показывает, почему использование встроенного ас-          
     семблера в Турбо Си более гибко и мощно,  чем вызов .ASM подпрог-          
     рамм. Этот пример работает с модулями, компилируемыми как с боль-          
     шой, так и с малой моделями  памяти,  как  с  Паскаль,  так  и  с          
     Си-соглашениями по вызову.                                                 

                         - 421,422 -
                                                                                
                                                                                
          Эквивалент  же .ASM всегда должен быть изменен в зависимости          
     от модели памяти и соглашения по вызову (Си или Паскаль). В  .ASM          
     эквиваленте  для min вы всегда отвечаете за смещение параметров и          
     написание идентификатора (_min или MIN); этого не нужно в  случае          
     версии встроенного ассемблера.                                             
                                                                                
          Замечание: существует новый особый вызов _emit_, позволяющий          
     программировать на встроенном Турбо Паскале. Для более полной ин-          
     формации  об  _emit_ отсылаем вас к введению главы 2 "Руководства          
     по Турбо Си".                                                              
                                                                                
          Любые коды операций 8086 могут быть включены  как  операторы          
     встроенного  ассемблера.  Существует 4 класса команд, допускаемых          
     компилятором Турбо Си:                                                     
                                                                                
        - нормальные команды - обычный набор кодов операций 8086;               
        - строковые команды - специальные команды управления строками;          
        - команды перехода - различные команды перехода;                        
        - директивы ассемблера - определение и размещение данных.               
                                                                                
          Заметим, что компилятор допускает любые операнды,  даже если          
                                                                                
                                                                                
     они ошибочны или недопустимы с точки зрения ассемблера.  Компиля-          
     тор не накладывает ограничений на формат операндов.                        
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 423,424 -
                                                                                
               Команды                                                          
     -----------------------------------------------------------------          
                                                                                
          Далее  следует полный список мнемоники команд, которые могут          
     быть использованы в качестве нормальных команд:                            
                                                                                
     =================================================================          
     aaa     fcom          fldl2t         fsub          or                      
     aad     fcomp         fldlg2         fsubp         out                     
     aam     fcompp        fldln2         fsubr         pop                     
     aas     fdecstp **    fldlpi         fsubrp        popa                    
     adc     fdisi         fldz           ftst          popf                    
     add     fdivi         fmul           fwait         push                    
     and     fdivp         fmulp          fxam          pusha                   
     bound   fdivr         fnclex         fxch          pushf                   
     call    fdivrp        fndisi         fxtract       rcl                     
     cbw     feni          fneni          fyl2x         rcr                     
     clc     ffree **      fninit         fyl2xpl       ret                     
     cld     fiadd         fnop           hlt           rol                     
     cli     ficom         fnsave         idiv          ror                     
     cmc     ficomp        fnstcw         imul          sahf                    
     cmp     fidiv         fnstenv        in            sal                     
                                                                                
     cwd     fidivr        fnstsw         inc           sar                     
     daa     fild          fpatan         int           sbb                     
     das     fimul         fprem          into          shl                     
     dec     fincstr **    fptan          iret          shr                     
     div     finit         frndint        lahf          sbc                     
     enter   fist          frstor         lds           std                     
     f2xm1   fistp         fsave          lea           sti                     
     fabs    fisub         fscale         leave         sub                     
     fadd    fisubr        fsqrt          les           test                    
     faddp   fld           fst            mov           wait                    
     fbld    fld1          fstcw          mul           xchg                    
     fbstp   fldcw         fstenv         neg           xlat                    
     fchs    fldenv        fstp           not           xor                     
     fclex   fldl2e        fstsw                                                
     =================================================================          
                                                                                
                   Таблица 12.7. Мнемоника кодов операций                       
                                                                                
          Замечание: когда вы используете  мнемонику  команд  80186  в          
     операторах  встроенного ассемблера,  то должны включить опцию ко-          
     мандной строки -1.  Это вызовет вывод в компилятор ассемблера не-          
     обходимых операторов,  так что TASM будет правильно понимать мне-          

                         - 425,426 -
                                                                                
                                                                                
     монику команд.  Кроме того,  если  вы  используете  более  ранние          
     версии ассемблера, то эта мнемоника кодов не поддерживается.               
                                                                                
          Другое замечание: если вы используете встроенный ассемблер в          
     подпрограммах  с  эмуляцией  команд с плавающей точкой (опция TCC          
     -f), то операции, помеченные (**), не поддерживаются.                      
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
               Строковые команды                                                
     -----------------------------------------------------------------          
                                                                                
          В дополнение к списку кодов операций следующие строковые ко-          
     манды  могут  быть  использованы  самостоятельно или с префиксами          
     повторения:                                                                
                                                                                
     ===============================================================            
                                                                                
          cmps       insw       movsb       outsb       scasw                   
          cmpsb      lods       movsbw      outsw       stos                    
          cmpsw      lodsb      msb         scas        stosb                   
          ins        lodsw      outs        scasb       stosw                   
          insb       movs                                                       
                                                                                
     ===============================================================            
                   Таблица 12.8. Строковые команды                              
                                                                                
                                                                                
                                                                                
                                                                                

                         - 427,428 -
                                                                                
               Префиксы повторения                                              
     -----------------------------------------------------------------          
                                                                                
          Могут быть использованы следующие префиксы повторения:                
                                                                                
          rep      repe       repne      repnz       repz                       
                                                                                
                                                                                
                                                                                
               Команды перехода                                                 
     -----------------------------------------------------------------          
                                                                                
          Команды перехода трактуются специально. Tак как метка не мо-          
     жет быть включена в команду сама по себе, то переходы должны быть          
     к Си-меткам  (рассмотренным в разделе "Использование команд пере-          
     хода и меток"). Разрешены следующие команды перехода:                      
                                                                                
     ================================================================           
                                                                                
            ja        jge       jnc       jnp       js                          
            jae       jl        jne       jns       jz                          
            jb        jle       jng       jnz       loop                        
                                                                                
                                                                                
            jbe       jmp       jnge      jo        loope                       
            jс        jna       jnl       jp        loopne                      
            jcxz      jnae      jnle      jpe       loopnz                      
            je        jnb       jno       jpo       loopz                       
            jg        jnbe                                                      
                                                                                
     =================================================================          
                     Таблица 12.9. Команды перехода                             
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 429,430 -
                                                                                
               Директивы ассемблера                                             
     -----------------------------------------------------------------          
                                                                                
          Во встроенном ассемблере Турбо Си разрешены следующие дирек-          
     тивы:                                                                      
                                                                                
         db         dd         dw         extrn                                 
                                                                                
                                                                                
                                                                                
          Указатели встроенного ассемблера к данным и функциям                  
     -----------------------------------------------------------------          
                                                                                
          Вы можете  использовать  Си-идентификаторы в операторах asm;          
     Турбо Си будет автоматически превращать их в соответствующие опе-          
     ранды  ассемблера и ставить знак подчеркивания в именах идентифи-          
     каторов.  Могут быть использованы все идентификаторы, включая ав-          
     томатические  (локальные)  переменные,  регистровые  переменные и          
     параметры функции.                                                         
                                                                                
          В общем, идентификатор Си может быть использован в любой по-          
     зиции, где допускается адресный операнд. Понятно, что регистровая          
                                                                                
                                                                                
     переменная может быть использованна там, где регистр - допустимый          
     операнд.                                                                   
                                                                                
          Если программа  во  время  грамматического разбора операндов          
     команды встроенного ассемблера наталкивается на идентификатор, то          
     его поиск выполняется в таблице идентификаторов Си. Имена регист-          
     ров 8086 исключают из этого поиска. Могут быть использованы любые          
     заглавные или строчные формы имен регистров.                               
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 431,432 -
                                                                                
               Встроенный ассемблер и регистровые переменные                    
     -----------------------------------------------------------------          
                                                                                
          Имеется два наиболее часто встречающихся обьявления  регист-          
     ров в функциях: либо как регистровые переменные, либо как автома-          
     тические (локальные) переменные.  Если  ключевое  слово  register          
     встречается  в  объявлении,  которое не может быть регистром,  то          
     ключевое слово игнорируется.                                               
                                                                                
          В регистре могут быть размещены только short, int (или соот-          
     ветствующие  unsigned типы) или двухбайтные переменные указатели.          
     SI и DI - это регистры 8086,  используемые для регистровых  пере-          
     менных. Если в функции не дано объявления регистровых переменных,          
     то программа встроенного ассемблера может  свободно  использовать          
     SI  или DI как случайные регистры.  Функция Си при входе и выходе          
     автоматически сохраняет и восстанавливает регистры SI и DI  вызы-          
     вающей программы.                                                          
                                                                                
          Если в функции имеются регистровые объявления, то встроенный          
     ассемблер может использовать или  изменять  значения  регистровой          
     переменной,  используя  SI или DI.  Однако более предпочтительный          
     метод состоит в том, чтобы использовать идентификатор Си в случае          
                                                                                
                                                                                
     внутреннего использования регистровых переменных.                          
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 433,434 -
                                                                                
                                                                                
                        Встроенный ассемблер:                                   
                смещения и замещения размеров операндов                         
     -----------------------------------------------------------------          
                                                                                
          Во время программирования вам не нужно интересоваться точны-          
     ми смещениями локальных переменных. Просто использование имен бу-          
     дет включать правильные смещения.                                          
                                                                                
          Однако, может возникнуть необходимость включить соответству-          
     ющие  WORD PTR,  BYTE PTR или другие замещения размеров операндов          
     ассемблерной команды. Замещение WORD PTR необходимо в LES или ко-          
     мандах косвенного far-вызова.                                              
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
               Использование элементов Си-структуры                             
     -----------------------------------------------------------------          
                                                                                
          Вы, конечно, можете описывать элементы структуры в операторе          
     встроенного ассемблера   обычным   порядком,   т.е.   при  помощи          
     . . В этом случае вы имеете дело с переменной и          
     можете хранить в ней или получить из нее значения. Однако, вы так          
     же можете непосредственно описывать элемент (без  имени  перемен-          
     ной),  используя  числовую  константу.  В этой ситуации константа          
     равна смещению (в байтах) от начала  структуры,  содержащей  этот          
     элемент. Рассмотрим следующий программный фрагмент:                        
                                                                                
          struct myStruct (                                                     
             int     a_a;                                                       
             int     a_b;                                                       
             int     a_c;                                                       
          ) myA ;                                                               
                                                                                
          myfunc()                                                              
          (                                                                     
        ...                                                                     
             asm mov  ax,myA.a_b                                                

                         - 435,436 -
                                                                                
             asm mov  bx,[di].a_c                                               
        ...                                                                     
          )                                                                     
                                                                                
          Мы объявили тип структуры,  названной myStruct, с 3 членами:          
     a_a,  a_b,  a_c,  а также объявили переменную myA типа  myStruct.          
     Первый  оператор  встроенного  ассемблера  заносит  величину   из          
     myA.a_b в регистр AX.  Второй заносит величину по адресу  [di]  +          
     смещение (a_c) в регистр BX (оператор берет адрес,  сохраненный в          
     DI,  и добавляет к нему смещение а_с от начала myStruct).  В этой          
     последовательнности  ассемблеровские операторы производят следую-          
     щую программу:                                                             
                                                                                
          mov   ax,  DGROUP : myA+2                                             
          mov   bx,  [di+4]                                                     
                                                                                
          Для чего вам может это понадобиться сделать?  Если вы загру-          
     жаете регистр (такой, как DI) адресом структуры типа myStruct, то          
     вы можете использовать имена, для того чтобы непосредственно ука-          
     зывать элементы. Имя элемента может быть использовано в любой по-          
     зиции,  где  численная константа разрешена в операнде утверждения          
     ассемблера.                                                                
                                                                                
                                                                                
                                                                                
          Элемент структуры должен начинаться с точки  (.)  для  того,          
     чтобы указывать,  что используется имя члена структуры, а не нор-          
     мальный идентификатор Си.  Имена элементов замещаются в ассембле-          
     ровском коде числовыми смещениями элементов в структуре (числовое          
     смещение а_с есть 4),  но тип информации не сохраняется.  Поэтому          
     элементы  могут  быть использованы только во время компиляции ас-          
     семблеровских операторов.                                                  
                                                                                
          Однако, есть одно ограничение: если две структуры используют          
     одно  и  то  же имя элемента,  вы должны имя элемента заключить в          
     круглые скобки, сделав его как бы ядром.                                   
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 437,438 -
                                                                                
               Использование команд перехода и меток                            
     -----------------------------------------------------------------          
                                                                                
          Во встроенном ассемблере вы можете использовать любые услов-          
     ные и безусловные команды перехода,  а также команды  цикла.  Все          
     они имеют силу только внутри функции. Так как в операторах asm не          
     могут быть  даны  метки,  команды  перехода  должны  использовать          
     Си-метки операторов goto как объекты перехода. Прямые far-перехо-          
     ды не могут генерироваться.                                                
                                                                                
          Также допускаются косвенные переходы.  Для того,  чтобы  ис-          
     пользовать  косвенный  переход,  вы можете применить имя регистра          
     как операнд команды перехода.  В следующей программе переход идет          
     к Си-метке "а" оператора goto.                                             
                                                                                
          int    x()                                                            
          {                                                                     
          a:                               /* Это goto метка "а" */             
              . . .                                                             
              asm jmp  a                   /* Переход к метке "а" */            
              . . .                                                             
          }                                                                     
                                                                                
               Функции прерываний                                               
     -----------------------------------------------------------------          
                                                                                
          Микропроцессор 8086 резервирует первые 1024 байта памяти для          
     набора 256 удаленных указателей (называемых векторами прерываний)          
     на специальные системные подпрограммы обработки  прерываний.  Эти          
     подпрограммы вызываются выполнением команды                                
                                                                                
          int <целое число>,                                                    
                                                                                
     где <целое число> принимает значения от 0h до ffh. По этой коман-          
     де компьютер сохраняет программный сегмент (CS), указатель команд          
     (IP) и флажки состояния, запрещает прерывания и затем делает уда-          
     ленный  переход  к  программе обработки,  соответствующей вектору          
     прерывания. Например, прерывание                                           
                                                                                
          int 21h                                                               
                                                                                
     вызывает большинство ДОС подпрограмм. Одако многие вектора преры-          
     ваний  незадействованы. Вы можете написать свой собственный обра-          
     ботчик прерывания и установить удаленный указатель на него в  од-          
     ном из неиспользуемых векторов.                                            

                         - 439,440 -
                                                                                
                                                                                
          Для  того, чтобы написать обработчик прерывания на Турбо Си,          
     необходимо определить функцию типа  interrupt.  Более  точно  это          
     должно выглядеть так:                                                      
                                                                                
          void  interrupt myhandler (bp, di, si, es, dx, cx,bx, ax,             
                                     ip, cs, flags, ...);                       
                                                                                
          Как вы можете видеть, все регистры передаются как параметры,          
     так  что  вы  можете использовать и модифицировать их в программе          
     без применения псевдопеременных, рассмотренных ранее в этой  гла-          
     ве.  Также  заметим, что вы можете иметь дополнительные параметры          
     (flags, ...), передаваемые обработчику. Они определяются соответ-          
     ственно.                                                                   
                                                                                
          Функции типа interrupt будут автоматически сохранять ( в до-          
     полнение к SI, DI и BP) регистры от AX до DX, ES и DS. Эти же ре-          
     гистры восстанавливаются при выходе из обработчика прерывания.             
                                                                                
          Во  всех моделях памяти обработчики преревания могут исполь-          
     зовать арифметические операции с плавующей точкой.  Всякий  обра-          
     ботчик  прерываний,  который  использует  сопроцессор 8087/80287,          
                                                                                
     должен сохранять его состояние и при выходе восстанавливать  его.          
     Функция  прерывания  может видоизменять свои параметры. Изменение          
     объявленных параметров будет модифицировать  соответствующий  ре-          
     гистр  при  выходе из обработчика. Это удобно, когда используется          
     обработчик прерывания для обслуживания запросов пользователя,  то          
     есть  аналогично  сервисному  запросу ДОСа INT 21. Также заметим,          
     что  выход  из  обработчика  прерываний  осуществляется  командой          
     IRET(возвращение из прерывания).                                           
                                                                                
          Зачем  же вам писать свой собственный обработчик прерываний?          
     Прежде всего затем, чтобы он работал,  как  работают  большинство          
     резидентных программ. Они устанавливают сами себя как обработчики          
     прерываний. Таким образом, всякий раз, когда происходят какие-ли-          
     бо  специальные  или  периодические действия (переключение часов,          
     нажатие клавиши на клавиатуре и т.п.), эти программы перехватыва-          
     ют обращения к подпрограммам обработки прерываний  и  определяют,          
     какое действие необходимо произвести. Выполнив это, они затем мо-          
     гут  передать управление исходной подпрограмме обработки прерыва-          
     ний.                                                                       
                                                                                
                                                                                
                                                                                

                         - 441,442 -
                                                                                
               Примеры программирования на низком уровне                        
     -----------------------------------------------------------------          
                                                                                
          Вы уже видели несколько примеров того, как использовать раз-          
     личные  приемы программирования на низком уровне в вашей програм-          
     ме;  теперь пришло время заглянуть немного дальше.  Для начала вы          
     будете писать реальный обработчик прерываний, который делает неч-          
     то безобидное и хорошо видимое (или,  в данном случае, слышимое).          
     Это будет звуковой сигнал, вызванный откуда-нибудь.                        
                                                                                
          Во-первых, вам нужно написать функцию как таковую. Она будет          
     выглядеть таким образом:                                                   
                                                                                
          #include                                                       
                                                                                
          void interrupt mybeep(unsigned bp, unsigned dl, 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++)                                        
                ; /* пустой оператор*/                                          
                                                                                
             /* Теперь включить его на определенное время */                    
              outportb(0x61, bits | 2);                                         
              for (j = 0; j <= 100; j++)                                        
                ; /* другой пустой оператор*/                                   
              }                                                                 
                                                                                
             /* Восстановить установки управляющего порта */                    
              outportb(0x61, originalbits);                                     
          }                                                                     
                                                                                
          Далее необходимо написать функцию, которая установит ваш об-          

                         - 443,444 -
                                                                                
     работчик  прерывания.  Вы  передадите  ей  адрес написанной ранее          
     функции и соответствующий номер прерывания (0 ...255 или 0х00...0          
     хFF). Эта функция должна выполнить 3 действия:                             
                                                                                
          - запретить прерывания, чтобы во время модернизации  вектор-          
            ной таблицы не произошло ничего непредвиденного;                    
          - сохранить переданный адрес в соответствующем месте;                 
          - разрешить прерывания, чтобы все снова правильно работало.           
                                                                                
          Ваша подпрограмма настройки будет выглядеть так:                      
                                                                                
          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();                                                     
          }                                                                     
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 445,446 -
                                                                                
      Использование библиотек программ для работы с плавающей точкой            
     -----------------------------------------------------------------          
                                                                                
          В Си вы работаете с двумя типами чисел:  целыми (int, short,          
     long и т.п.) и с плавающей точкой (float,  double). Установленный          
     в вашей машине процессор легко работает с  целыми  значениями,  а          
     для работы с плавающей точкой он затрачивает больше времени и ре-          
     сурсов.                                                                    
                                                                                
          Однако, семейство процессоров iAPx86  имеет  соответствующее          
     семейство математических сопроцессоров 8087 и 80287. 8087 и 80287          
     (и тот и другой мы называем здесь сопроцессорами) - это специаль-          
     ные  арифметические процессоры,  которые могут быть установлены в          
     ваш РС.  Они выполняют команды с плавающей точкой  очень  быстро.          
     Если вы работаете с величинами,  в которых часто встречается пла-          
     вающая точка,  то вам непременно захочется работать с сопроцессо-          
     ром.   Центральный   процессор   в  вашем  РС  взаимодействует  с          
     8087/80287 посредством специальных прерываний.                             
                                                                                
          Турбо Си поможет гибко использовать программы непосредствен-          
     но для вашего РС и ваших нужд.                                             
                                                                                
                                                                                
          - Если  вы не будете  использовать значения с плавающей точ-          
     кой, сообщите об этом заранее компилятору.                                 
                                                                                
          - Если вам нужно использовать значения с  плавающей  точкой,          
     но  ваш РС не имеет арифметического сопроцессора (8087/80287), то          
     сообщите Турбо Си,  что выполнять компоновку в специальных  подп-          
     рограммах можно так же,  как если бы у вас был сопроцессор. В том          
     случае, если ваша программа работает в машине с сопроцессором, он          
     будет использоваться автоматически, и программа будет выполняться          
     очень быстро.                                                              
                                                                                
          - Если вы пишите программы для систем, которые имеют арифме-          
     тический сопроцессор,  то можете предписать компилятору Турбо  Си          
     использовать программу,   которая всегда работает с сопроцессором          
     8087/80287.                                                                
                                                                                
          В  следующих  примерах  для  ТСС и TLINK предполагается, что          
     файл TURBOC.CFG существует с корректной установкой маршрутов -L и          
     -I и что библиотека и начальные объектные файлы сохранены в  под-          
     каталоге, именуемом LIB.                                                   
                                                                                
                                                                                

                         - 447,448 -
                                                                                
               Эмуляция микросхемы 8087/80287                                   
     -----------------------------------------------------------------          
                                                                                
          Предположим, что вы хотите использовать величины с плавающей          
     точкой или вы имеете программу, использующую величины с плавающей          
     точкой,  но  ваш компьютер не имеет арифметического сопроцессора,          
     то будет ли выполняться ваша программа? Успокойтесь, Турбо Си по-          
     может в этой ситуации.                                                     
                                                                                
          В соответствии  с опцией эмуляции компилятор будет генериро-          
     вать программу так, как если бы 8087/80287 в РС был, но программа          
     будет  выполнять компоновку в библиотеке эмуляции (EMU.LIB).  При          
     выполнении программа будет использовать 8087/80287, если он есть;          
     в противном случае программа будет использовать специальное прог-          
     раммное обеспечение для 8087/80287 - эмулятор.                             
                                                                                
          Библиотека эмуляции работает так:                                     
                                                                                
          - когда  ваша  программа  начинает выполняться, то начальная          
     Си-программа определяет, есть ли 8087/80287 или нет;                       
                                                                                
          - если сопроцессор есть, то программа при помощи специальных          
                                                                                
     прерываний   для  8087/80287  разрешит  использование  микросхемы          
     8087/80287 для программы пользователя.                                     
                                                                                
          - если сопроцессора нет, то программа останавливает прерыва-          
     ния и переходит к эмуляционным подпрограммам.                              
                                                                                
          Пусть вы модифицировали RATIO.C таким образом:                        
                                                                                
          main()                                                                
         {                                                                      
             float  a,b,ratio;                                                  
                                                                                
             printf(" Введите два значения:  ");                                
             scanf("%f %f",%a,%b);                                              
             ratio = a/b;                                                       
             printf("Результат деления будет %0.2f\n",ratio);                   
          }                                                                     
                                                                                
                                                                                
          Если вы  используете  ТС (версию пользовательского интерфей-          
     са),  то нужно выйти в меню Options,  выбрать  Compiler,  выбрать          
     Code  generation  и  затем выбирать элемент Floating-point до тех          

                         - 449,450 -
                                                                                
     пор,  пока на следующем поле не прочтете Emulation. Когда вы ком-          
     пилируете и компонуете свою программу, Турбо Си автоматически вы-          
     бирает для нее соответствующие опции и библиотеки.                         
                                                                                
          Если вы используете ТСС (автономный компилятор),  то команд-          
     ная строка будет выглядеть следующим образом:                              
                                                                                
          tcc -mX ratio                                                         
                                                                                
          Если вы выполняете компоновку результирующей программы с ко-          
     мандной строки,  то должны точно определить  как  соответствующую          
     математическую  библиотеку  (зависящую от размера модели),  так и          
     файл EMU.LIB.  Опция эмуляции (-f) устанавливается по  умолчанию,          
     так что не нужно задавать ее, если ваш файл TURBOC.CFG не включа-          
     ет один из переключателей плавающей точки (-f- или -f87).                  
                                                                                
          Вызов TLINK будет выглядеть так:                                      
                                                                                
          tlink lib\c0X ratio, ratio, ratio, lib\emu.lib lib\mathX.lib          
                lib\cX.lib                                                      
                                                                                
     где X - буква, обозначающая соответствующую модель библиотеки.             
                                                                                
                                                                                
                                                                                
          Замечание: команда tlink записывается на одной строке.                
          Также помните, что порядок библиотек очень важен.                     
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 451,452 -
                                                                                
              Использование микросхемы математического                          
                      сопроцессора 8087/80287                                   
     -----------------------------------------------------------------          
                                                                                
          Если вы абсолютно уверены, что ваша программа будет работать          
     только на машинах,  имеющих микросхему 8087 или 80287,  то можете          
     создать  программу,  которая будет использовать преимущества этой          
     микросхемы. В этом случае результирующие .ЕХЕ файлы будут меньше,          
     так   как  Турбо  Си  не  будет  включать  подпрограммы  эмуляции          
     8087/80287 (EMU.LIB).                                                      
                                                                                
          Если вы используете интегрированную версию Турбо Си, то сле-          
     дует  перейти  в  меню  Options,  выбрать Compiler,  выбрать Code          
     generation и затем выбирать элемент Floating point  до  тех  пор,          
     пока на следующем поле не появится надпись "8087/80287". Когда вы          
     компилируете и компонуете свою программу,  Турбо Си будет автома-          
     тически выбирать для нее нужные опции и библиотеки.                        
                                                                                
          Если вы используете ТСС (автономный компилятор),  то нужно в          
     командной строке использовать опцию - f87, то есть:                        
                                                                                
          tcc -f87 -mX  ratio                                                   
                                                                                
                                                                                
                                                                                
          Это даст указание Турбо Си генерировать встроенные вызовы  к          
     микросхеме  8087/80287.  Когда  TLINK  вызвана,  файлы FP87.LIB и          
     MATHx.LIB будут скомпонованы.                                              
                                                                                
          Если вы выполняете компоновку результирующей программы вруч-          
     ную, то должны определить как соответствующую математическую биб-          
     лиотеку (зависящую от размера модели), так и библиотеку процессо-          
     ра FP87 следующим образом:                                                 
                                                                                
          tlink lib\c0X ratio, ratio, ratio, lib\fp87.lib                       
                lib\mathX.lib lib\cX.lib                                        
                                                                                
     где Х, как всегда, обозначает нужную модель библиотеки.                    
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 453,454 -
                                                                                
                 Если вы не используете плавающую точку...                      
     -----------------------------------------------------------------          
                                                                                
          Если ваша  программа  не  использует  никакие подпрограммы с          
     плавающей точкой, то компоновщик не будет задействовать соответс-          
     твующие  библиотеки (EMU.LIB или FP87.LIB совместно с MATHx.LIB),          
     даже если вы запишете их в командной строке.  Не включив эти биб-          
     лиотеки в командную строку компоновщика, вы можете оптимизировать          
     шаг выполнения компоновки (если в программе не используются  опе-          
     рации над числами с плавающей точкой).                                     
                                                                                
         Предположим, вы  хотите компилировать и компоновать следующую          
     программу (записанную в RATIO.C):                                          
                                                                                
          main()                                                                
         {                                                                      
            int a,b,ratio;                                                      
                                                                                
            printf ("Введите два значения:   ");                                
            scanf ("%d %d", %a,%b);                                             
            ratio = a/b;                                                        
            printf ("Результат деления будет %d\n",ratio);                      
                                                                                
          }                                                                     
                                                                                
          Так как эта программа не использует подпрограммы с плавающей          
     точкой,  то  вы  можете выбирать, компилировать ли ее с эмуляцией          
     плавающей точки или без плавающей точки вообще.                            
                                                                                
          Если вы используете интегрированную версию Турбо Си и решили          
     компилировать  с  эмуляцией,  то  выберите Compile to OBJ из меню          
     Compile (Эмуляция включена по умолчанию). Компоновщик будет вклю-          
     чать  библиотеки с плавающей точкой на шаге установки связей,  но          
     ни одна связь не будет реально установлена.                                
                                                                                
          Если вы хотите ускорить процесс компоновки, то можете  уста-          
     новить режим "без плавающей точки". Перейдите в меню Options, вы-          
     берите  Сompiler,  выберите  Code generation, а затем - положение          
     Floating point.                                                            
                                                                                
          Нажимая несколько  раз клавишу "Ввод" в циклах этой команды,          
     вы пройдете 3 опции:  None, Emulation и 8087/80287. Вам нужна оп-          
     ция  None.  Можете  нажать "Esc" 3 раза,  чтобы вернуться в стро-          
     ку-линейку меню (или нажмите F10).                                         
                                                                                

                         - 455,456 -
                                                                                
          Когда вы компилируете и компонуете эту же программу в режиме          
     Floating point, установленном в None, Турбо Си не пытается компо-          
     новать, используя математические подпрограммы с плавающей точкой.          
                                                                                
          Если вы используете TCC (автономный компилятор), то примени-          
     те опцию -f- в командной строке:                                           
                                                                                
          tcc  -f-  -mX  ratio.c                                                
                                                                                
          Это информирует Турбо Си о том, что в вашей программе вообще          
     нет команд с плавающей точкой,  а также о том, что использовалась          
     модель памяти Х,  где Х - буква, обозначающая желаемую модель па-          
     мяти (t = tiny (крохотная),  s = small (малая), c = compact (ком-          
     пактная),  m  = medium (cредняя),  l = large (большая),  h = huge          
     (огромная)).                                                               
                                                                                
          Так как RATIO.C - автономная программа, то ТСС будет автома-          
     тически вызывать TLINK и выполнять компоновку с COx.OBJ и Cx.LIB,          
     и в результате вы получите RATIO.EXE.                                      
                                                                                
          Если вы использовали опцию "только компиляция" (-c) в коман-          
     дной строке ТСС, то компоновку результирующей программы необходи-          
                                                                                
                                                                                
     мо выполнить вручную. В этом случае вам не нужно  специфицировать          
     какую-либо  математическую  библиотеку;  вызов  TLINK будет иметь          
     вид:                                                                       
                                                                                
          tlink lib\cOm ratio, ratio,  ratio,  lib\cx.lib                       
                                                                                
          По этой команде выполняется компоновка СОх.OBJ и RATIO.OBJ с          
     использованием  библиотеки Cx.LIB,  и создаются файлы RATIO.EXE и          
     RATIO.MAP.                                                                 
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 457,458 -
                                                                                
               Переменная среды 87                                              
     -----------------------------------------------------------------          
                                                                                
          Если вы строите свою программу с эмуляцией 8087/80287  (дру-          
     гими  словами,  выбираете  из меню Floating point...Emulation или          
     включаете опцию -f в командной строке ТСС),  то при запуске прог-          
     раммы  начальный модуль C0x.OBJ будет использовать алгоритм авто-          
     обнаружения 8087/80287. Это означает, что начальная программа бу-          
     дет автоматически проверять, доступен ли 8087/80287.                       
                                                                                
          Если 8087/80287  доступен,  то программа будет его использо-          
     вать; если не доступен, то программа воспользуется подпрограммами          
     эмуляции.                                                                  
                                                                                
          Существуют ситуации,  в которых вам может понадобиться отка-          
     заться от принятого по умолчанию метода  автообнаружения.  Напри-          
     мер, применяемая вами исполняющая система может иметь 8087/80287,          
     но вы хотите убедиться,  что ваша программа будет работать в сис-          
     теме без сопроцессора. Или ваша программа может потребоваться для          
     выполнения на РС-совместимой машине, но эта машина возвращает ал-          
     горитму  автообнаружения  неверную информацию (говоря,  что несу-          
     ществующий 8087/80287 доступен или наоборот).                              
                                                                                
                                                                                
          Турбо Си предоставляет опцию для замещения алгоритма автооб-          
     наружения начальной программы; эта опция - переменная среды 87.            
                                                                                
          Вы устанавливаете переменную среды 87 в приглашение  ДОС при          
     помощи команды SET следующим образом:                                      
                                                                                
          C> SET 87=N                                                           
                                                                                
     или                                                                        
                                                                                
          C> SET 87=Y                                                           
                                                                                
          Установка переменной среды 87 в N (для No) указывает началь-          
     ной программе, что вы не хотите использовать 8087/80287 (даже ес-          
     ли он присутствует в системе).                                             
                                                                                
          И, наоборот, установка переменной среды 87 в Y (для Yes) оз-          
     начает, что сопроцессор имеется, и вы хотите, чтобы ваша програм-          
     ма использовала его.  Предостережение программисту! Если вы уста-          
     навливаете 87=Y, а на самом деле 8087/80287 отсутствует в машине,          
     то программа потерпит крах и сгорит в логическом аду.                      

                         - 459,460 -
                                                                                
                                                                                
          Переменная среды 87 в состоянии замещать  устанавливаемый по          
     умолчанию алгоритм автообнаружения потому, что когда вы начинаете          
     выполнять свою программу,  начальная программа первым долгом про-          
     веряет, определена ли переменная среды 87.                                 
                                                                                
          - Если переменная среды 87 определена, то начальная програм-          
     ма не смотрит далее, и ваша программа выполняется в описанном ре-          
     жиме.                                                                      
                                                                                
          - Если переменная среды 87 не определена, то начальная прог-          
     рамма проходит через свой алгоритм автообнаружения для того, что-          
     бы  проверить, доступна ли микросхема 8087/80287, и программа ра-          
     ботает в соответствии с результатом этой проверки.                         
                                                                                
          Если переменная среды 87 определена (любым значением), но вы          
     хотите переопределять ее,  то введите следующее выражение в приг-          
     лашение ДОС:                                                               
                                                                                
          C> SET 87=                                                            
                                                                                
     (Это означает нажатие клавиши "Ввод" сразу после знака равенства)          
                                                                                
                                                                                
               Регистры и 8087/80287                                            
     -----------------------------------------------------------------          
                                                                                
          Существует 2 момента, относящихся к  регистрам,  которые  вы          
     должны учитывать, когда используете плавающую точку.                       
                                                                                
          Во-первых,  в  режиме эмуляции 8087/80287 регистровый цикли-          
     ческий переход не поддерживается.                                          
                                                                                
          Во-вторых, если вы используете плавающую точку совместно  со          
     встроенным  ассемблером, то необходимо быть особенно внимательным          
     при использовании регистров, потому что регистр 8087/80287 очища-          
     ется перед Турбо Си-вызовами функции. Вам понадобится  извлечь  и          
     сохранить  регистры  8087/80287  до  вызова функций, использующей          
     сопроцессор, даже если вы уверенны,  что  существует  достаточное          
     количество свободных регистров.                                            
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 461,462 -
                                                                                
               Использование matherr с плавающей точкой                         
     -----------------------------------------------------------------          
                                                                                
          Если  во  время выполнения программы обнаруживается ошибка в          
     одной из подпрограмм с плавающей точкой, то эта подпрограмма  ав-          
     томатически  вызывает  _matherr  с несколькими аргументами. Затем          
     _matherr заполняет своими аргументами особую  структуру  (опреде-          
     ленную  в math.h) и вызывает matherr с указателем на эту структу-          
     ру.                                                                        
                                                                                
          matherr - это зацепка, которая позволяет вам  написать  свою          
     собственную  подпрограмму  анализа  ошибки. По умолчанию, matherr          
     только возвращает 0, и ничего более. Однако, вы  можете  изменить          
     matherr  соответствующим образом для того, чтобы создать подпрог-          
     рамму обработки ошибок плавающей  точки.  Такая  модифицированная          
     matherr возвращает величину, отличную от 0, если ошибка была исп-          
     равлена, и 0 в противном случае.                                           
                                                                                
          Для   получения  более  подробной   информации  о  matherr и          
     _matherr смотри описание matherr в части 2 "Справочного руководс-          
     тва по Турбо Си".                                                          
                                                                                
                                                                                
                                                                                
                          Предостережения и советы                              
                          ------------------------                              
                                                                                
                         Как Турбо Си использует RAM                            
     -----------------------------------------------------------------          
                                                                                
          Во время  компиляции Турбо Си не хранит на диске промежуточ-          
     ные структуры данных (на диск Турбо Си записывает только  объект-          
     ные файлы .OBJ).  Для всех промежуточных структур данных Турбо Си          
     использует RAM. Поэтому вы можете столкнуться с сообщением OUT OF          
     MEMORY..., если для компилятора не достаточно памяти.                      
                                                                                
          Чтобы решить эту проблему, нужно сделать ваши функции меньше          
     или разделить  файл,  который имеет большие функции.  Можно также          
     стереть любые резидентные программы,  установленные вами,   чтобы          
     освободить большую память для Турбо Си.                                    
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                

                         - 463,464 -
                                                                                
                                                                                
               Нужно ли вам использовать Паскаль-соглашения?                    
     -----------------------------------------------------------------          
                                                                                
          Нет, если только вы прочли эту главу и хорошо разобрались  в          
     ней.                                                                       
                                                                                
          Запомните, что если вы устанавливаете связи главного файла с          
     помощью Паскаль-соглашений по вызову, то должны объявить main как          
     Си-функцию:                                                                
                                                                                
          cdecl  main(int argc, char * argv[], char * envp[])                   
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
               Заключение                                                       
     -----------------------------------------------------------------          
                                                                                
          Вы увидели,  как  использовать все 3 аспекта низкоуровневого          
     программирования в Турбо Си (псевдопеременные, встроенный ассемб-          
     лер, функции прерывания); вы также изучили связь с другими языка-          
     ми,  включая ассемблер; вы были ознакомлены с некоторыми деталями          
     использования подпрограмм с плавающей точкой; вы поняли, как вза-          
     имодействуют различные модели памяти в 8086. Теперь вы можете ис-          
     пользовать  все эти приемы для получения полного контроля над ва-          
     шим компьютером. Наилучших вам пожеланий!