 
                          ГЛАВА 3
 
              Программирование на Турбо-Паскале
 
     Язык программирования Паскаль был разработан  Николасом
Виртом  в  начале  70-х годов для обучения программированию.
Поэтому он наиболее удобен как первый изучаемый  язык  прог-
раммирования.  Если у вас есть опыт программирования на дру-
гих языках, вам будет нетрудно освоиться и с Паскалем.
     Для первого ознакомления с программированием на Паскале
в  настоящей  главе описываются основные элементы языка и их
использование в программах. Конечно, эта глава не может  ох-
ватить  всех  аспектов  программирования на Паскале. Если вы
начинающий программист на Паскале, вам лучше приобрести  ко-
пию "Turbo Pascal Tutor" ("Учебник по Турбо-Паскалю"), кото-
рый является полным учебным пособием по программированию  на
Паскале и использованию версии 5.0 Турбо-Паскаля и поставля-
ется в виде книги и диска.
     Прежде, чем читать эту главу, возможно, имеет смысл об-
ратиться к Главе 7 "Все об интегрированной среде" и Приложе-
нию В "Использование реадктора", где описывается использова-
ние меню и текстового редактора в Турбо-Паскале. Вам следует
установить Турбо-Паскаль (сделать рабочую копию диска с Тур-
бо-Паскалем  или  скопировать файлы на жесткий диск) в соот-
ветствии с описанием из Главы 1. Удостоверьтесь, что Вы соз-
дали  файл TURBO.ТР или установили файл с расширением .EXE с
помощью ТINSТ.ЕХЕ (см. Приложение  D).  В  противном  случае
Турбо-Паскаль  не  будет  знать, где размещаются стандартные
модули TURBO.ТРL и файл конфигурации. (Исключение составляет
случай,  когда  вы  работаете в операционной системой версии
3.х  и  эти  файлы  находятся  в  том  же  каталоге,  что  и
TURBO.ЕХЕ).
     Теперь, когда это все сделано, можно приступить к  изу-
чению программирования на Турбо-Паскале.

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

     Данные
     Это константы, переменные и структуры, содержащие числа
(целые  и вещественные), текст (символы и строки) или адреса
(переменных и структур).
 
     Операции
     Операции осуществляют присваивание значений, их  комби-
нирование  (сложение, деление и так далее) и сравненние зна-
чений (равные, не равные и так далее).
 
     Вывод
     Это означает запись информации на экран, на диск или  в
порт ввода/вывода.
 
     Условное выполнение
     Условное выполнение предполагает выполнение набора  ко-
манд  в случае, если выполняется (является истинным) некото-
рое условие (если это условие не выполняется, то эти команды
пропускаются  или  же  выполняется  другой набор команд) или
если некоторый элемент данных  имеет  некоторое  специальное
значение или значение из некоторого спектра.
 
     Циклы
     Благодаря циклам  некоторый  набор  команд  выполняется
повторно  или фиксированное число раз, или пока является ис-
тинным некоторое условие, или пока некоторое условие не ста-
ло истинным.
 
     Подпрограммы
     Подпрограммы представляют собой наборы инструкций,  ко-
торые  имеют самостоятельное имя и которые могут быть выпол-
нены из любого места программы при обращении к ним по имени.
 
     Теперь рассмотрим, как эти элементы используются в Тур-
бо-Паскале.
 
                        Типы данных
 
     При написании программы Вы имеете дело  с  информацией,
которая,  как  правило, относятся к одному из следующих пяти
основных типов: целые числа, вещественные числа,  символы  и
строки, булевские выражения и указатели.
     Целые числа - это числа, с помощью которых  вы  учились
считать (например, 1,5,-21 и 752).
     Вещественные числа  имеют  дробную  часть  (3.14159)  и
экспоненту  (2.579х10**24). Иногда их также называют числами
с плавающей запятой.
     К символам относятся все буквы алфавита, знаки и  числа
0-9.  Они  могут использоваться по отдельности (а,Z,!,3) или
соединяться друг с другом в строки ('Это только проверка').
     Булевы выражения могут принимать одно из двух значений:
True  или False (истина или ложь). Они используются в услов-
ных выражениях, о которых речь будет идти дальше.
     Указатели содержат адрес ячейки в памяти компютера, ко-
торая в свою очередь содержит некоторую информацию.
 
                 Типы целочисленных данных
 
     В стандартном Паскале целочисленный тип данных  опреде-
ляется как содержащий значения в интервале от -МахInt до 0 и
от 0 до MaxInt, где MaxInt - наибольшее возможное целое зна-
чение, допустимое для используемого компьютера. В Турбо-Пас-
кале поддерживается целочисленный тип,  MaxInt  определяется
равным 32767 и допускается значение -32768. Переменная цело-
численного типа занимает два байта.
     Турбо-Паскаль, кроме того, поддерживает пять других це-
лочисленных  типов данных, каждый из которых имеет свой диа-
пазон значений. В Таблице 3.1 приведены все пять целочислен-
ных типов.

                                             Таблица 3.1
                 Целочисленные типы данных
------------------------------------------------------------
Тип                         диапазон         размер в байтах
------------------------------------------------------------
целое длиной в байт          0...255                  1
короткое целое            -128...127                  1
целое                   -32768...32767                2
целое длиной в слово         0...65535                2
длинное целое      -2147483648...2147483647           4
------------------------------------------------------------
 
     Примечание. Турбо-Паскаль позволяет использовать  шест-
надцатеричные  целые значения (с основанием 16). При опреде-
лении шест- надцатеричной константы  перед  ней  указывается
знак  доллара  ($);  например, $27=39 (в десятичном исчисле-
нии).
 
                  Типы вещественных данных
 
     В стандартном Паскале вещественный тип данных представ-
лен в виде значений с плавающей запятой, содержащих мантиссу
(дробную часть), умноженную на экспоненту (степень 10).  Ко-
личество  цифр  (так  называемых значащих цифр) в мантиссе и
диапазон значений экспоненты зависят от компьютера.  В  Тур-
бо-Паскале  вещественный тип имеет длину в 6 байт, допускает
11 значащих цифр и использует диапазон экспоненты от 10**-38
до 10**38.
     Кроме того, если в вашей системе имеется математический
сопроцессор  и вы указали директиву компилятора для числовой
поддержки или параметр среды программирования {$N+}, то Тур-
бо-Паскаль будет поддерживать Стандарт IЕЕЕ 754 для двоичной
арифметики с плавающей запятой. В  этом  случае  добавляются
типы  данных  с  одинарной точностью, с двойной точностью, с
повышенной точностью и сложные. Данные с одинарной точностью
используют  4  байта,  допускают  7 значащих цифр и диапазон
экспоненты от 10**-38 до 10**38; данные с двойной  точностью
используют  8  байт,  допускают  15 значащих цифр и диапазон
экспоненты от 10**-38 до 10**38, а данные с повышенной  точ-
ностью используют 10 байт, допускают 19 значащих цифр и диа-
пазон экспоненты от 10**-4931 до 10**4931.

                                                Таблица 3.2
                  Типы вещественных данных
------------------------------------------------------------
Тип                     Диапазон         Значащие   Размер в
                                           цифры     байтах
------------------------------------------------------------
вещественный            10**-38..10**38      11           6
с одинарной точностью   10**-38..10**38       7           4
с двойной точностью     10**-38..10**38       8          15
с повышенной точностью  10**-4931..10**4931  10          19
сложный                 2**63..2**63-1        8          ??
------------------------------------------------------------
 
     Войдем в  редактор  Турбо-Паскаля  и  введем  следующую
программу:

program DoRatio;
var
  A,B    : integer;
  Ratio  : real;
begin
  Write('Введите два числа: ');
  Readln(A,B);
  Ratio := A div B;
  Writeln('Отношение равно ',Ratio)
end.
 
     Запишем эту программу в файл DORATIO.PAS, вызвав основ-
ное  меню и выбрав команду FILE/WRIТЕ (Файл/Записать). Затем
нажмем клавишу R с тем, чтобы  откомпилировать  и  выполнить
программу. Введем два значения (например, 10 и 3) и получаем
результат (3.000000).
     Вы, вероятно, ожидали  ответа  3.3333333333,  а  вместо
него  получили  3. Это произошло потому, что был использован
оператор div, который выполняет деление целых чисел. Поэтому
необходимо исправить оператор div на следующий:
 
     Ratio := A / B;
 
Сохраните код на диске (нажав F2),  затем  оттранслируйте  и
выполните  программу. Теперь результатом будет 3.3333333333,
как вы и ожидали. Использование оператора деления (/)  обес-
печивает наиболее точный результат - вещественное число.
 
             Типы символьных и строковых данных
 
     Теперь вы знаете, как записываются числа в Турбо-Паска-
ле, а как обстоят дела с символами и строками? Паскаль пред-
лагает встроенный тип данных char, имеющий длину в 1 байт  и
содержащий  только один символ. Символьные константы выража-
ются символом, заключенным в  одиночные  кавычки  (например,
'А', 'е', '?', '2'). Отметим, что '2' означает символ 2 в то
время, как 2 означает целое число 2 (а 2.0 означает  вещест-
венное число 2).
     Ниже приводится модификация программы DORATIO, позволя-
ющая выполнять ее повторно несколько раз (здесь используется
цикл repeat..until, о котором речь пойдет ниже):
 
program DoRatio;
var
  A,B    : integer;
  Ratio  : real;
  Ans    : char;
begin
  repeat
    Write('Введите два числа: ');
    Readln(A,B);
    Ratio := A / B;
    Writeln('Отношение равно ',Ratio)
    Write('Повторить+ (Y/N)  ');
    Readln(Ans)
  until (Ans = 'n') or (Ans = 'N')
end.
 
     После вычисления коэффициента, программа выдает сообще-
ние:
 
     Повторить? (Y/N)

и ждет, пока вы введете один из указанных символов и нажмете
клавишу "Enter". Если вы введете строчную n или прописную N,
то условие until будет выполнено и цикл завершится; в обрат-
ном  случае  программа  возвратится на оператор repeat и все
будет повторено заново.
     Отметим, что n не есть то же самое,  что  N,  поскольку
они имеют различные значения в коде АSCII. Символы представ-
лены  в  коде  АSCII,  то  есть  каждый  символ  имеет  свое
8-битовое значение (запомните, что символы занимают 1 байт).
В Приложении Д "Справочного руководства" приведен список ко-
дов АSCII для всех символов.
     В Турбо-Паскале предлагается два дополнительных способа
представления  символьных  констант:  с помощью символа каре
(^) и с помощью символа номера (#). Символы с кодами от 0 до
31  известны  как управляющие символы (поскольку исторически
они использовались для управления операциями телетайпа). Они
обозначаются  с  помощью  своих аббревиатур (CR для carriage
return (возврат каретки), LF - для linefeed  (перевод  стро-
ки),  ЕSС  -  для  escape (выход) и так далее) или с помощью
слова Сtrl, за которым следует соответствующая буква (значе-
ние  буквы  образуется  путем  прибавления 64 к управляющему
коду). Например, управляющий символ с кодом АSCII 7 известен
как  Bel (Звонок) или Сtrl-G. Турбо-Паскаль позволяет предс-
тавлять эти символы с помощью знака  каре  (^),  за  которым
стоит  соответствующая  буква (или символ). Так, ^G является
допустимым представлением в прог- рамме для Сtrl-G, и Вы мо-
жете использовать такие операторы, как Writeln(G), что вызо-
вет подачу короткого звукового сигнала. Этот метод,  однако,
применим только к управляющим символам.
     Кроме того, вы можете представлять  любой  символ,  ис-
пользуя  знак  номера  (#),  за  которым следует код символа
АSCII. Так, #7 - это то же самое, что и ^G,  #65 - то же са-
мое,  что  и  'А',  а  #233 представляет один из специальных
графических символов для персонального компьютера IBM РС.

                     Определение строки
 
     Отдельные символы - это хорошо, но как  быть  в  случае
строк  символов?  Ведь  в  большинстве  случаев используются
именно они. Если в стандартном Паскале не поддерживается от-
дельный тип строковых данных, то в Турбо-Паскале эта возмож-
ность реализована. Рассмотрим следующую программу:
 
program Hello;
var
  Name  : string[30];
begin
  Write('Как Вас зовут+ ');
  Readln(Name);
  Writeln('Здравствуйте, ',Name)
end.
 
В ней переменная Name описывается с типом string и  для  нее
отводится  пространство  под  30  символов. Кроме того, Тур-
бо-Паскаль отводит еще один байт, который  содержит  текущую
длину  строки.  Поэтому  независимо  от того, какой длины Вы
вводите имя в ответ на подсказку, по оператору Writeln  рас-
печатывается имя только указанной длины. Если вы введете имя
длиной более 30 символов, то в этом случае только первые  30
символов  будут использоваться, а остальные будут проигнори-
рованы.
     При  описании  строковой  переменной  можно  указывать,
сколько  символов (до 255) она может содержать. Если вы опи-
сываете переменную (или параметр) с типом string и не указы-
ваете  длину, то в этом случае предполагается длина по умол-
чанию в 255 символов.
     Турбо-Паскаль предлагает несколько встроенных  процедур
и  функций  для работы со строками. Их описание дано в Главе
16 "Справочного руководства".
 
                    Тип булевских данных
 
     Встроенный тип булевских данных может иметь два  значе-
ния True и False (истина и ложь). Вы можете описать перемен-
ную с этим типом, затем присвоить ей значение True или False
или же присвоить ей значение выражения, которое при вычисле-
нии принимает одно из этих значений.
     Булевское выражение - это такое выражение, которое при-
нимает значение True или False. Оно состоит из выражений от-
ношений, булевских операций, булевых переменных и/или других
булевых выражений. Например, следующий оператор while содер-
жит булевскоевыражение:
 
     while (Index <= Limit) and not Done do ...
 
Указанное булево выражение содержит все,  заключенное  между
ключевыми словами while и do, и предполагает, что Done явля-
ется переменной (или функцией) с булевским типом.
 
                   Тип данных "указатель"
 
     Все рассмотренные выше типы данных содержат только дан-
ные.  Указатель  (или ссылочный тип) содержит другой вид ин-
формации - адреса. Указатель представляет собой  переменную,
которая  содержит не сами данные, а адрес в памяти (ОП), где
эти данные хранятся. Другими словами, он указывает  на  дан-
ные, как, например, адресная книга или индекс.
     Обычно (но необязательно) указатель является  специфич-
ным для некоторого другого типа данных. Рассмотрим следующие
описания:
 
  type
    Buffer = string[255];
    BufPtr = ^Buffer;
  var
    Buf1   : Buffer;   
    Buf2   : BufPtr;
 
     Тип данных Buffer является  только  другим  именем  для
string[255],  в то время как тип Bufptr определяет указатель
на Buffer. Переменная Buf1 имеет тип  Buffer,  она  занимает
256  байт  памяти. Переменная Buf2 имеет тип BufPtr; она со-
держит 32-битовый адрес и занимает только 4 байта памяти.
     На что указывает Buf2? В  текущий  момент  ни  на  что.
Прежде  чем использовать BufPtr, необходимо выделить некото-
рое количество памяти и адрес этой памяти запомнить в  Buf2.
Для этого используется процедура New:
 
     New(Buf2);
 
Поскольку Buf2 указывает на тип Buffer, этот  оператор  соз-
даст  256-байтовый  буфер  в  памяти  и его адрес поместит в
Buf2.
     Как используются данные, на которые указывает  Buf2+  С
помощью операции разыменования . Например, допустим, что вам
нужно записать строку как в Buf1, так и в буфер, на  который
указывает Buf2. В этом случае операторы будут иметь вид:
 
     Buf1 := 'Эта строка хранится в Buf1.'
     Buf2 := 'Эта строка там, куда указывает Buf2.'
 
     Отметим различие между Buf2  и  Buf2.  Buf2  обозначает
4-байтовую    переменную    указателя;    Buf2    обозначает
256-байтовую строковую переменную, адрес которой хранится  в
Buf2.
     Каким образом очищается память,  на  которую  указывает
Buf2?  С  помощью  процедуры  Dispose. Dispose делает память
доступной для другого использования. После того, как к  ука-
зателю  применена  процедура  dispose,  обязательно  следует
присвоить этому указателю (встроенное) значение nil. Это оз-
начает, что указатель ни на что больше не указывает:
 
     Dispose(Buf2);
     Buf2 := nil;
 
Отметим, что значение nil присваивается Buf2, а не Buf2.
     На этом заканчивается наше краткое рассмотрение  указа-
телей;  в  любом  хорошем  описании языка Паскаля Вы найдете
сведения, как и где их использовать.
 
                       Идентификаторы
 
     До сих пор мы давали имена переменным,  не  заботясь  о
тех  ограничениях,  которые  могут быть для них установлены.
Теперь рассмотрим эти ограничения.
     Имена, которые даются константам, типам  данных,  пере-
менным  и функциям, называются идентификаторами. Упоминаемые
дальше идентификаторы используются для:
 
 integer,real,string   Встроенных типов данных
 Hello,DoSum,DoRatio   Основной функции программы
 Name,A,B,Sum,Ratio    Переменных, определяемых
 пользователем
 Write,Writeln,Readln  Встроенных процедур
 
     Турбо-Паскаль имеет несколько новых правил  использова-
ния идентификаторов. Ниже дано краткое их описание:
 
     - Все идентификакторы должны начинаться с буквы  (a...z
или  A...Z).  В  остальной части идентификатора могут содер-
жаться буквы, знаки подчеркивания и/или цифры (0...9); ника-
кие другие символы не допустимы.
     - Идентификаторы используются без учета  регистра,  это
означает, что строчные буквы (a...z) рассатриваются тождест-
венными прописным буквам (A...Z).  Например,  идентификаторы
idnx, Idnx и IDNX являются тождественными.
     - Идентификаторы могут иметь  любую  длину,  но  только
первые 63 символа являются значащими.
 
                          Операции
 
     После того, как данные получены программой (и присвоены
переменным),  их  нужно  каким-то  образом обрабатывать, для
этого в вашем распоряжении имеются  операции.  Эти  операции
относятся к восьми типам: операции присваивания, унарные/би-
нарные операции, поразрядные операции (операции над битами),
операции  отношений, логические операции, операции над адре-
сами, операции над множествами и операции над строками.
     Большинство операций в Паскале являются  бинарными,  то
есть  имеют  два  операнда;  остальные являются унарными, то
есть имеют только  один  операнд.  Бинарные  операции  имеют
обычную  алгебраическую форму, например, a + b. Унарная опе-
рация предшествует своему операнду, например, -b.
     В сложных выражениях порядок выполнения операций  уста-
навли- вается правилами приоритета (см. Таблицу 3.3).
 
                                         Таблица 3.3
                   Приоритет операций
------------------------------------------------------------
Операции                   Приоритет         Категории
------------------------------------------------------------
@,not                    Первый(высший)     унарные операции
*,/,div,mod,and,shl,shr  Второй            
 мультипликативные
                                            операции
+,-,or,xor               Третий             аддитивные
 операции
=,<>,<,>,<=,>=,in        Четвертый(низший)  операции
 отношения
------------------------------------------------------------
 
     Операции с равным приоритететом, как правило,  выполня-
ются справа налево, хотя иногда компилятор может перегруппи-
ровать операнды в целях генерации оптимального кода.
     Последовательности операций  с  одинаковым  приоритетом
вычисляются  слева направо. Выражения, заключенные в скобки,
вычисляются первыми независимо от того, каковы  предшествую-
щие или последующие операции.
 
                   Операции присваивания
 
     Базовой операцией является операция  присваивания,  как
например, в Ratio := А/В. В Паскале знак присваивания предс-
тавляет собой комбинацию  из  двоеточия  и  знака  равенства
(:=).  В  приведенном  примере  значение А/В справа от знака
присваивания присваивается переменной Ratio,  которая  стоит
слева.
 
                Унарные и бинарные операции
 
     Паскаль поддерживает стандартный набор бинарных арифме-
тических  операций, которые выполняются над целыми и вещест-
венными значениями:
 
     - умножение (*)
     - деление целых чисел (div)
     - деление вещественных чисел (/)
     - взятие по модулю (mod)
     - сложение (+)
     - вычитание (-)
 
     Кроме  того,  Паскаль  поддерживает  унарные  операции:
унарный  минус  (a+(-b)),  который  выполняет  дополнение до
двух, и унарный плюс (a+(+b)), который ничего не  выполняет,
но введен для полноты.
 
                    Поразрядные операции
 
     Для действий над битами Паскаль имеет следующие  опера-
ции:
 
 shl(сдвиг влево)   Сдвигает биты влево на указанное количество 
                    разрядов, заполняя остающиеся справа разряды 
                    нулями.
 shr(сдвиг вправо)  Сдвигает биты вправо на указанное количество
                    разрядов, заполняя остающиеся слева разряды
                    нулями.
 and                Выполняет логическое умножение and над каждой
                    соответствующей парой битов, возвращая 1,
                    если оба бита содержат 1, или 0 в обратном 
                    случае.
 or                 Выполняет логическое сложение or над каждой
                    соответствующей парой битов, возвращая 0,
                    если оба бита содержат 0, или 1 в обратном 
                    случае.
 xor                Выполняет логическое сложение по модулю 2 над
                    каждой соответствующей парой битов, возвращая
                    1, если биты отличны друг от друга, или 0 в
                    обратном случае.
 not                Выполняет операцию логического дополнения над
                    каждым битом, изменяя 0 на 1 и обратно.
 
     Эти операции позволяют выполнять действия на очень низ-
ком уровне с целочисленными значениями.
 
                     Операции отношений
 
     Операции отношения позволяют сравнивать  два  значения,
возвращая  в  результате  булевское значение Тrue или False.
Ниже приводятся операции отношения, реализуемые в Паскале:
 
     >      больше
     >=     больше или равно
     <      меньше
     >=     меньше или равно
     =      равно
     <>     не равно
     in     есть элемент
 
     Почему необходимо знать, имеет ли место  значение  Тrue
или False? Введем следующую программу:
 
program TestGreater;
var
  A,B    : integer;
  Test   : boolean;
begin
  Write('Введите два числа: ');
  Readln(A,B);
  Test := A > B;
  Writeln('A больше B', Test);
end.
 
При выполнении программы будет напечатано Тrue, если А боль-
ше В, или False, если А меньше или равно В.
 
                    Логические операции
 
     Имеется четыре логические операции - and, xorr,  orr  и
not,  которые хотя и аналогичны поразрядным операциям, но не
тождественны им. Логические операции выполняются  над  логи-
ческими  значениями  (Тrue  и False), позволяя комбинировать
выражения отношения, булевы переменные и  булевские  выраже-
ния.
     Они отличаются от соответствующих поразрядных опрераций
следующим образом:
 
     - Логические операции всегда в качестве результата име-
ют  Тrue или False (то есть булево значение), в то время как
поразрядные операции выполняют побитные действия  над  цело-
численными значениями.
     - Эти операции не позволяют комбинировать булевы и  це-
лочисленные  выражения;  другими словами, выражение Flag and
Indx недопустимо, если Flag имеет булев тип, Indx - целочис-
ленный тип (и наоборот).
     - По умолчанию логические операции and и or выполняются
по  короткой  схеме,  а  операции xor и not - нет. Допустим,
имеется выражение exp1 and exp2. Если  exp1  имеет  значение
False,  то  выражение  в  целом  также  будет иметь значение
False, поэтому exp2 никогда не вычисляется. Аналогичным  об-
разом,  если дано выражение exp1 or exp2, то exp2 никогда не
вычисляется, если exp1 имеет значение Тrue. Вы можете  уста-
новить вычисление булева выражения по полной схеме с помощью
директивы компилятора {$В+} или соответствующей опции  инте-
рактивной среды программирования.
 
                   Операции над адресами
 
     Паскаль поддерживает две специальные операции над адре-
сами:  операцию вычисления адреса (@) и операцию разыменова-
ния (^).
     Операция @ возвращает адрес заданной  переменной.  Если
Sum  является переменной целочисленного типа, то @Sum предс-
тавляет собой адрес (ячейку в памяти) этой переменной.  Ана-
логичным  образом,  если СhrPtr является указателем для типа
char, то СhrPtr представляет собой символ, на который указы-
вает СhrPtr.
 
                  Операции над множествами
 
     Операции над множествами выполняются в  соответствии  с
правилами  теории множеств. Операции над множествами включа-
ют:
 
      +     объединение
      (-)   разность
      *     умножение
 
                   Операции над строками
 
     Единственной операцией над строками  является  операция
+, которая используется для конкатенации двух строк.
 
                           Вывод
 
     Может показаться забавным, что о выводе речь идет рань-
ше,  чем  о вводе, однако программа без вывода информации не
представляет никакой пользы. Вывод обычно  принимает  форму,
зависящую от того, куда выводится информация: на экран (сло-
ва и изображения), или на запоминающее устройство (гибкие  и
жесткие  диски),  или  в порт ввода/вывода (последовательный
порт или порт печатающего устройства).
 
                     Процедура Writeln
 
     Вы уже использовали наиболее  распространенную  функцию
вывода  в Паскале - подпрограмму Writeln. Назначение Writeln
- выводить информацию на экран. Ее формат прост  и  гибок  в
использовании:
 
     Writeln(элемент,элемент,...);
 
где каждый элемент - это то, что нужно напечатать на экране.
Элементом  может  быть литеральное значение, например, целое
или вещественное  число  (3,42,-1732.3),  символ  ('а','Z'),
строка  ('Hello,world')  или  булево  значение (Тrue). Кроме
того, им может быть именованная константа, переменная, разы-
менованный указатель или обращение к функции, если она возв-
ращает значение, которое имеет целый, вещественный, символь-
ный,  строковый или булев тип. Все элементы печатаются в од-
ной строке в заданном порядке. После этого курсор устанавли-
вается  в  начало  следующей строки. Если вы хотите оставить
курсор на той же строке после последнего  элемента,  то  ис-
пользуйте оператор:
 
     Write(элемент,элемент,...);
 
Когда распечатываются элементы по оператору  Writeln,  между
ними  автоматически  не  вставляются пробелы; если Вы хотите
разделить элементы пробелами, то Вы должны указать их  сами,
например:
 
     Writeln(элемент,' ',элемент,' ',...);
 
Поэтому по следующим операторам будет получен указанный  вы-
вод:
 
  A := 1; B := 2; C := 3;
  Name := 'Frank';
  Writeln(A,B,C);                123
  Writeln(A,' ',B,' ',C);        1 2 3 
  Writeln('Hi',Name);            HiFrank
  Writeln('Hi, ',Name,'.');      Hi, Frank.
 
     Кроме того, можно использовать спецификаторы для  опре-
деления ширины поля для данного элемента. В этом случае опе-
ратор имеет формат:
 
     Writeln(элемент:ширина,...)
 
где ширина - целое выражение (литерал,  константа,  перемен-
ная,  обращение к функции или комбинация из них), определяю-
щее общую длину поля, в котором должен быть записан элемент.
Например,  рассмотрим следующую программу и полученный в ре-
зультате вывод:
 
  A := 10; B := 2; C := 100;
  Writeln(A,B,C);                  102100
  Writeln(a:2,B:2,C:2);            10 2100
  Writeln(A:3,B:3,C:3);             10  2100
  Writeln(A,B:2,C:4);              10 2 100
 
Отметим, что элемент дополняется начальными пробелами  слева
с тем, чтобы соответствовать указанной длине поля. Само зна-
чение выравнивается справа.
     Что если ширина поля меньше, чем необходимо? Во  втором
операторе  Writeln из приведенного выше примера для С, имею-
щего значение 100, ширина поля указана  2,  хотя  необходима
ширина 3. Как Вы видете в выводе, Паскаль увеличил ширину до
минимального необходимого размера.
     Этот способ выполняется для всех допустимых  элементов:
целых  чисел,  вещественных чисел, символов, строк и булевых
выражений. Однако, при указании  спецификатора  ширины  поля
вещественные числа распечатываются в экспоненциальной форме:
 
  X := 421.53;
  Writeln(X);              4.2153000000E+02
  Writeln(X:8);            4.2E+02
 
     Поэтому Паскаль позволяет добавить второй  спецификатор
ширины  поля: элемент:ширина:цифры. Это второе значение ука-
зывает распечатать вещественное число в формате  с  фиксиро-
ванной точкой и определяет, сколько цифр поместить после де-
сятичной точки:
 
     X := 421.53;
     Writeln(X:6:2); 421.53
     Writeln(X:8:2); 421.53
     Writeln(X:8:4); 421.5300
 
                            Ввод
 
     Стандартный Паскаль имеет две  основные  функции  ввода
Read  и Readln, которые используются для чтения данных, вво-
димых с клавиатуры. Общий формат этих операторов следующий:
 
     Read(элемент,элемент,...); или
     Readln(элемент,элемент,...);
 
где каждый элемент представляет собой переменную целого, ве-
щественного,  символьного  или строкового типа. Числа должны
отделяться от других значений пробелами или нажатием клавиши
"Enter".
 
                     Операторы условия 
 
     Иногда бывает необходимо, чтобы некоторая  часть  прог-
раммы  была выполнена, если некоторое заданное условие имеет
значение Тrue или же не имеет, или если  некоторое  заданное
выражение  принимает  определенное  значение. Посмотрим, как
это реализуется в Паскале.
 
                        Оператор If
 
     Посмотрим, как использовался оператор if  в  предыдущих
примерах.  Отметим,  что он может иметь следующий обобщенный
формат:
 
    if выражение
      then оператор1
      else оператор2
 
где "выражение" обозначает любое  булевское  выражение  (при
вычислении  дающее значение Тrue или False), а "оператор1" и
"оператор2" - допустимые в Паскале операторы. Если выражение
имеет  значение  Тrue, то выполняется оператор1. В противном
случае выполняется оператор2.
     Необходимо разъяснить два важных момента в  использова-
нии  операторов if/then/else. Во-первых, оператор else явля-
ется необязательным; другими  словами,  является  допустимым
следующий оператор if:
 
     if выражение
       then оператор1
 
В этом случае оператор1 выполняется в том  и  только  в  том
случае,  если  выражение имеет значение Тrue. Если выражение
имеет значение False, то оператор1 пропускается и  продолжа-
ется выполнение программы.
     Во-вторых, как быть, если  необходимо  выполнить  более
одного  оператора в случае, когда указанное выражение прини-
мает значение Тrue или False. В этом случае следует  исполь-
зовать  составной  оператор.  Составной  оператор состоит из
ключевого слова begin,  нескольких  операторов,  разделенных
точкой с запятой, и ключевого слова end.
     В примере с вычислением отношения в предложении if  ис-
пользуется одиночный оператор:
 
  if B = 0.0
    then Writeln('Деление на нуль запрещено.')
 
а в предложении else - составной оператор:
 
  else begin
    Ratio = A / B;
    Writeln('Отношение равно ',Ratio)
  end;
 
     Как вы могли уже заметить, тело каждой написанной  вами
программы представляет собой составной оператор, в конце ко-
торого стоит точка.
 
                       Оператор Саsе
 
     Этот оператор позволяет осуществить в  программе  выбор
между альтернативами, не используя большого числа операторов
if.
     Оператор Саsе состоит из выражения (селектора) и списка
операторов, каждому из которых предшествует метка case, име-
ющая тот же тип, что и селектор. Это означает, что тот  опе-
ратор  должен  быть  выполнен, чья метка case равна текущему
значению селектора. Если ни одна из меток case  не  содержит
значения  селектора, то или не выполняется ни один оператор,
или выполняются операторы, стоящие после необязательного за-
резервированного  слова else. (Ключевое слово else представ-
ляет собой расширение стандартного Паскаля).
     Метка case состоит из нескольких констант или поддиапа-
зонов, за которыми стоит двоеточие и которые разделены между
собой запятыми, например:
 
  case BirdSight of
                  'C', 'c' : Curlews := Curlews + 1;
                  'H', 'h' : Herons  := Herons + 1;
                  'E', 'e' : Egrets  := Egrets +1;
                  'Y', 't' : Terns   := Terns +1;
                end;  { case }
 
     Поддиапазон записывается в виде двух  констант,  разде-
ленных ограничителем поддиапазона '..'. Тип константы должен
соответствовать типу селектора. Оператор, стоящий после мет-
ки  case,  выполняется в том случае, если значение селектора
равно значению констант или если оно попадает в один из под-
диапазонов.
 
                           Циклы
 
     Так же, как могут существовать  операторы  (или  группы
операторов), которые необходимо выполнить при выполнении оп-
ределенного условия, так могут быть операторы, которые нужно
выполнить  несколько  раз. Такая конструкция называется цик-
лом.
     Имеется три основных  вида  циклов:  цикл  while,  цикл
repeat и цикл for. Рассмотрим их в этом же порядке.
 
         Оператор цикла с предусловием (цикл While)
 
     Оператор while используется для проверки некоторого ус-
ловия в начале цикла. Введем следующую программу:
 
program Hello;
var
  Count : integer;
begin
  Count := 1;
  while Count <= 10 do begin
    Writeln('Здравствуйте и до свидания!');
    Inc(Count)
  end;
  Writeln('Это конец!')
end.
 
     Первое, что произойдет при запуске  этой  программы,  -
это присваивание Count значения 1, затем следует цикл while.
Здесь сначала проверяется, не  является  ли  значение  Count
меньшим  или  равным  10. Если да, то выполняется тело цикла
(begin..end).  При  этом  на  экране  печатается   сообщение
"Здравствуйте и до свидания!", затем значение Count увеличи-
вается на 1. Count проверяется заново и тело цикла  выполня-
ется  еще  раз.  Это  продолжается до тех пор, пока значение
Count при проверке меньше или равно  10.  Как  только  Count
принимает значение 11, цикл завершается, и на экране печата-
ется строка "This is the end!" ("Это конец!").
     Формат оператора while имеет вид:
 
     while выражение do оператор
 
где "выражение" - это булевское выражение, а оператор - оди-
ночный или составной оператор.
     В цикле while вычисляется  выражение.  Если  оно  имеет
значение Тrue, то оператор выполняется, и выражение вычисля-
ется заново. Если выражение имеет значение  False,  то  цикл
while завершается и выполнение программы продолжается.
 
     Оператор цикла с постусловием (цикл Repeat..until)
 
     Второй вид представлен  циклом  repeat..until,  который
использован в программе DORATIO.PAS:
 
program DoRatio;
var
  A,B    : integer;
  Ratio  : real;
  Ans    : char;
begin
  repeat
    Write('Введите два числа: ');
    Readln(A,B);
    Ratio := A / B;
    Writeln('Отношение равно ',Ratio)
    Write('Повторить+ (Y/N)  ');
    Readln(Ans)
  until (Ans = 'n') or (Ans = 'N')
end.
 
     Как указывалось выше, эта программа повторно выполняет-
ся до тех пор, пока не будет получен ответ n или N на вопрос
"Повторить+". Другими словами, заключенное  между  repeat  и
until  повторно выполняется до тех пор, пока выражение после
until имеет значение Тrue.
     Обобщенный формат для цикла repeat..until имеет вид:
 
     repeat
       оператор;
       оператор;
       ...
       оператор
     until выражение
 
     Имеется три основных различия между циклом while и цик-
лом  repeat.  Во-первых, операторы в цикле repeat всегда вы-
полняются хотя бы один  раз,  поскольку  проверка  выражения
осуществляется не сразу после ключевого слова repeat. Наобо-
рот, в цикл while, если выражение изначально имеет  значение
False, то пропускается все тело цикла.
     Во-вторых, цикл repeat выполняется до тех пор, пока вы-
ражение  не  примет  значение  Тrue;  в отличие от него цикл
while выполняется, пока выражение имеет значение  Тrue.  Это
означает, что следует внимательно заменять один тип цикла на
другой. Например, рассмотрим программу НЕLLО, переписанную с
использованием цикла repeat:
 
program Hello;
var
  Count : integer;
begin
  Count := 1;
  repeat
    Writeln('Здравствуйте и до свидания!');
    Inc(Count)
  until Count > 10;
  Writeln('Это конец!')
end.
 

     Отметим, что теперь проверяется, не является ли  значе-
ние  Count большим 10, в то время как в цикле while проверя-
лось, не является ли значение Count меньшим или равным 10.
     Наконец, цикл repeat может содержать несколько операто-
ров,  не образующих составной оператор. Заметьте, что в пос-
ледней программе не используется begin..end, в то время  как
в варианте с циклом while это имело место.
     Кроме того, следует запомнить, что цикл  repeat  всегда
выполняется  по крайней мере один раз. Цикл while может и не
быть выполненным в зависимости от выражения.
 
           Оператор цикла с параметром (цикл For)
 
     Цикл for можно найти во многих языках программирования,
включая Паскаль. Однако, вариант этого цикла в Паскале явля-
ется одновременно ограниченным и эффективным. 
     В обычном случае набор операторов выполняется  фиксиро-
ванное  число раз, пока некоторая переменная (так называемая
индекная переменная) принимает значения из указанного диапа-
зона.  Например,  модифицируем  приводимую  ранее  программу
НЕLLО следующим образом:
 
program Hello;
var
  Count : integer;
begin
  for Count := 1 to 10 do
    Writeln('Здравствуйте и до свидания!');
  Writeln('Это конец!')
end.
 
     При выполнении этой программы можно убедиться, что рас-
сматриваемый  цикл  выполняется  так  же,  как циклы while и
repeat и фактически он точно эквивалентен циклу while. Обоб-
щенный формат для цикла for имеет следующиий вид:
 
     for индекс := выражение1 to выражение2 do оператор
 
где индекс - это переменная скалярного  типа  (целого  типа,
символьного,  булева или перечислимого типа), "выражение1" и
"выражение2" представляют собой выражения, имеющие тип, сов-
местимый  с  индексом, а "оператор" - это одиночный или сос-
тавной оператор. Индекс увеличивается  на  1  после  каждого
прохождения цикла.
     Вы можете также уменьшать, а не  увеличивать  индексную
переменную,  для  этого  нужно заменить ключевое слово to на
downto.

     Цикл for эквивалентен следующей программе:
 
  index := expr1;
  while index <= expr2 do
  begin
    оператор
    Inc(индекс)
  end;
 
     Основным недостатком цикла for является то, что он поз-
воляет увеличивать или уменьшать только на 1. К его основным
преимуществам относятся краткость и возможность  использова-
ния символьного и перечислимого типов в диапазоне значений.
 
                    Процедуры и функции
 
     Вы изучили условное и итеративное выполнение программы.
Теперь  посмотрим,  как выполнить один и тот же набор команд
на различных множествах данных или в различных местах  прог-
раммы. Для этого нужно поместить эти операторы в подпрограм-
му, которую потом можно вызывать при необходимости.
     В Паскале имеется два  типа  подпрограмм:  процедуры  и
функции.  Основное  различие  между  ними состоит в том, что
функция возвращает значение и может использоваться в выраже-
ниях, например:
 
     X := Sin(A);
 
в то время как процедура вызывается для выполнения одной или
более задач:
 
     Writeln('Это проверка');
 
     Однако, прежде, чем знакомиться с процедурами и функци-
ями, необходимо рассмотреть структуру программы на Паскале.
 
                    Структура программы
 
     В стандартном Паскале программы имеют строгий формат:
 
      program Имя-программы
      label
        метки;
      соnst
        описание констант;
      type
        определения типов данных;
      var
        описания переменных;
        procedures и functions;
 
      begin
        основное тело программы
      end.
 
     Из пяти секций описания - label,  const,  type,  var  и
procedures и functions - не все должны присутствовать в каж-
дой программе. Однако, в стандартном Паскале, если они  при-
сутствуют,  то  они  должны  следовать в указанном порядке и
каждая секция должна появляться только один раз. За  секцией
описания могут следовать процедуры и функции, и только затем
- основное тело программы,  состоящее  из  некоторого  числа
операторов.
     Турбо-Паскаль  обеспечивает  значительно  более  гибкую
структуру программы. Все, что требуется, - это, чтобы опера-
тор program (если таковой имеется) был  первым,  а  основное
тело  программы  -  последним.  Между ними можно иметь сколь
угодно много секций описания, в любом порядке и  как  угодно
смешанными с процедурами и функциями. Но прежде, чем что-ли-
бо использовать, это должно быть определено, иначе на  этапе
компиляции появится сообщение об ошибке.
 
                    Структура программы
 
     В стандартном Паскале программы имеют строгий формат:
 
  program Имя_программы;
  label;   
      метки;
  const
      описания констант;
  type
      определения типов данных;
  var
      описание переменных;
  процйедуры и функции;
  begin
      тело основной программы;
  end.
 
     Все пять разделов описаний (label, const, type, var,  а
также процедуры и функции) не обязательно должны присутство-
вать в каждой программе. Однако в стандартном Паскале в  том
случае, если эти разделы имеются, они должны следовать имен-
но в этом порядке и каждый раздел должен встречаться  только
один раз. За разделом описаний могут следовать ваши процеду-
ры и фукнции, если они имеются, и,  наконец,  тело  основной
программы, содержащее некоторое количество операторов.
     Турбо-Паскаль допускает большую  гибкость  в  структуре
программы.  Все его требования заключаются в том, чтобы опе-
ратор program (если он у вас есть) следовал первым,  а  тело
основной  программы  - последним. Межд ними можно разместить
столько разделов описаний, сколько вам требуется, и в  любом
необходимом  вам  порядке.  При этом описания могут свободно
чередоваться с процедурами и функциями. Однако  все  объекты
должны  определяться  перед  их  использованием (в противном
случае во время компиляции возникает ошибка).
 
               Структура процедуры и функции
 
     Как упоминалось ранее, процедуры и  функции,  известные
как подпрограммы, могут появиться в любом месте до основного
тела программы. Для процедур используется следующий формат:
 
      procedure имя-процедуры(параметры);
      label
        метки;
      const
        описания констант;
      type
        определения типов данных;
      var
        описания переменных;
        procedures и functions;
 
      begin
        основное тело процедуры;
      end;
 
     Функции имеют такой же формат, что и процедуры за  иск-
лючением того, что они начинаются с заголовка function и за-
канчиваются типом данных для возвращаемого значения функции:
 
     function имя_функции(параметры) : тип данных;
 
     Как можно видеть, здесь имеется только два различия  от
структуры  обычной программы: процедуры и функции начинаются
с заголовка procedure или function, а не с зголовка program,
и  заканчиваются  не точкой, а точкой с запятой. Процедуры и
фукнции могут иметь свои собственные константы, типы данных,
переменные и даже собтвенные процедуры и функции. Но все эти
элементы могут использоваться  только  в  тех  процедурах  и
функциях, в которых они определены.
 
                      Пример программы
 
     Ниже приводится вариант программы DORATIO.PAS, в  кото-
ром  используется  процедура  для  получения двух значений и
функция для вычисления коэффициента:
 
program DoRatio;
var
  A,B    : integer;
  Ratio  : real;
 
procedure GetData(var X,Y : integer);
begin
  Write('Введите два числа: ');
  Readln(X,Y)
end;
 
function GetRatio(I,J : integer) : real;
begin
  GetRatio := I/J
end;
 
begin
  GetData(A,B);
  Ratio := GetRatio(A,B);
  Writeln('Отношение равно ',Ratio)
end.
 
     Эта программа, конечно, не является улучшением первона-
чальной программы, поскольку она имеет больший размер и мед-
леннее выполняется, однако, она  иллюстрирует  использование
процедур и функций.
     При компиляции и запуске программы первым в теле  прог-
раммы  выполняется оператор GetData(А,В). Этот тип оператора
называется вызовом процедуры.  При  обработке  этого  вызова
программа выполняет операторы в GetData, заменяя Х и Y (фор-
мальные параметры) на А и В (фактические параметры).  Ключе-
вое  слово  var  перед  Х  и  Y в операторе вызова процедуры
GetData указывает, что фактические параметры должны быть пе-
ременными  и  что  значения переменных могут быть изменены и
переданы обратно  вызывающей  программе.  Поэтому  процедуре
GetData  нельзя  передавать литералы, константы, выражения и
так далее. При завершении выполнения GetData управление  пе-
редается  в основное тело программы оператору, следующему за
вызовом GetData.
     Этим  следующим  оператором  является   вызов   функции
GetRatio.  Отметим  здесь некоторые важные различия. Во-пер-
вых, GetRatio возвращает значение, которое затем должно быть
где-нибудь  использовано; в данном случае это значение прис-
ваивается Ratio. Во-вторых, в основном теле функции значение
присваивается  GetRatio.  Таким  образом функция определяет,
какое значение следует возвратить. В-третьих, перед формаль-
ными параметрами I и J здесь отсутствует ключевое слово var.
Это означает, что фактические параметры  могут  быть  любыми
целочисленными     выражениями,     например,    Ratio    :=
GetRatio(А+В,300), и что даже если значения формальных пара-
метров  в теле функции будут изменены, новые значения не бу-
дут переданы обратно вызывающей программе. Это,  кстати,  не
является  различием между процедурами и функциями; Вы можете
использовать оба типа параметров с любым видом подпрограм.
 
                  Комментарии в программе
 
     Иногда бывает необходимо вставить в  программу  замеча-
ния,  напоминающие  (или  информирующие) о том, что означают
некоторые переменные,  какие  действия  выполняют  некоторые
функции или операторы, и так далее. Эти замечания называются
комментариями. Паскаль,  как  и  большинство  других  языков
программирования, позволяет вставлять в программу как угодно
много комментариев.
     Комментарий начинается левой фигурной скобкой ({),  ко-
торая  указывает компилятору игнорировать все последующее до
тех пор, пока не будет  обнаружена  правая  фигурная  скобка
(}).
     Комментарии можно даже располагать на нескольких  стро-
ках, например:
 
    {Это пример длинного
     комментария, занимающего
     несколько строк.}
 
     Кроме того, Паскаль допускает альтернативную форму ком-
ментария,  начинающегося левой круглой скобкой со звездочкой
(* и заканчивающегося правой круглой скобкой  со  звездочкой
*).  Этим  принимаются в расчет ограничения на вложения ком-
ментариев, поскольку комментрарий, начинающийся с  (* игнори-
рует все фигурные скобки, и наоборот.
     Теперь, после того, как вы прошли  начальный  курс,  мы
рекомендуем  вам  обратиться  к хорошему учебному пособию по
Турбо-Паскалю  (например,  к  учебнику  по  Турбо-Паскалю  -
"Turbo  Pascal  Tutor",  а  также книгам: К. Йенсен, Н. Вирт
"ПАСКАЛЬ: Руководство для пользователя  и  описание  языка",
М.: Финансы и статистика, 1982; П. Грогоно "Программирование
на языке Паскаль", М.:  Мир,  1982;  Н.  Вирт  "Алгоритмы  +
структуры  данных  =  программы",  М.;  Мир, 1985; Р. Грэхем
"Практический курс языка Паскаль для микроЭВМ", М., Радио  и
связь,  1986;  В.Г.  Абрамов, Н.П. Трифонова, Г.Н. Трифонова
"Введение в Паскаль", М., Наука, 1988 - прим. перев.).

 
                          ГЛАВА 4
 
 
             Модули и связанные с ними тонкости
 
     В Главе 3 было показано, как писать  стандартные  прог-
раммы  на Паскале. Но как быть в случае нестандартного прог-
раммирования - частного случая  программирования  на  персо-
нальном компьютере IBM РС, с экранным управлением, с обраще-
ниями к операционной системе ДОС и графикой+ Для того, чтобы
самостоятельно  писать  такие  программы,  необходимо  иметь
представление о модулях или об аппаратных  средствах  персо-
нального компьютера. В настоящей главе разъясняется, что та-
кое модуль, как он  используется,  какие  встроенные  модули
доступны  пользователю,  как  писать собственные программные
модули и как компилировать их. 
 
                    Что же такое модуль?
 
     Турбо-Паскаль обеспечивает вам доступ к большому  числу
встроенных  констант,  типов  данных, переменных, процедур и
функций. Некоторые из них специфичны для Турбо-Паскаля; дру-
гие  специфичны  для персонального компьютера IBM РС (и сов-
местимых с ним компьютеров)  или  для  операционной  системы
ДОС. Их количество велико, однако, в своей программе вы ред-
ко используете их все сразу. Поэтому они разделены  на  свя-
занные  группы, называемые модулями. В этом случае можно ис-
пользовать только те модули, которые необходимы в программе.
     Программный модуль представляет собой  набор  констант,
типов  данных, переменных, процедур и функций. Каждый модуль
аналогичен отдельной прогамме на Паскале: он может иметь ос-
новное  тело,  которое вызывается перед запуском вашей прог-
раммы и осуществляет необходимую инициализацию. Короче гово-
ря,  модуль  представляет собой библиотеку описаний, которую
можно вставить в свою программу и которая  позволит  разбить
программу на части, компилируемые отдельно.
     Все описания внутри модуля связаны друг с другом.  Нап-
ример,  модуль  Crt  содержит  все описания, необходимые для
подпрограмм работы с экраном на вашем персональном компьюте-
ре.
     Турбо-Паскаль предоставляет пользователю семь  стандар-
тных  модулей.  Пять  из  них  -  System,  Graph, DOS, Crt и
Printer - обеспечивают поддержку обычных  программ  на  Тур-
бо-Паскале. Два других - Тurbo3 и Graph3 - предназначены для
обеспечения совместимости с программами  и  файлами  данных,
созданными  под  версией 3.0 Турбо-Паскаля. Все семь модулей
хранятся в файле TURBO.TPL. Некоторые  из  них  более  полно
разъясняются  в главе 5, здесь же рассматривается общее наз-
начение каждого модуля. 
 
                      Структура модуля
 
     Модуль обеспечивает набор средств благодаря  процедурам
и  функциям  при поддержке констант, типов данных и перемен-
ных, однако действительная реализация этих средств скрыта  в
силу  того,  что модуль разделен на две секции: интерфейса и
реализации. Если программа использует модуль, то все  описа-
ния модуля становятся доступными этой программе, как если бы
они были определены в ней самой.
     Структура модуля аналогична структуре программы, однако
есть  несколько  существенных различий. Например, рассмотрим
модуль:
 
     unit <идентификатор>;
     interface
     uses <список модулей>;      { Необязательный }
       { открытые описания }
     implementation
       { закрытые описания }
       { процедуры и функции }
 
     begin
       { код инициализации }
     end.
 
Заголовок модуля начинается зарезервированным  словом  unit,
за  которым следует имя модуля (идентификатор) точно так же,
как и в случае имени программы. Следующим элементом в модуле
является ключевое слово interface. Оно обозначает начало ин-
терфейсной секции модуля - секции, видимой всем другим моду-
лям или программам, в которых он используется. 
     Программный модуль может  использовать  другие  модули,
для  этого  они определяются в предложении uses. Предложение
uses, если имеет место, то  следует  сразу  после  ключевого
слова interface. Отметим, что здесь выполняется общее прави-
ло использования предложения uses: если модуль, имя которого
указано  в  предложении  uses,  использует другие модули, то
имена этих модулей также должны быть указаны  в  предложении
uses, причем до того, как они будут использованы.
 
                    Интерфейсная секция 
 
     Интерфейсный раздел - "общедоступная" часть в модуле  -
начинается зарезервированным словом interface, следует сразу
после заголовка модуля и заканчивается перед зарезервирован-
ным словом implementation. Интерфейс определяет, что являет-
ся "видимым" для любой программы (или модуля),  использующей
данный  модуль.  Любая  программа, использующая этот модуль,
имеет доступ к этим "видимым" элементам.
     В интерфейсе модуля можно  определять  константы,  типы
данных,  переменные, процедуры и функции. Как и в программе,
они могут быть расположены в любом порядке, и  секции  могут
встречаться повторно (например, type ... var ... <процедуры>
... const ... type
     Процедуры и функции, видимые для любой  программы,  ис-
пользующей  данный  модуль, описываются в секции интерфейса,
однако их действительные тела -  реализации  -  находятся  в
секции  реализации.  Если  процедура  (или функция) является
внешней, то в интерфейсе должно быть указано ключевое  слово
external,  а в секции реализации не нужно повторно указывать
описание процедуры. Если процедура  (или  функция)  является
директивой  inline,  то в секции интерфейса будет находиться
машинный код (список целочисленных констант), а в секции ре-
ализации не нужно указывать никакого другого описания проце-
дуры. Описания forward (предописаниия) не являются необходи-
мыми  и не разрешаются. Тела всех обычных процедур и функций
находятся в секции реализации, если заголовки этих  процедур
и функций перечислены в интерфейсной секции.
 
                     Секция реализации
 
     Секция реализации - "приватная" часть - начинается  за-
резервированным  словом  implementation.  Все, что описано в
секции интер- фейса, является видимым в  секции  реализации:
константы,  типы, переменные, процедуры и функции. Кроме то-
го, в секции реализации могут быть свои дополнительные  опи-
сания, которые не являются видимыми для программ, использую-
щих этот модуль. Программа не знает о их существовании и  не
может  ссылаться  на  них  или обращаться к ним. Однако, эти
спрятанные элементы могут использоваться  (и,  как  правило,
используются)  "видимыми"  процедурами  и функциями, то есть
теми подпрограммами, чьи заголовки указаны в  секции  интер-
фейса.
     Если какие-то процедуры были описаны как внешние, то  в
исходном файле должны быть указаны одна или несколько дирек-
тив {$L имя_файла}. Если в исходном файле отсутствует секция
инициализации, то директива {$L имя_файла} может быть указа-
на в любом месте до завершающего end  модуля.  Директива  $L
позволяет  компоновать  объектные  модули  языка ассемблера,
разрешающие внешние процедуры.
     Обычные процедуры и функции, описанные  в  интерфейсной
секции  (то  есть  те  из них, которые не имеют тип inline),
должны быть повторно указаны в секции реализации.  Заголовок
procedure/function  должен быть или идентичным тому, который
указан в секции интерфейса, или иметь более краткую форму. В
случае  краткой  формы напечатайте ключевое слово (procedure
или function), а за ним укажите имя подпрограммы  (идентифи-
катор). Затем подпрограмма должна содержать все свои локаль-
ные описания (метки, константы, типы, переменные и вложенные
процедуры и функции), за которыми должно находиться основное
тело самой подпрограммы. Пусть в интерфейсной секции указаны
следующие описания:
 
     procedure ISwap(var V1,V2 : integer);
     function IMax(V1,V2 : integer) : integer;
 
Секция реализации тогда будет иметь вид:
 
procedure ISwap; var
     Temp := integer; begin
     Temp := V1; V1 := V2; V2 := Temp end; { конец процедуры
Swap } function IMax(V1,V2 : integer) : integer; begin
     if V1 > V2
     then IMax := V1
     else IMax := V2 end; { конец функции Max }
     Подпрограммы, локальные для секции реализации (то  есть
не  описанные в секции реализации), должны иметь полный, не-
сокращенный заголовок procedure/function.
 
                    Секция инициализации
 
     Обычно вся секция реализации модуля заключена между за-
резервированными  словами implementation и end. Однако, если
перед end поместить зарезервированное слово begin,  а  между
ними  - операторы, то получившийся составной оператор, очень
похожий на основное тело программы, становится секцией  ини-
циализации модуля.
     Секция инициализации представляет собой место, где ини-
циализируются структуры данных (переменных), которые исполь-
зует программный модуль или  которые  он  делает  доступными
программе,  использующей  данный модуль. Вы можете использо-
вать эту секцию для открытия файлов, которые  программа  ис-
пользует позже. Например, стандартный модуль Printer исполь-
зует секцию инициализации для выполнения запросов на  откры-
тие  (для  вывода) текстового файла Lst, который затем можно
использовать в операторах Write и Writeln в вашей программе.
     При выполнении программы,  использующей  некоторый  мо-
дуль, секция инициализации этого модуля вызывается перед за-
пуском основного тела программы. Если  программа  использует
более  одного  модуля,  то секции инициализации всех модулей
вызываются (в порядке, указанном в операторе uses в програм-
ме) перед тем, как выполнить основное тело программы.
 
                  Как используются модули?
 
     Модули, которые использует ваша программа, уже  оттран-
слированы  и  хранятся,  как машинный код, а не как исходный
код на Паскале, поскольку они не являются включаемыми файла-
ми (файлами типа Include). Даже интерфейсная секция хранится
в специальном двоичном формате символьной таблицы, использу-
емом  в  Турбо-Паскале. Более того, определенные стандартные
модули хранятся в специальном файле (TURBO.TPL) и  автомати-
чески загружаются в память вместе с Турбо-Паскалем.
     В результате использование одного или нескольких  моду-
лей  очень  незначительно увеличивает время компиляции вашей
программы (обычно менее, чем на секунду).  Если  программные
модули  загружаются  из  отдельного файла на диске, то может
потребоваться несколько дополнительных секунд для  чтения  с
диска.
     Как указывалось ранее, для  использования  специального
модуля  или набора модулей необходимо в начале программы по-
местить предложение uses, после которого указать список имен
тех модулей, которые будут использоваться; имена должны быть
разделены запятыми:
 
     program MyProg;
     uses thisUnit,thatUnit,theOtherUnit;
 
Когда компилятор встречает это предложение uses, он  прибав-
ляет информацию из секции интерфейса каждого модуля к табли-
це символов и присоединяет  машинный  код,  представленый  в
секции реализации, к самой программе.
     Модули присоединяются к таблице  символов  в  указанном
порядке. Этот порядок может быть существенным, если один мо-
дуль использует другой. Например, если  thisUnit  использует
thatUnit, то предложение uses должно иметь вид:
 
     uses thatUnit,thisUnit,theOtherUnit;
 
или
 
     uses thatUnit,theOtherUnit,thisUnit;
 
Короче говоря, в списке модуль должен быть указан после всех
тех модулей, которые он использует.
     Если в программе  не  указано  предложение  uses,  Тур-
бо-Паскаль  в  любом  случае  присоединит стандартный модуль
System. Этот модуль обеспечит выполнение некоторых  стандар-
тных  паскалевских  подпрограмм, а также нескольких подпрог-
раммах, специфических для Турбо-Паскаля.
 
                 Ссылки на описания модуля
 
     Как только вы включили модуль  в  свою  программу,  все
константы,  типы  данных,  переменные,  процедуры и функции,
описанные в секции интерфейса этого модуля, становятся  дос-
тупными для вашей программы. Например, допустим, что имеется
следующий модуль:
 
unit MyStuff;
interface
  const
    MyValue = 915;
  type
    MyStars = (Deneb,Antares,Betelgeuse);
  var
    MyWord : string[20];
 
  procedure SetMyWord(Star : MyStars);
  function TheAnswer : integer;
 
Та часть модуля, которая находится  в  интерфейсной  секции,
является  видимой  для  вашей программы (и может быть ею ис-
пользована). Благодаря этому можно написать следующую  прог-
рамму:
 
program TestStuff;
uses MyStuff;
var
  I     : integer;
  AStar : MyStars;
begin
  Writeln(myValue);
  AStar := Deneb;
  SetMyWord(AStar);
  Writeln(MyWord);
  I := TheAnswer;
  Writeln(I)
end.
 
После того, как включения в программу  предложения  uses  вы
можете  ссылаться  на все идентификаторы, описанные в интер-
фейсной секции модуля МyStuff (МyWord, МyValue и так далее).
Однако, рассмотрим следующую ситуацию:
 
program TestStuff;
uses MyStuff;
const
  MyValue = 22;
var
  I     : integer;
  AStar : MyStars;
 
  function TheAnswer : integer;
  begin
    TheAnswer := 1
  end;
 
begin
  Writeln(myValue);
  AStar := Deneb;
  SetMyWord(AStar);
  Writeln(MyWord);
  I := TheAnswer;
  Writeln(I)
end.
 
     В этой программе переопределяются некоторые из  иденти-
фикаторов,  описанных  в МyStuff. Будучи оттранслированной и
выполненной, эта программа  будет  использовать  собственные
определения для МyValue и ТheAnswer, поскольку они были опи-
саны позже, чем определения в МyStuff
     Вероятно, вам интересно знать, каким  образом  в  такой
ситуации  можно  ссылаться  на идентификаторы в МyStuff. Для
этого необходимо перед каждым идентификатором  помещать  имя
МyStuff  с  точкой (.). Например, рассмотрим еще одну версию
этой программы:
 
program TestStuff;
uses MyStuff;
const
  MyValue = 22;
var
  I     : integer;
  AStar : MyStars;
 
  function TheAnswer : integer;
  begin
    TheAnswer := 1
  end;
 
begin
  Writeln(MyStuff.MyValue);
  AStar := Deneb;
  SetMyWord(AStar);
  Writeln(MyWord);
  I := MyStuff.TheAnswer
  Writeln(I)
end.
 
     Эта программа даст такие же ответы, что и первая,  даже
в  том случае, если вы переопределите MyValue и ТheAnswer. В
действительности вы имели полное право (хотя и довольно сом-
нительное) написать первую программу следующим образом:
 
program TestStuff;
uses MyStuff;
var
  I     : integer;
  AStar : MyStuff.MyStars;
 
begin
  Writeln(MyStuff.MyValue);
  AStar := My.Stuff.Deneb;
  MyStuff.SetMyWord(AStar);
  Writeln(My.Stuff.MyWord);
  I := MyStuff.TheAnswer;
  Writeln(I)
end.
 
Отметим, что имя модуля может предшествовать любому  иденти-
фикатору: константе, типу данных, переменной или подпрограм-
ме.
 
             Предложение uses секции реализации
 
     В версии 5.0 Турбо-Паскаль позволяет  вам  размещать  в
секции реализации предложение использования (uses). В случае
его присутствия предложение  uses  должно  следовать  непос-
редственно  за  ключевым  словом  implementation (аналогично
тому, как в интерфейсной секции  предложение  clause  должно
следовать непосредственно за ключевым словом interface).
     Размещение секции реализации предложения uses позволяет
"скрыть"  внутренние  детали модуля, поскольку использемые в
секции реализации модули оказываются "невидимыми" для  того,
кто  этот модуль использует.  Более важным, однако, является
то, что это позволяет вам строить взаимнозависимые модули.
     Поскольку программные модули в Турбо-Паскале не обязаны
иметь строго иерархическую структуру, то допускается исполь-
зовать циклические ссылки на модули. В следующем разделе по-
казан  пример,  демонстрирющий полезное использование цикли-
ческих ссылок.
 
               Циклические ссылки на модули
 
     В следующей программе показаны два модуля, которые "ис-
пользуют" друг друга. Основная программа Circular использует
модуль с именем Display. Модуль Display содержит в своей ин-
терфейсной  секции одно программу WriteXY, которая имеет три
параметра: пару координат (x,y) и сообщение  для  вывода  на
экран. WriteXY перемещает курсор в точку (x,y) и выводит там
сообщение. В противном случае она вызыает простую  программу
обработки ошибки.
     Пока мы не видим здесь  ничего  интересного:  процедура
WriteXY  просто используется  вместо процедуры Write. Однако
далее, когда программа обработки ошибки будет выводить сооб-
щение  на  экран,  начинаются  циклические  ссылки (ведь при
этом она снова использует WriteXY). Таким образом, мы  имеем
процедуру  WriteXY, вызывающую  процедуру  обработки  ошибки
SwapError, которая в свою очередь вызывает WriteXY для выво-
да  сообщения на экран. Если у вас уже от всего этого закру-
жилась голова, не беда. Давайте рассмотрим  исходный  код  в
Епримере и увидим, что все это не столь уж запутано.
     Основная программа Circular очищает экран  и  выполняет
три обращения к процедуре WriteXY:
 
  program Circular;
  { выводит текст, используя WriteXY }
 
  uses
     Crt, Display;
 
  begin
    ClrScr;
    WriteXY(1, 1, 'Левый верхний угол экрана');
    WriteXY(100, 100, 'За пределами экрана');
    WriteXY(81 - Lenght('Снова в экран..'), 15, 'Снова в
 экран..');
  end.

     Взгляните н координты (x,y) ири втором обращении к про-
цедуре   WriteXY.   В  точке  с  координатами  (100,100)  на
80х25-символьном экране вывести  текст  невозможно.  Давайте
теперь посмотрим, как работает процедура WriteXY. Далее при-
веден текст исходного кода модуля Display, в котором  содер-
жится  процедура WriteXY. Если координаты (x,y) являются до-
пустимыми, она выводит на экран сообщение. В противном  слу-
чае она выводит сообщение об ошибке.
 
 unit Display;
 { содержит простую программу вывода информации на ээкран }
 
 interface
 
 procedure WriteXY(X,Y : integer, Message : string);
 
 inplementation
 uses
    Crt, Error;
 procedure WriteXY(X,Y : integer, Message : string);
 begin
   if (X in [1..80] and Y in [1..25] then
   begin
     Goto(X,Y);
     Write(Message);
   end;
   else
     ShowError('Неверные координаты в процедуре WriteXY');
  end;
 
  end.
 
     Процедура ShowError, вызываемая  в  процедуре  WriteXY,
показана в приведенном далее исходном коде модуля Error. Она
всегда выводит сообщение об ошибке на 25-й строке экрана.
 
  unit Error;
  { содержит простую программу сообщения об ошибке }
  
  interface
 
  procedure ShowError(ErrMsg : string);
 
  inplementation
 
  uses
     Display;
 
  procedure ShowError(ErrMsg :string);
  begin
    WriteXY(1,25, 'Ошибка: '+ ErrMsg);
  end;
 
  end.
 
     Обратите внимание, что предложения uses в секции реали-
зации обоих модулей (Display и Error) ссылаются друг на дру-
га. Эти два модуля могут ссылаться друг на  друга  в  секции
реализации благодаря тому, что Турбо-Паскаль может для обеих
модулей выполнять  полную  компиляцию  интерфейсных  секций.
Другими словами, компилятор Турбо-Паскаля оспринимает ссылку
на частично скомпилированный модуль А  в  секции  реализации
модуля  В,  если  интерфейсные секции модуля А и модуля В не
зависят друг от друга (и, следовательно, строго собблюдаются
правила Турбо-Паскаля, касающиеся порядка описания).
 
             Совместное использование описаний
 
     Можно модифицировать процедуру WriteXY  таким  образом,
чтобы  она  воспринимала  дополнительный  параметр, задающий
прямоугольное окно на экране:
 
  procedure WriteXY(SomeWindow : WindRec;
                    X, Y :       integer;
                    Message :    string);
 
  procedure ShowError(Somewindow : WindRec;
                      ErrMsg : string);
 
     Нужно учитывать, что две процедуры находятся  в  разных
модулях.  Даже  если  вы  описываете WindData в интерфейсной
секции одного модуля, то нет такого допустимого  способа,  с
помощью  которого это описание могло бы бать доступно в дру-
гом модуле. Решение состоит в том, чтобы описать третий  мо-
дуль,   в   котором  содержится  только  определение  записи
WindRec:
 
 unit WindData;
 integface
 type
   WindRec = record
               X1, Y1, X2, Y2 : integer;
               ForeColor,
               BackColor      : byte;
               Active         : boolean;
             end;
  inplementation
  end.
 
     В добавление к  тому,  что  модификация  кода  процедур
WriteXY и ShowError позволяет использовать новый параметр, в
интерфейсной секции модулей Display и  Error   теперь  модет
использоваться  WindData.  Это  допустимо,  така  как модуль
WindData не зависит от своего  предложения  uses,  а  модули
Display  и Error ссылаются друг на друга только в соответст-
вующих секциях реализации.
 
                         ТURВО.TPL
 
     Файл ТURВО.TPL  содержит  стандартные  модули:  System,
Crt, DOS, Printer, Graph, Turbo3 и Graph3. Эти модули загру-
жаются в память вместе с Турбо-Паскалем; они всегда доступны
для  пользователя.  Обычно  файл ТURВО.TPL хранится в том же
каталоге, что и ТURВО.ЕХЕ (или ТРС.ЕХЕ). Однако,  Вы  можете
хранить  его в любом месте, если, конечно, это "любое место"
определено как каталог Турбо. Это можно определить с помощью
программы  ТINSТ.ЕХЕ, которая инсталлирует каталог Турбо не-
посредственно в файл ТURВО.ЕХЕ.
 
     System                     Используемые модули: нет
 
     System содержит все стандартные и встроенные  процедуры
и  функции  Турбо-Паскаля. Любая подпрограмма Турбо-Паскаля,
не являющаяся частью стандартного Паскаля и  не  находящаяся
ни  в  каком другом модуле, содержится в System. Этот модуль
присоединяется ко всем программам.
 
     DOS                         Используемые модули: нет
 
     DOS определяет многочисленные паскалевские процедуры  и
функции,  которые  эквивалентны  наиболее часто используемым
вызовам ДОСа, как например, GetТime, SetТime, DiskSize и так
далее. Кроме того, он определяет две программы низкого уров-
ня МsDos и Intr, которые позволяют активизировать любой  вы-
зов МС-ДОСа или системное прерывание. Registers представляет
собой тип данных для параметра в МsDos и Intr.  Кроме  того,
определяются некоторые другие константы и типы данных.
 
     Crt                         Используемые модули: нет
 
     Crt обеспечивает набор специфичных для IBM РС  описаний
констант,  переменных  и программ для операций ввода/вывода.
Последние можно использовать для работы с  экраном  (задание
окон,  непосредственное  управление  курсором, цвет текста и
фона). Кроме того, Вы можете  осуществлять  "необработанный"
ввод  с  клавиатуры  и  управлять платой генерации звукового
сигнала персонального компьютера. Этот  модуль  обеспечивает
множество  подпрограмм,  которые  были стандартными в версии
3.0.
 
     Printer                     Используемые модули: Crt
 
     В модуле Printer дано  описание  переменной  текстового
файла  Lst, которая связывается с драйвером устройства, поз-
воляющим направлять стандартный для Паскаля вывод на печата-
ющее устройство с помощью Write и Writeln. Например, включив
Printer в свою программу, Вы можете сделать следующее:
 
  Write(Lst,'Сумма ',A:4,' и ',B:4,' равна ');
  C := A + B;
  Writeln(Lst,C:8);
 
 
     Graph                       Используемые модули: Crt
 
     Graph обеспечивает набор быстродействующих, эффективных
графических  подпрограмм,  которые  позволяют использовать в
полной мере  графические  возможности  Вашего  персонального
компьютера.  Этот модуль реализует независимый от устройства
графический драйвер фирмы "Борланд",  позволяющий  поддержи-
вать  графические адаптеры типа СGА, ЕGА, Hercules, АТТ 400,
МСGА, 3270 РС и VGА.
 
     Graph3                      Используемые модули: Crt
 
     Graph3 поддерживает полный набор  графических  подпрог-
рамм  для  версии  3.0  - для обычной, расширенной графики и
графики, использующей относительные команды.  Они  идентичны
по имени, параметрам и функции подпрограммам версии 3.0.
 
     Turbo3                      Используемые модули: Crt
 
     Этот модуль содержит две переменные и несколько  проце-
дур,  которые  больше  не поддерживаются Турбо-Паскалем. Они
включают встроенную файловую переменную Кbd, булеву перемен-
ную  CBreak и первоначальные целочисленные версии MemAvail и
MaxAvail (которые возвращают размер свободной памяти  в  па-
раграфах, а не в байтах, как это делают настоящие версии).
 
     Graph                       Используемые модули: нет
 
     Модуль Graph не встроен в файл TURBO.TPL. Вместо  этого
он  содержится  на том же диске, что и вспомогательные файлы
.BGI и .CHR. Поместите файл GRAPH.TPU в текущем каталоге или
используйте  для задания маршрута файла GRAPH.TPU полное имя
пути доступа.
     В модуле Graph содержится ряд быстрых и  мощных  графи-
ческих процедур, позволяющих вам полностью использовать гра-
фические возможности компьютера  IBM  PC  (и  совместимых  с
ним).  Он  реализует  независимый  от  устройств графический
драйвер фирмы Борланд,  осуществляющий  поддержку  адаптеров
CGA,  EGA,  Hercules,  AT&T400, MCGA, 3270 PC, VGA и графики
8514.
     Теперь, когда вы знаете кое-что о модулях, давайте поп-
робуем сами что-нибудь написать.
 
                Создание собственных модулей
 
     Допустим, вы написали модуль  IntLib,  записали  его  в
файл  INTLIВ.PAS  и  оттранслировали на диск; получившийся в
результате код находится в файле INTLIВ.ТРU. Для использова-
ния  этого модуля в программе необходимо включить в нее опе-
ратор uses, указывающий компилятору, какой модуль  использу-
ется. Ваша программа может выглядеть следующим образом:
 
     program MyProg;
     uses IntLib;
 
     Отметим, что Турбо-Паскаль предполагает,  что  файл,  в
котором  находится модуль, имеет такое же имя, что и сам мо-
дуль. Если имя вашего модуля МyUtilities,  то  Турбо-Паскаль
будет  искать файл с именем МYUTILIТ.PAS. Вы можете отменить
это предположение с помощью директивы компилятора  $U.  Этой
директиве  передается  имя  файла с расширением .PAS, причем
она должна быть  указана  непосредственно  перед  оператором
uses.  Например,  если  в вашей программе используются прог-
раммные модули Dos, Crt и МyUtilities, а последний  хранится
в файле UTIL.PAS, то вам следует написать:
 
     uses Dos, Crt, {$U UTIL.PAS} MyUtilities;
 
                     Компиляция модуля
 
     Модуль компилируется точно так  же,  как  компилируется
программа: он создается с помощью редактора, а затем вызыва-
ется команда  Соmpile/Соmpile  (Компилировать/Компилировать)
(или  нажима-  ются  клавиши  Аlt-С). Однако, вместо файла с
расширением .ЕХЕ Турбо-Паскаль создает  файл  с  расширением
.ТРU (Turbо Раscal Unit - модуль Турбо-Паскаля). После этого
Вы можете оставить этот файл как есть или же вставить его  в
ТURВО.TPL с помощью TPUMOVER.ЕХЕ (см. Приложение С).
     В любом случае имеет смысл переслать файлы с расширени-
ем  .ТРU (вместе с исходными файлами) в каталог модулей, ко-
торый определен с помощью команды О/D/Unit directories  (Ка-
талоги  модулей).  Таким образом, вы можете ссылаться на эти
файлы, не указывая директивы {$U} (Команда Unit  directories
(Каталоги  модулей) позволяет задавать компилятору несколько
каталогов для поиска файлов модулей).
     В одном исходном файле может находиться только один мо-
дуль,  поскольку компиляция прекращается, как только обнару-
жен завершающий оператор end.
 
                           Пример
 
     Теперь напишем небольшой модуль. Назовем его  IntLib  и
вставим  в  него  две простые подпрограммы для целых чисел -
процедуру и функцию:
 
unit IntLib;
interface
  procedure ISwap(var I,J : integer);
  function IMax(I,J : integer) : integer;
implementation
 
procedure ISwap;
var
  Temp : integer;
 
begin
  Temp := I; I := J; J := Temp
end;  { конец процедуры ISwap }
 
function IMax;
begin
  if I > J
    then IMax := I
    else IMax := J
end;  { конец функции IMax }
 
end.  { конец модуля IntLib }
 
     Введем эту подпрограмму, запишем ее в файл  INTLIВ.PAS,
а  затем оттранслируем на диск. В результате получим код мо-
дуля в файле INTLIВ.ТРU.  Перешлем  его  в  каталог  модулей
(если таковой имеется).
     Следующая программа использует модуль IntLib:
 
program IntTest;
uses IntLib;
var
  A,B  : integer;
begin
  Write('Введите два целочисленных значения: ');
  Readln(A,B);
  ISwap(A,B);
  Writeln('A = ',A,' B = ',B);
  Writeln('Максимальное значение равно ',IMax(A,B));
end.  { конец программы IntTest }
 
     Поздравляем! Вы только что создали свой первый модуль!
 
                   Модули и большие программы
 
     Вероятно, до сих пор вы представляли программные модули
только как библиотеки - наборы полезных подпрограмм, которые
могут использоваться несколькими программами. Однако, у  мо-
дуля  есть еще одна функция - разбивать большую программу на
составные части.
     Два аспекта  Турбо-Паскаля  способствуют  использованию
модулей  в  такой функции: (1) высокая скорость компиляции и
компоновки и (2) способность работать с несколькими  файлами
одновременно, например, с программой и несколькими модулями.
     В обычном случае большая программа разбивается на моду-
ли,  которые  группируют процедуры по их функциям. Например,
программа редактора может быть разделена на части, выполняю-
щие  инициализацию, распечатку, чтение и запись файлов, фор-
матирование и так далее. Кроме того, как имеет место  основ-
ная  программа, определяющая глобальные константы, типы дан-
ных, переменные, процедуры и функции,  так  же  может  иметь
место  и  "общий" модуль, который используется всеми другими
модулями.
     Структура большой программы может  выглядеть  следующим
образом: 

   program Editor; 
   uses
     Dos,Crt,Printer { Стандартные модули из TURBO.TPL }
     EditGlobals,   { Модули, написанные пользователем }
     EdiFtInit,
     EditPrint,
     EditRead,EditWrite,
     EditFormat; { описания программы, процедуры и  
                   функции  }  
   begin  
   { основная программа } 
   end. { конец программы Editor }
     Отметим, что модули, указанные в этой программе, должны
быть  или в ТURВО.TPL, или в ваших собственных файлах с рас-
ширением означает, что при повторном компилировании програм-
мы  Еditor  Турбо-Паскаль  проверит последние обновления для
каждого файла с расширением .TPL и заново оттранслирует их в
случае необходимости.
     Другая причина использования модулей в больших програм-
мах определяется ограничениями на размер сегмента. Процессо-
ры 8086 (и связанные с ними) ограничивают размер куска прог-
раммы  или сегмента до 64К. Это означает, что основная прог-
рамма и любой данный сегмент не должны превышать по  размеру
64К.  Турбо-Паскаль разрешает эту ситуацию, превращая каждый
модуль в отдельный сегмент. Верхним пределом является  коли-
чество  памяти, поддерживаемое аппаратными средствами и опе-
рационной системой, то есть 640К на большинстве персональных
компьютерах РС. Без использования модулей пределом для вашей
программы является 64К (обратитесь  к  Главе  5  "Разработка
программ"  за  более  полной информацией о работе с большими
программами).
 
         Использование модулей в качестве оверлеев
 
     Иногда даже возможность использования нескольких  моду-
лей не момогает решить проблему нехватки памяти - ведь у вас
модет оказаться меньше 640К памяти, или одновременно исполь-
зуете  в  памяти большое количество данных. Другими словами,
может оказаться, что ваша программа не может целиком  помес-
титься  в  память.  Турбо-Паскаль  предлагает  решение такой
проблемы - оверлеи. Оверлей представляет собой  часть  прог-
раммы, которая загружается в память при необходимости ее ис-
пользовать и выгружается, когда она не нужна. Это  позволяет
вам  помещать в память только те секции программы, которые в
данный момент необходимы.
     Оверели в Турбо-Паскале основываются на  модулях:  наи-
меньший  участок  кода  может загружаться и выгружаться, как
целый модуль. Вы можете определить сложный  набор  оверлеев,
определяя,  определяя, какой модуль должен или не должен на-
ходится в памяти в данный момент. Кроме того в Турбо-Паскале
имеется превосходная развитая подсистема управления оверлея-
ми, поэтому вам не нужно беспокоиться о загрузке и разгрузке
модулей, поскольку это делается автоматически.
     Более подробно о том, как работать с  оверлеями,  гово-
риться в Главе 13 ("Оверлеи") "Справочного руководства".

             Предложение uses секции реализации
 
     В версии 5.0 Турбо-Паскаль позволяет  вам  размещать  в
секции реализации предложение использования (uses). В случае
его присутствия предложение  uses  должно  следовать  непос-
редственно  за  ключевым  словом  implementation (аналогично
тому, как в интерфейсной секции  предложение  clause  должно
следовать непосредственно за ключевым словом interface).
     Размещение секции реализации предложения uses позволяет
"скрыть"  внутренние  детали модуля, поскольку использемые в
секции реализации модули оказываются "невидимыми" для  того,
кто  этот модуль использует.  Более важным, однако, является
то, что это позволяет вам строить взаимнозависимые модули.
     Поскольку программные модули в Турбо-Паскале не обязаны
иметь строго иерархическую структуру, то допускается исполь-
зовать циклические ссылки на модули. В следующем разделе по-
казан  пример,  демонстрирющий полезное использование цикли-
ческих ссылок.
 
               Циклические ссылки на модули
 
     В следующей программе показаны два модуля, которые "ис-
пользуют" друг друга. Основная программа Circular использует
модуль с именем Display. Модуль Display содержит в своей ин-
терфейсной  секции одно программу WriteXY, которая имеет три
параметра: пару координат (x,y) и сообщение  для  вывода  на
экран. WriteXY перемещает курсор в точку (x,y) и выводит там
сообщение. В противном случае она вызыает простую  программу
обработки ошибки.
     Пока мы не видим здесь  ничего  интересного:  процедура
WriteXY  просто используется  вместо процедуры Write. Однако
далее, когда программа обработки ошибки будет выводить сооб-
щение  на  экран,  начинаются  циклические  ссылки (ведь при
этом она снова использует WriteXY). Таким образом, мы  имеем
процедуру  WriteXY, вызывающую  процедуру  обработки  ошибки
SwapError, которая в свою очередь вызывает WriteXY для выво-
да  сообщения на экран. Если у вас уже от всего этого закру-
жилась голова, не беда. Давайте рассмотрим  исходный  код  в
примере и увидим, что все это не столь уж запутано.

     Основная программа Circular очищает экран  и  выполняет
три обращения к процедуре WriteXY:
 
  program Circular;
  { выводит текст, используя WriteXY }
 
  uses
     Crt, Display;
 
  begin
    ClrScr;
    WriteXY(1, 1, 'Левый верхний угол экрана');
    WriteXY(100, 100, 'За пределами экрана');
    WriteXY(81 - Lenght('Снова в экран..'), 15, 'Снова в
 экран..');
  end.

     Взгляните н координты (x,y) ири втором обращении к про-
цедуре   WriteXY.   В  точке  с  координатами  (100,100)  на
80х25-символьном экране вывести  текст  невозможно.  Давайте
теперь посмотрим, как работает процедура WriteXY. Далее при-
веден текст исходного кода модуля Display, в котором  содер-
жится  процедура WriteXY. Если координаты (x,y) являются до-
пустимыми, она выводит на экран сообщение. В противном  слу-
чае она выводит сообщение об ошибке.
 
 unit Display;
 { содержит простую программу вывода информации на ээкран }
 
 interface
 
 procedure WriteXY(X,Y : integer, Message : string);
 
 inplementation
 uses
    Crt, Error;
 procedure WriteXY(X,Y : integer, Message : string);
 begin
   if (X in [1..80] and Y in [1..25] then
   begin
     Goto(X,Y);
     Write(Message);
   end;
   else
     ShowError('Неверные координаты в процедуре WriteXY');
  end;
 
  end.
 
     Процедура ShowError, вызываемая  в  процедуре  WriteXY,
показана в приведенном далее исходном коде модуля Error. Она
всегда выводит сообщение об ошибке на 25-й строке экрана.
 
  unit Error;
  { содержит простую программу сообщения об ошибке }
  
  interface
 
  procedure ShowError(ErrMsg : string);
 
  inplementation
 
  uses
     Display;
 
  procedure ShowError(ErrMsg :string);
  begin
    WriteXY(1,25, 'Ошибка: '+ ErrMsg);
  end;
 
  end.
 
     Обратите внимание, что предложения uses в секции реали-
зации обоих модулей (Display и Error) ссылаются друг на дру-
га. Эти два модуля могут ссылаться друг на  друга  в  секции
реализации благодаря тому, что Турбо-Паскаль может для обеих
модулей выполнять  полную  компиляцию  интерфейсных  секций.
Другими словами, компилятор Турбо-Паскаля оспринимает ссылку
на частично скомпилированный модуль А  в  секции  реализации
модуля  В,  если  интерфейсные секции модуля А и модуля В не
зависят друг от друга (и, следовательно, строго собблюдаются
правила Турбо-Паскаля, касающиеся порядка описания).
 
             Совместное использование описаний
 
     Можно модифицировать процедуру WriteXY  таким  образом,
чтобы  она  воспринимала  дополнительный  параметр, задающий
прямоугольное окно на экране:
 
  procedure WriteXY(SomeWindow : WindRec;
                    X, Y :       integer;
                    Message :    string);
 
  procedure ShowError(Somewindow : WindRec;
                      ErrMsg : string);
 
     Нужно учитывать, что две процедуры находятся  в  разных
модулях.  Даже  если  вы  описываете WindData в интерфейсной
секции одного модуля, то нет такого допустимого  способа,  с
помощью  которого это описание могло бы бать доступно в дру-
гом модуле. Решение состоит в том, чтобы описать третий  мо-
дуль,   в   котором  содержится  только  определение  записи
WindRec:
 
 unit WindData;
 integface
 type
   WindRec = record
               X1, Y1, X2, Y2 : integer;
               ForeColor,
               BackColor      : byte;
               Active         : boolean;
             end;
  inplementation
  end.
 
     В добавление к  тому,  что  модификация  кода  процедур
WriteXY и ShowError позволяет использовать новый параметр, в
интерфейсной секции модулей Display и  Error   теперь  модет
использоваться  WindData.  Это  допустимо,  така  как модуль
WindData не зависит от своего  предложения  uses,  а  модули
Display  и Error ссылаются друг на друга только в соответст-
вующих секциях реализации.
 
                      Утилита TPUMOVER
 
     При использовании стандартных модулей времени  выполне-
ния  (System,  Dos  и так далее) нет необходимости указывать
директиву {$U <имя_файла>}, поскольку все эти модули записа-
ны в файл модулей Турбо-Паскаля (TURBO.TPL). Во время компи-
ляции эти модули всегда готовы к использованию. 
     Допустим, вы хотите добавить к этим стандартным модулям
хорошо написанный и полностью отлаженный модуль с тем, чтобы
он загружался в память при запуске компилятора. Каким  обра-
зом  можно переслать его в библиотечный файл стандартных мо-
дулей Турбо-Паскаля? - С помощью утилиты TPUMOVER.EXE.
     Кроме того, TPUMOVER используется для удаления  модулей
из  библиотечного  файла  стандартных модулей Турбо-Паскаля,
благодаря чему уменьшается его размер и  количество  памяти,
необходимой для его загрузки. (Более подробно об использова-
нии TPUMOVER см. в Приложении С "Утилиты Турбо-Паскаля").
     Как вы вероятно поняли, писать собственные модули абсо-
лютно  не  сложно.  Хорошо  написанный, хорошо реализованный
программный модуль упрощает разработку  программы;  проблемы
решаются  только  один  раз,  а не повторно для каждой новой
программы. Более того,  использование  модулей  обеспечивает
простое средство для написания больших программ.



 
                          ГЛАВА 5
 
                    Разработка программ
 
     Теперь вы знаете, как писать программы на  Турбо-Паска-
ле,  как  использовать  встроенные  программные модули и как
создвать свои собственные модули. На этом этапе вы уже може-
те  создавать  программы большие по размеру и разделенные на
несколько исходных файлов. Как работать с  такой  прогаммой?
Настоящая  глава  предлагает  сведения  о том, как разбивать
программу  на  модули,  как  воспользоваться  преимуществами
строенных  средств Make и Build, как использовать автономную
утилиту Make, как использовать условную компиляцию при обра-
ботке  исходного файла и как оптимизировать свою программу с
тем, чтобы достичь более высокой скорости выполнения.
 
                   Организация программы
 
     Турбо-Паскаль версии 5.0 позволяет разбивать  программу
на сегменты кода. Ваша основная программа представляет собой
одиночный сегмент кода, который при компиляции преобразуется
в  машинный код, не превышающий 64К. Однако, это ограничение
можно преодолеть, разбив программу на модули. Каждый  модуль
также  может при компиляции содержать до 64К машинного кода.
Вопрос состоит в том, как организовать программу в виде  со-
вокупности модулей.
     Первый шаг состоит в том, чтобы собрать все  глобальные
определения:  констант,  типов  данных и переменных - в один
модуль; назовем его МyGlobals. Это необходимо,  если  другие
модули  обращаются  к этим определениям. В отличие от файлов
типа include (включаемых файлоа) модули  не  могут  "видеть"
определения,  указанные  в  основной  программе. Им доступно
только то, что содержится в их собственной интерфейсной сек-
ции  и в секциях интерфейса модулей, которые они используют.
Однако, модули могут использовать МyGlobals и таким  образом
ссылаться на все глобальные описания.

     Вторым модулем может быть МyUtils. В этом модуле  можно
собрать все вспомогатегатальные подпрограммы, используемые в
других частях Вашей программы. Это должны быть подпрограммы,
не зависящие ни от каких других подпрограмм (за исключением,
может быть, других подпрограмм из МyUtils).
     Помимо этого можно распределить процедуры и функции  по
логическим  группам. В каждой группе, как правило, находятся
несколько процедур и функций, которые вызываются  из  других
частей  программы,  а также несколько (или множество) проце-
дур/функций, которые вызываются первыми. Такая группа предс-
тавляет собой превосходный модуль. Покажем, как его создать:
 
     - Скопируйте все процедуры и функции в отдельный файл и
удалите их из основной программы.
     - Откройте этот файл для редактирвания.
     - Введите следующие строки перед процедурами и функция-
ми:
 
     unit имя_модуля;
       interface
       uses MyGlobals;
       implementation
 
где "имя_модуля" представляет собой имя модуля  (а  также  и
имя редактируемого вами файла).
     - В самом конце файла напечатайте end.
     - В пространство между interface и implementation  ско-
пируйте  заголовки процедур и функций, которые вызываются из
других частей программы. Эти  заголовки  представляют  собой
первую строку в каждой подпрограмме и начинаются с procedure
(или function).
     - Если модуль использует  какие-либо  другие  процедуры
или  функции, то напечатайте их имена (разделенные запятыми)
в операторе uses между MyGlobals и точкой с запятой.
     - Оттранслируйте созданный вами модуль.
     - Возвратитесь в основную программу и добавьте имя  мо-
дуля в оператор uses в начале программы.

     В идеальном случае  желательно,  чтобы  программа  была
структурирована таким образом, чтобы при работе над каким-то
определенным аспектом приходилось модифицировать и  трансли-
ровать заново только один сегмент (модуль или основную прог-
рамму). Это уменьшает время компиляции и, что  более  важно,
позволяет работать с небольшими по размеру и легко управляе-
мыми частями кода.
 
                       Инициализация
 
     Прежде всего запомните, что каждый модуль может (необя-
зательно) иметь свой собственный код инициализации. Этот код
автоматически выполняется при загрузке программы. Если прог-
рамма  использует несколько модулей, то выполняется код ини-
циализации каждого модуля.  Порядок  выполнения  зависит  от
места  модуля в списке оператора uses в программе; таким об-
разом, если программа содержит оператор:
 
     uses MyGlobal,MyUtils,EditLib,GraphLib; 
 
то  секция инициализации  МyGlobals  (если  она имеется) бу-
дет вызвана первая,  за  ней  -  МyUtils,  ЕditLib  и  затем 
GrafLib.
     Для создания секции инициализации модуля поместите клю-
чево  слово begin перед словом end, которым завершается сек-
ция реализации. Это будет определением секции  инициализации
модуля точно так же, как пара begin..end определяет основное
тело программы, процедуры или функции. Сюда  можно  вставить
любой  код  на  Паскале.  Он может содержать ссылки на любой
элемент, описанный в этом модуле, - как в  открытой  (интер-
фейс),  так  и закрытой (реализация) секциях; он может также
содержать ссылки на любой элемент из секции интерфейса любо-
го модуля, который используется данным модулем.
 
                   Средства Make и Build
 
     Турбо-Паскаль предлагает важное средство для разработки
программ:  встроенную утилиту Make. Для уяснения ее важности
обратимся вновь к предыдущему примеру.
     Допустим, имеется программа МYАРР.PAS, которая  исполь-
зует  четыре модуля: МyGlobals, МyUtils, ЕditLib и GraphLib.
Эти четыре модуля  содержатся  в  четырех  текстовых  файлах
МYGLOBAL.PAS, МYUTILS.PAS, ЕDITLIВ.PAS и GRAPHLIВ.PAS, соот-
ветственно. Кроме  того,  МyUtils  использует  МyGlobals,  а
ЕditLib и GraphLib используют МyGlobals и МyUtils.
     При трансляции МYАРР.PAS компилятор осуществляет  поиск
файлов МGLOBAL.ТРU, МYUTILS.ТРU, ЕDITLIВ.ТРU и GRAPHLIВ.ТРU,
загружает их в память, компонует их с кодом, полученным  при
компиляции МYАРР.PAS и записывает все в МYАРР.ЕХЕ (если ком-
пиляция выполняется на диск). 
     Допустим, вам необходимо внести некоторые  изменения  в
ЕDITLIВ.PAS.   Для   того,   чтобы  получить  новый  вариант
МYАРР.PAS, необходимо оттранслировать заново  ЕDITLIВ.PAS  и
МYАРР.PAS. Это немного скучная, но не сложная задача.
     Теперь допустим, что вы внесли изменения в  интерфейсню
секцию МYGLOBAL.PAS. Для обновления МYАРР.ЕХЕ необходимо от-
транслировать заново все четыре модуля и саму МYАРР.PAS. Это
означает,  что при каждом изменении МYGLOBAL.PAS потребуется
выполнить пять отдельных компиляций, что  послужит  для  вас
достаточным поводом не использовать широко программные моду-
ли. Однако, подождите делать выводы.
 
                       Средство Make
 
     Как вы уже  догадались,  Турбо-Паскаль  предлагает  для
этого  решение.  С  помощью  средства Make Вы можете указать
Турбо-Паскалю выполнить всю эту работу. Задача  не  сложная:
после  внесения каких-либо изменений в модули и/или в основ-
ную программу, вам нужно будет заново оттранслировать только
основную программу.
     Турбо-Паскаль в этом случае выполняет три  вида  прове-
рок.
 
     - Во-первых, для каждого модуля, используемого програм-
мой,  Турбо-Паскаль  проверяет  и  сравнивает времена и даты
создания файла с расширением .ТРU и соответствующего файла с
расширением  того,  как  был создан файл с расширением .ТРU,
Турбо-Паскаль заново оттранслирует файл с расширением .PAS и
создаст  обновленный файл с расширением .ТРU. Поэтому в пер-
вом примере, когда в файл ЕDITLIВ.PAS вносятся изменения,  а
затем  заново  транслируется  МYАРР.PAS  (с помощью средства
Make),   Турбо-Паскаль   автоматически   перед   компиляцией
МYАРР.PAS заново оттранслирует ЕDITLIВ.PAS.
     - Вторая проверка состоит в том,  что  устанавливается,
были ли внесены изменения в секцию интерфейса модифицируемо-
го модуля. Если это имело место, то Турбо-Паскаль заново от-
транслирует все модули, использующие данный модуль.
     Во втором примере, когда изменения  вносятся  в  секцию
интерфейса  файла МYGLOBAL.PAS, а затем МYАРР.PAS транслиру-
ется заново, Турбо-Паскаль автоматически  перед  компиляцией
МYАРР.PAS  оттранслирует  заново  МYGLOBAL.PAS,  МYUTIL.PAS,
ЕDITLIВ.PAS и GRAPHLIВ.PAS (в этом  порядке).  Однако,  если
изменения  будут  внесены только в секцию реализации, то нет
необходимости заново транслировать другие зависимые  модули,
поскольку  (в том, что касается их) указанный модуль изменен
не был.
     - Третья проверка состоит в том,  что  устанавливается,
были  ли  внесены  изменения в какие-либо файлы типа Include
или в объектные файлы  (.OBJ),  содержащие  подпрограммы  на
языке  ассемблера  и  используемые  каким-либо модулем. Если
данный файл с расширением файла, с которыми он  компонуется,
то  соответствующий модуль транслируется заново. Таким обра-
зом, если вы внесете изменения в подпрограммы на  языке  ас-
семблера,  используемые  вашим модулем, и оттранслируете их,
то при компиляции программы, использующей указанный  модуль,
последний будет автоматически оттранслирован заново.
 
     При работе в интегрированной среде программирования для
использования  средства Make необходимо выбрать команду Make
(Соз- дать) из меню Compile (Компилировать) или нажать  кла-
вишу  F9.  При работе с компилятором, использующим командные
строки, для вызова этого средства укажите параметр /М. Отме-
тим,  что  средства Make не имеет силы для модулей, содержа-
щихся в ТURВО.TPL.
 
                       Средство Build
 
     Средство  Build  представляет  собой   частный   случай
средства  Make.  При  компиляции  программы с использованием
средства Build Турбо-Паскаль автоматически заново транслиру-
ет все модули, используемые данной программой (за исключени-
ем, конечно, модулей из ТURВО.TPL). Это простой  способ  га-
рантировать, что все будет обновлено.
     При работе в интегрированной среде программирования для
использования  параметра  Build  необходимо  выбрать команду
Build (Построить) из меню Compile (Компилировать). При рабо-
те с компилятором, использующим командные строки, для вызова
этого средства укажите параметр /В.
 
                  Автономная утилита Make
 
     Турбо-Паскаль предлагает простой доступ к большому  на-
бору  средств для обработки больших, сложных программ, пост-
роенных из многочисленных модулей, исходных и объектных фай-
лов.  Он  может  автоматически  выполнить  операцию Make или
Build и заново оттранслировать модули в случае  необходимос-
ти.  Однако, понятно, что Турбо-Паскаль не содержит средства
для получения объектных файлов для программ на языке  ассем-
блера  (файлов  с  расширением .АSМ), в которые были внесены
изменения. Для этого необходимо использовать  отдельный  ас-
семблер. Таким образом, вопрос заключается в том, как обнов-
лять файлы с расщирением .АSМ и .OBJ?
     Ответ простой: используйте утилиту MAKE, находящуюся на
вашем  диске. МАКЕ является интеллектуальным программным ад-
министратором, который - при задании определенных  команд  -
выполнит  все  необходимое  для обновления программы. В дей-
ствительности возможности МАКЕ значительно шире. Эта утилита
создает резервные копии, удаляет файлы из различных подкато-
логов и даже автоматически запускает ваши  программы,  внося
изменения  в  файлы  данных, которые они используют. По мере
использования МАКЕ вы обнаружите и другие способы  использо-
вания этой утилиты для разработки программ.
     МАКЕ представляет собой автономную утилиту. Она отлича-
ется  от  средств Make и Build, входящих в состав интегриро-
ванной среды программирования или компилятора, использующего
командные строки. Полное описание утилиты МАКЕ дано в Прило-
жении С, а здесь приводится пример,  иллюстрирующий  ее  ис-
пользование.
 
                      Небольшой пример
 
     Допустим, вы пишите несколько программ,  осуществляющих
вывод на экран дисплея информации о соседних звездных систе-
мах. Одна программа,  GETSTARS.PAS,  считывает  в  текстовый
файл  список звездных систем, осуществляет некоторую его об-
работку и создает файл двоичных данных, содержащий  получен-
ную информацию.
     GETSTARS.PAS использует три модуля:  STARDEFS.TPU,  со-
держащий глобальные определения; STARLIB.TPU, содержащий не-
которые прикладные подпрограммы, и STARPROC.TPU, осуществля-
ющий основную обработку. Исходный код этих модулей находится
в файлах STARDEFS.PAS,  STARLIB.PAS  и  STARPROC.PAS,  соот-
ветственно.
     Отметим следующие зависимости. STARDEFS.PAS не  исполь-
зует  никаких  других  модулей,  STARLIB.LIB.PAS  использует
STARDEFS;  STARPROC.PAS  использует  STARDEFS   и   STARLIB;
GETSTARS.PAS использует STARDEFS, STARLIB и STARPROC.
     Таким образом, для получения GETSTARS.EXE  Вам  следует
просто оттранслировать GETSTARS.PAS. Турбо-Паскаль (и в слу-
чае интегрированной среды программирования, и в случае  ком-
пилятора,  использующего командные строки) оттранслирует за-
ново все необходимые модули. 
     Допустим, что вы переписали  несколько  подпрограмм  из
STARLIB.PAS  на  языке  ассемблера, создав файлы SLIB1.ASM и
SLIB2.ASM. При трансляции этих файлов получены  SLIB1.OBJ  и
SLIB2.OBJ.  Каждый раз при компиляции STARLIB.PAS эти объек-
тные файлы компонуются вместе с ней. Фактически,  Турбо-Пас-
каль   заново   оттранслирует  STARLIB.PAS  в  случае,  если
STARLIB.TPU окажется старше любого из этих объектных файлов.
     Однако, как быть, если объектный файл  окажется  старше
файла  с расширением .ASM, от которого он зависит? Это озна-
чает, что определенный файл с расширением .ASM требует  пов-
торной трансляции. Турбо-Паскаль не способен оттранслировать
такие файлы; что же в этом случае делать?
     Необходимо создать файл типа  make  и  вызвать  утилиту
МАКЕ. Файл типа make состоит из зависимостей и команд. Зави-
симости указывают МАКЕ, какие файлы зависят от данного  фай-
ла.  Команды указывают МАКЕ, как создать данный файл из дру-
гих.
 
                  Создание файла типа make
 
     Ваш файл типа make в этом случае может иметь  следующий
вид:
 
getstars.exe: getstars.pas stardefs.pas starlib.pas \
    slib1.asm slib2.asm slib1.obj slib2.obj  tpc getstars /m
 
slib1.obj: slib1.asm
  A86 slib1.asm slib1.obj
 
slib2.obj: slib2.asm 
  A86 slib2.asm slib2.obj

     Это выглядит несколько непонятным. Дадим пояснения:
 
     - Первые две строки указывают  МАКЕ,  что  GETSTARS.ЕХЕ
зависит от трех файлов на языке Паскаля, двух файлов на язы-
ке ассемблера и двух объектных файлов (обратная косая  черта
в  конце  первой  строки  указывает МАКЕ игнорировать разрыв
строки, поскольку определение зависимостей  продолжается  на
следующей строке).
     Третья строка указывает МАКЕ, как построить новый  GET-
STARS.ЕХЕ. Отметим, что для обработки GETSTARS.PAS вызывает-
ся компилятор, использующий командные строки, и используется
встроенное в Турбо-Паскаль средство Make (параметр /М).
     - Следующие две  строки  (пустая  строка  игнорируется)
указывают  МАКЕ, что SLIВ1.OBJ зависит от SLIВ1.АSМ, и опре-
деляют, как построить новый файл SLIВ1.OBJ.
     - Две последние строки определяют  зависимости  (факти-
чески только от одного файла) и действие МАКЕ для SLIВ2.OBJ.
 
                     Использование МАКЕ
 
     Допустим, что вы создали этот файл с помощью  редактора
среды  программирования  Турбо-Паскаля (или с помощью любого
другого редактора, работающего в кодах ASCII) и записали его
на диск в виде STARS.MAK. Вы можете его использовать, указав
в команде:
 
     make -fstars.mak
 
где -f является параметром, указывающим МАКЕ, какой файл ис-
пользовать.  МАКЕ  обрабатывает этот файл с нижней строки по
верхнюю. Вначале она проверяяет, не  является  ли  SLIB2.OBJ
старше  SLIB2.ASМ.  Если это имеет место, то МАКЕ выдает ко-
манду:
 
     A86 SLIB2.asm SLIB2.obj
 
по которой осуществляется трансляция SLIB2.ASM  и  создается
новая версия SLIB2.OBJ. Затем выполняется такая проверка для
SLIB1.ASM и в случае необходимости выдается такая же  коман-
да.  Наконец, проверяются все зависимости для GETSTARS.EXE и
в случае необходимости выдается команда:
 
     tpc getstars /m
 
     Параметр /М указывает Турбо-Паскалю использовать собст-
венные  внутренние  подпрограммы  МАКЕ, которые разрешат все
зависимости   модулей,    включая    повторную    трансляцию
STARLIB.PAS  в случае, если SLIB1.OBJ или SLIB2.OBJ окажутся
более новыми, чем STARLIB.TPU.
     Это только простой  пример  использования  МАКЕ.  Более
полное описание можно найти в Приложении С.
 
                    Условная компиляция
 
     С тем, чтобы упростить разработку программ,  Турбо-Пас-
каль предлагает условную компиляцию. Это означает, что с по-
мощью параметров и определенных  символов  можно  указывать,
какие части программы следует компилировать.
     По формату  условные  директивы  аналогичны  директивам
компилятора,  с  которыми  вы уже имеете навык работы; иными
словами, они имеют следующий формат:
 
     {$директива арг}
 
где  "директива"  обозначает  директиву  (например,  DEFINE, 
IFDEF и другие), а "арг" является  аргументом.  Отметим, что 
между "директива" и "арг" должен быть  разделитель  (пробел, 
символ  табуляции). В Таблице 5.1 перечислены  все  условные 
директивы и их значения.
                                            Таблица 5.1
              Список директив компилятора
-----------------------------------------------------------
{$DEFINE символ}    Определяет символ для других директив
{$UNDEF символ}     Отменяет определение символа
{$IFDEF символ}     Если символ определен, то последующий 
                    код транслируется
{$IFNDEF символ}    Если символ не определен, то последу-
                    ющий код транслируется
{$IFOPT х+}         Если директива х активизирована, то 
                    последующий код транслируется
{$IFOPT х-}         Если директива х не активизирована, 
                    то последующий код транслируется
{$ELSE}             Если предыдущая директива IFxxx не 
                    дает знания Тrue (истинно), то после-
                    дующий код транслируется
{$ENDIF}            Обозначает конец секций IFxxx или ЕLSЕ
-----------------------------------------------------------
 
                  Директивы DEFINE и UNDEF

     Директивы IFDEF и IFNDEF проверяют, определен некоторый
символ  или нет. Эти символы определяются с помощью директив
DEFINE и UNDEF. (Вы можете определять символы также в коман-
дной строке и в интегрированной среде программирования).
     Для определения символа вставьте в свою  программу  ди-
рективу:
 
     {$DEFINE символ}
 
Где "символ"  подчиняется  обычным  правилм  для идентифика-
торов в отношении длины, допустимых символов и других специ-
фикаций. Например, можно определить:
 
     {$DEFINE debug} 
 
    Это является определением символа debug  для  оставшейся  
части  программы  или  до того момента, как встретится  опе-
ратор:
 
  {$UNDEF debug}
 
     Как вы уже  догадались,  UNDEF  "отменяет  определение"
символа.  Если  символ  не  определен, то UNDEF ни на что не
влияет.
 
               Определение в командной строке
 
     Если вы работаете с компилятором Турбо-Паскаля, исполь-
зующим  командные  строки (ТРС.ЕХЕ), то вы можете определить
условные символы в самой командной строке. ТРС  воспринимает
параметр /D, за которым следует список символов, разделенных
точкой с запятой:
 
     tpc myproc /Ddebug; test; dump
 
В этой строке определяются символы debug, test  и  dump  для
программы  MYPROG.PAS. Отметим, что параметр /D является ку-
мулятивным, поэтому следующая командная строка  эквивалентна
предыдущей:
 
     tpc myprog /Ddebug /Dtest /Ddump
 
    Определение в интегрированной среде программирования

     Условные символы можно определить с помощью  меню  "Оп-
ции/Компилятор/Условные  определения"  (О/С/Соnditional  de-
fines). Вы можете определить несколько символов, поместив их
в рамку ввода и разделив точками с запятыми. Синтаксис имеет
такой же вид, как и в случае компилятора с командными  стро-
ками.
 
                     Встроенные символы
 
     Помимо символов, определяемых пользователем, можно осу-
ще-  ствлять  проверку некоторых символов, определенных Тур-
бо-Паскалем (предопределенных или  встроенных  символов).  В
Таблице  6.2 перечислены эти символы. Затем приводится более
подробное описание каждого из них.
                                                Таблица 6.2
                  Встроенные условные символы
------------------------------------------------------------
VER50    Всегда определен (ТР 5.1 будет определять VER51 и
         т.д.)
MSDOS    Всегда определен
CPU86    Всегда определен
CPU87    Определен, если на этапе компиляции функционирует
         процессор 8087
------------------------------------------------------------
 
                        Символ VER50
 
     Символ VER40 всегда определен (по крайней мере  в  Тур-
бо-Паскале  версии  5.0).  Каждая  последующая  версия будет
иметь соответствующий встроенный символ. Например, в  версии
5.1  будет определен VER51, а в версии 6.0 - VER60 и так да-
лее. Это позволит создавать исходные  коды,  которые  смогут
использовать последующие расширения в случае, если последние
сохранят совместимость с версией 5.0.
 
                   Символы MSDOS и CPU86
 
     Эти символы всегда определены (по крайней мере  в  Тур-
бо-Паскале  версии 5.0, работающем под операционной системой
ДОС фирмы Микрософт). Символ МSDОS указывает, что компиляция
осуществляется  под  управлением  операционной  системы ДОС.
Символ СРU86  означает,  что  компиляция  осуществляется  на
компьютере, использующем процессор iАРх86 фирмы Интел (8088,
8086, 80186, 80286, 80386).
     Поскольку в последующем  станут  доступны  версии  Тур-
бо-Паскаля  для  других операционных систем и процессоров, в
них будут присутствовать аналогичные  символы,  указывающие,
какая операционная система и/или какой процессор используют-
ся. Используя эти символы,  можно  создавать  один  исходный
код,  компилируемый  с помощью Турбо-Паскаля на разных маши-
нах, при условии, что и там компиляция выполняется  по  сло-
вам.
 
                        Символ CPU87
 
     Турбо-Паскаль версии 4.0 поддерживает операции над чис-
лами  с плавающей запятой двумя способами: аппаратно и прог-
раммно. Если в вашем  компьютере  установлен  математический
сопроцессор  80х87,  то вы можете использовать типы данных с
плаващей запятой по стандарту IЕЕЕ (с одинарной точностью, с
двойной  точностью, с повышенной точностью, сложное), и Тур-
бо-Паскаль будет осуществлять прямые вызовы к математическо-
му  процессору.  Если такого процессора в системе нет, то вы
можете использовать вещественный тип данных с плавающей  за-
пятой  (размером  в 6 байт), и Турбо-Паскаль будет поддержи-
вать все операции над ними с помощью программ. Вы  можете  с
помощью директивы $N указать, что Вы будуте использовать.
     При загрузке компилятора  Турбо-Паскаля  осуществляется
проверка  на  наличие микросхемы 80х87. Если она имеется, то
символ СРU87 определяется. В противном случае он не  опреде-
ляется. Поэтому в начале своей программы вы можете поместить
следующий код:
 
  {$IFDEF CPU87}  { Если 80х87 имеется }
  {$N+}           { то используйте внутренний код 8087 }
  {$ELSE} 
  {$N-}          { В обратном случае используйте эмулирующую 
                   бибилиотеку }
  {$ENDIF}
 
Вы можете использовать аналогичную схему для определения пе-
ременных,  или же можно использовать в этих целях  директиву 
{$IFOPT N+}.
 
                Символы IFххх, ELSE и ENDIF
 
     Использование условных директив состоит в том, что  вы-
бирается  некоторая  часть исходного кода, которая будет от-
транслирована, если некоторый символ определен (или не опре-
делен)  или если некоторое средство включено (или не включе-
но). Общий формат имеет следующий вид:
 
     {$IFxxx}
       исходный код
     {$ENDIF}
 
где IFxxx есть или IFDEF, или IFNDEF, или IFOPT, за  которым
следует соответствующий аргумент, а исходный код предствляет
собой некоторый код на Турбо-Паскале. Если выражение  в  ди-
рективе IFxxx имеет значение True (истинно), то исходный код
компилируется. В противном случае он игнорируется, как  если
бы он был комментарием в программе.
     Довольно часто приходится  использовать  альтернативные
части  исходного  кода.  Если  выражение имеет значение True
(истинно), то компилируется одна часть кода, а если  выраже-
ние  имеет значение False (ложно), - то компилируется другая
часть. Турбо-Паскаль позволяет это осуществить с помощью ди-
рективы $ELSE:
 
     {$IFxxx}
       исходный код А
     {$ELSE}
       исходный код В
     {$ENDIF}
 
     Если выражение в IFxxx имеет значение  True  (истинно),
то  компилируется код А, в обратном случае компилируется код
В.
     Отметим, что все директивы IFxxx должны быть  выполнены
внутри  одного  и того же исходного файла, что означает, что
они не могут иметь начало в одном исходном файле, а заверше-
ние  - в другом. Однако, между директивами IFxxx могут нахо-
диться файлы типа include:
 
  {$IFxxx}
  {$I file1.pas}
  {$ELSE}
  {$I file2.pas}
  {$ENDIF}
  
     Таким образом, можно выбирать альтернативные файлы типа
include, осуществляя проверку некоторого условия.
     Конструкции IFxxx..ENDIF можно вкладывать друг в  друга
до 16 уровней, например:
 
{$IFxxx}   {Первая директива IF}
...
{$IFxxx}   {Вторая директива IF}
...
{$ENDIF}   {Завершение первой директивы IF}
...
{$ENDIF}   {Завершение второй директивы IF}
 
      Рассмотрим каждую из директив IFxxx более подробно.
 
                    Директивы IFDEF и IFNDEF

     Теперь вы знаете, как определить символ, и, кроме того,
знаете  о  наличии нескольких встроенных символов. Директивы
IFDЕF и IFNDЕF позволяют  осуществлять  условную  компиляцию
кода  в  зависимости  от того, определены эти символы или не
определены. Этот пример уже встречался ранее:
 
{$IFDEF CPU87}    { Если имеется 80х87 }
{$N+}             { то используйте внутрениий код 8087}
{$ELSE}
{$N-}  { В обратном случае используйте 
         эмулирующую библиотеку }
{$ENDIF}
 
     Вставка этого кода в программу означает  автоматическое
включение  опции $N при наличии математического сопроцессора
8087 во время компиляции программы. Отметим  важный  аспект:
$N  является  средством  этапа  компиляции. Если сопроцессор
8087 имеется в компьютере во время компиляции, то  программа
будет  оттранслирована при активизированной директиве компи-
лятора $N+ или соответствующего средства среды  программиро-
вания, что означает осуществление прямых вызовов к сопроцес-
сору 8087 и использование только тех типов данных с  плаваю-
щей  запятой, которые соответствуют стандарту IEEE. В обрат-
ном случае (при отсутствии 8087) компиляция программы с  ди-
рективой $N или соответствующим средством среды программиро-
вания будет означать, что используется пакет программ  обра-
ботки  данных с плавающей запятой и допускается только стан-
дартный для Турбо-Паскаля вещественный тип данных с  плаваю-
щей  запятой длиной в 6 байт. Если программа оттранслирована
на компьютере, имеющем сопроцессор 8087, то полученный в ре-
зультате файл ЕХЕ не может быть выполнен на машине, не имею-
щей 8087.
     Другим распространенным способом использования директив
IFDEF  и  IFNDEF является отладка. Например, в начале каждой
процедуры вы можете поместить следующий код:
 
{$IFDEF debug}

  Writeln('Теперь введите proc name');
  Readln; {пауза до тех пор, пока пользователь 
           не нажмет "Enter"}
{$ENDIF}
 
где proc name является именем процедуры. Если в начале прог-
раммы поместить следующую директиву:
 
  {$DEFINE debug}
 
и оттранслировать ее, то компилятором  будет  генерироваться
полная  отладочная  информация (для последующей работы с ин-
тегрированный отладчиком или Турбо-отладчиком).  Аналогично,
вы  можете задать включение в текст программы исходного кода
только в случае отладки. Тогда следует записать:
 
     {$IFNDEF debug}
        исходный код
     {$ENDIF}
 
где исходный код компилируется только в том случае,  если  в
этой точке debug определен.
 
                      Директива IFOPT
 
     Иногда бывает необходимо вставить или исключить  код  в
зависимости  от  того,  какие  средства  компилятора выбраны
(проверка диапазона, проверка ввода/вывода, обработка  чисел
и  так далее). Турбо-Паскаль позволяет это сделать с помощью
директивы IFOPT, которая имеет две формы:
 
     {$IFOPT x+} и
     {$IFOPT x-}
 
где x обозначает один из параметров компилятора: A, B, D, E, 
F, I, L, N, O, R, S, T, V (более полное описание можно найти
в Приложении С "Справочного  руководства"). В случае  первой
формы  последующий код транслируется, если параметр компиля-
тора в текущий момент активизирован. В случае  второй  формы
код транслируется, если в текущий момент параметр не активи-
зирован. Поэтому в качестве примера можно  записать  следую-
щее:
 
var
  {$IFOPT N+}
    Radius,Circ,Area  : double;
  {$ELSE}
    Radius,Circ,Area  : real;
  {$ENDIF}
 
     В этом примере выбирается тип данных для  перечисленных
переменных на основе того, необходима или нет поддержка соп-
роцессора 8087. Если этот пример скомбинировать с ранее ука-
занным  примером ({$IFDEF CPU87}), то для исходного кода бу-
дет автоматически выбираться надлежащий параметр компилятора
и  типы  данных  в  зависимости от того, имеется ли во время
компиляции в компьютере сопроцессор 8087 или нет.
     Рассмотрим альтернативный пример:
 
Assign(F,Filename);
Reset(F);
{$IFOPT I-}
IOCheck;
{$ENDIF}
 
где IOCheck представляет собой написанную пользователем про-
цедуру, которая получает значение IOResult и печатает в слу-
чае необходимости сообщение об ошибке. Не имеет  смысла  ис-
пользовать  IOCheck, если указан параметр $I+, поскольку при
обнаружении ошибки выполнение  программы  будет  остановлено
прежде, чем будет вызвана IOCheck.
 
                   Оптимизация программы
 
     Несколько средств компилятора оказывают влияние на раз-
мер  и  скорость выполнения программы. Это происходит в силу
того, что благодаря им в программе  осуществляется  проверка
ошибок  и  обработка ошибок. Эти средства лучше использовать
при разработке  программы,  однако  в  окончательной  версии
программы  имеет  смысл  их  отключить.  Ниже  приведены эти
средства и те их установки, которые имеют значение для опти-
мизации:
 
     - {$A-} разрешает для переменных  и  типизованных  кон-
стант выравнивание на границу слова.  Для процессоров  80х86
это приводит к более быстрому доступу к памяти. По умолчанию
эта опция установлена.
     - {$B-} устанавливает вычисление булевских выражений по
корот- кой схеме. В зависимости от установленного режима вы-
числения булевских выражений можно получить код, который вы-
полняется быстрее.
     - {$E-} устанавливает в зависимости от значения  дирек-
тивы {$N} использование аппаратных средств  для  стандартных
6-байтовых данных вещественного типа. По умалчанию директива
имеет значение {E-}.
     - {$I-} выключает проверку ошибок ввода/вывода.  Вызвав
встроенную  функцию IOResult, программа может сама обрабаты-
вать ошибки ввода/вывода.
     - {$N-} генерирут для операций с плавающей запятой код,
использующий сопроцессор 8087. Если эта директива выключена,
то все операции с плавающей запятой будут выполняться с  ис-
пользованием  программной  эмулирующей  библиотеки,  которая 
служит для работы со встроенным 6-байтовым типом  веществен-
ных чисел. Если эта директива установлена, то будут  исполь-
зоваться соответствующие  аппаратные  средства  (сопроцессор
8087. При задании директивы {$N+} директива {$E} игнорирует-
ся.
     - {$R-} выключает  проверку  диапазона.  При  генерации
кода не осуществляется проверка ошибок в индексировании мас-
сивов и в присвоении значений.
     - {$S-} выключает проверку стека. При генерации кода не
осуществляется  проверка наличия достаточного пространства в
стеке для каждого вызова процедуры или функции.
     - {$V-} отменяет проверку параметров var для строк. Это
позволяет передавать в качестве фактических параметров стро-
ки, длина которых отлична от длины, установленной  для  фор-
мального параметра var.
 
     Отключение каждого их этих средств  имеет  два  преиму-
щества.  Во-первых,  как  правило, это приводит к уменьшению
размера кода и к более быстрому его  выполнению.  Во-вторых,
это  позволяет  избавиться от того, что при обычных условиях
нельзя устранить. Однако это означает определенный риск, по-
этому эти возможности следует использовать с осторожностью и
при непонятном поведении программы  следует  вновь  включить
указанные средства.
     Отметим, что помимо вставки средств компилятора  непос-
редственно в исходный код Вы можете также устанавливать их с
помощью меню "Опции/Компилятор" (Options/Compiler) в  интег-
рированной  интерактивной  среде  программирования или с по-
мощью параметра /$X в командной строке  компилятора  (где  Х
предствляет собой соответствующую букву директивы компилято-
ра).



