ГЛАВА 9. ПРОГРАММЫ И МОДУЛИ. Синтаксис программы. Программа в Turbo Pascal имеет такой же вид, как объявление процедуры, за исключением заголовка программы и необязательного предложения uses. программа │ ┌─────────┐ ┌─┐ ┌────┐ ┌─┐ └───┬─Ў│заголовок├──Ў│;├────┬──────────────────Ў│блок├──Ў│.├──Ў │ │программы│ └─┘ ° │ ┌───────────┐ ° └────┘ └─┘ │ └─────────┘ │ └─Ў│предложение├─┘ └────────────────────┘ │ uses │ └───────────┘ Заголовок программы. Заголовок программы определяет имя программы и ее параметры. заголовок программы │ │ ┌───────┐ ┌─────────────┐ └──Ў│program├──Ў│идентификатор├─┬──────────────────────────────Ў └───────┘ └─────────────┘ │ ┌─┐ ┌─────────┐ ┌─┐ ° └─Ў│(├──Ў│параметры├──Ў│)├─┘ └─┘ │программы│ └─┘ └─────────┘ ┌───────────────┐ параметры программы ────Ў│ список ├────Ў │идентификаторов│ └───────────────┘ Если заголовок программы присутствует, то он является чисто декоративной деталью и игнорируется компилятором. Предложение uses. Предложение uses идентифицирует все модули, используемые программой, включая непосредственно используемые модули и модули, используемые этими модулями. ┌────┐ ┌─────────────┐ ┌─┐ предложение uses ───Ў│uses├──┬──Ў│идентификатор├─────Ў│;├───Ў └────┘ │ └─────────────┘ ° └─┘ │ ┌─┐ │ └─────Ў│,├───────────┘ └─┘ Модуль System всегда используется автоматически. System реализует весь нижний уровень, поддержку программ времени выполнения для поддержки таких средств, как файловый ввод/вывод, обработка строк, операции с плавающей запятой, динамическое распределение памяти и других. Кроме System, Turbo Pascal реализует многие стандартные модули, такие как Printer, Dos и Crt. Они не используются автоматически: Вы должны обязательно включить их в предложение uses, например: uses Dos, Crt; {теперь получен доступ к средствам Dos и Crt} Порядок перечисления модулей в uses определяет порядок их инициализации. См. раздел "Раздел инициализации". Cинтаксис модулей. Модули являются в Turbo Pascal основой модульного программирования. Они используются для создания библиотек, которые могут включаться в различные программы (при этом становится необязательным иметь в наличии исходный код), а большие программы могут подразделяться на логически связанные модули. ┌─────────┐ ┌─┐ ┌──────────┐ модуль ─────Ў│заголовок├──Ў│;├──Ў│интерфейс-├───┐ │ модуля │ └─┘ │ный раздел│ │ └─────────┘ └──────────┘ │ ┌────────────────────────────────────┘ │ ┌──────────┐ ┌─────────────┐ ┌─┐ └─Ў│ раздел ├───Ў│ раздел ├──Ў│.├──Ў │реализации│ │инициализации│ └─┘ └──────────┘ └─────────────┘ Заголовок модуля. В заголовке модуля определяется имя модуля ┌────┐ ┌────────────────────┐ заголовок модуля ───Ў│unit│──Ў│идентификатор модуля│────Ў └────┘ └────────────────────┘ Имя модуля используется при ссылке на модуль в предложении uses. Это имя должно быть уникальным, так как два модуля с одним именем не могут использоваться одновременно. Интерфейсный раздел. В интерфейсном разделе объявляются те константы, типы, переменные, процедуры и функции, которые являются глобальными, то есть доступными основной программе (программе или модулю, которые используют данный модуль). Основная программа имеет доступ к этим элементам, как если бы они были бы объявлены в блоке, который включает главную программу. интерфейсный раздел │ │ ┌─────────┐ └─Ў│interfaсe├─┬──────────────────────────────────────────────┬─Ў │ │ │ ┌───────────┐° ° │ ┌──────────────────┐ ° │ └─────────┘ └─Ў│предложение├┘ │ ├─Ў│раздел объявления ├──┤ │ │ uses │ │ │ │ констант │ │ │ └───────────┘ │ │ └──────────────────┘ │ │ │ │ ┌──────────────────┐ │ │ │ ├─Ў│раздел объявления ├──┤ │ │ │ │типов переменных │ │ │ │ │ └──────────────────┘ │ │ │ │ ┌──────────────────┐ │ │ │ ├─Ў│раздел объявления ├──┤ │ │ │ │переменных │ │ │ │ │ └──────────────────┘ │ │ │ │ ┌──────────────────┐ │ │ │ └─Ў│раздел заголовков ├──┘ │ │ │процедур и функций│ │ │ └──────────────────┘ │ └────────────────────────────┘ раздел заголовков процедур и функций │ ┌─────────┐ ┌─┐ └────┬──Ў│заголовок├──────────Ў│;├─┬───────────────────────Ў │ │процедуры│ ° └─┘ │ ┌─────────┐ ┌─┐ ° │ └─────────┘ │ └─Ў│директива├──Ў│;├─┘ │ ┌─────────────────┐ │ │ inline │ └─┘ └─Ў│заголовок функции├─┘ └─────────┘ └─────────────────┘ Если процедура или функция не объявлена как inline, то интерфейсный раздел только перечисляет заголовки процедур и функций. Тела процедур и функций находятся в разделе реализации. Раздел реализации. В разделе реализации определяются модули всех глобальных процедур и функций. В нем также описываются константы, типы, переменные, процедуры и функции, являющиеся локальными, то есть недоступными основной программе. раздел реализации │ │ ┌─────────┐ └─Ў│implemen-├─┬────────────────────┬─────────────────────────┬─> │ tation │ │ ┌───────────┐ ° ° │ ┌──────────────────┐ ° │ ° │ └─────────┘ └─>│предложение├─┘ │ ├─>│раздел объявления ├─┤ │ │ uses │ │ │ │ меток │ │ │ └───────────┘ │ │ └──────────────────┘ │ │ │ │ ┌──────────────────┐ │ │ │ ├─>│раздел объявления ├─┤ │ │ │ │ констант │ │ │ │ │ └──────────────────┘ │ │ │ │ ┌──────────────────┐ │ │ │ ├─>│раздел объявления ├─┤ │ │ │ │типов переменных │ │ │ │ │ └──────────────────┘ │ │ │ │ ┌──────────────────┐ │ │ │ ├─>│раздел объявления ├─┤ │ │ │ │переменных │ │ │ │ │ └──────────────────┘ │ │ │ │ ┌──────────────────┐ │ │ │ └─>│раздел объявления ├─┘ │ │ │процедур и функций│ │ │ └──────────────────┘ │ └───────────────────────────┘ По механизму действия объявление процедур и функций в интерфейсном разделе аналогично forward объявлению, хотя директива forward не указывается. Таким образом, эти процедуры и функции могут быть определены (и к ним можно обращаться в любой последовательности) в разделе реализации. Примечание: Заголовки процедур и функций могут быть сдублированы из интерфейсного раздела. Необязательно задавать список формальных параметров, но если вы сделали это, то в случае несоответствия объявления в интерфейсном разделе и разделе реализации компилятор выдаст ошибку времени компиляции. Раздел инициализации. Раздел инициализации является последним разделом модуля. Он может состоять либо из зарезервированного слова end (в этом случае модуль не содержит кода инициализации), либо из операторной части, которая должна выполняться для инициализации модуля. ┌───┐ раздел инициализации ───┬──>│end├──────────────────> │ └───┘ ° │ ┌─────────────────┐ │ └─>│операторная часть├─┘ └─────────────────┘ Разделы инициализации модулей, которые используются программой, выполняются в том же порядке, в каком модули указаны в предложении uses. Косвенные ссылки на используемые модули. В предложении uses в программе или модуле указываются имена только тех модулей, которые явно используются этим модулем (или программой). Рассмотрим следующий пример: program Prog; uses Unit2; const a = b; begin end. unit Unit2; interfase uses Unit1; const b = c; implementation end. unit Unit1; interfase const c = 1; implementation const d = 2; end. В приведенном выше примере Unit2 явно зависит от Unit1, а Prog явно зависит от Unit2. Более того, Prog неявно зависит от Unit1 (через Unit2), хотя ни один из идентификаторов, объявленных в Unit1, недоступен Prog. Чтобы откомпилировать модуль, Turbo Pascal должен найти все модули, от которых этот модуль зависит (явно или неявно). Так, чтобы откомпилировать Prog, компилятор должен найти Unit1 и Unit2, в противном случае произойдет ошибка. Когда в интерфейсную часть модуля вносятся изменения, другие модули, использующие этот модуль, должны быть заново откомпилированы. Однако, если изменения коснулись только раздела реализации или раздела инициализации, то другие модули, в которых используется этот модуль, перекомпилировать не нужно. В предыдущем примере, если интерфейсная часть модуля Unit1 изменялась (например С = 2), то модуль Unit2 нужно перекомпилировать. Изменение же раздела реализации (например, D=1) не требует перекомпиляции Unit2. При компиляции модуля в Turbo Pascal на основе контрольной суммы интерфейсного раздела вычисляется номер версии модуля. В предыдущем примере при компиляции модуля Unit2 в откомпилированной версии модуля Unit2 сохраняется номер версии модуля Unit1. При компиляции основной программы номер версии модуля Unit1 сравнивается с номером версии, сохраненным в модуле Unit2. Если номера версий не совпадают, что свидетельствует об изменении в интерфейсной части модуля Unit1 со времени последней компиляции модуля Unit2, то компилятор, в зависимости от режима компиляции, выдает сообщение об ошибке или перекомпилирует модуль Unit2. Круговые ссылки модулей. Включение предложения uses в разделе реализации модуля позволяет вам скрыть внутренние элементы модуля, так как модули, используемые в разделе реализации, невидимы для пользователей этого модуля. Еще более важно, однако, что это также позволяет создавать взаимно зависимые модули. Следующая программа показывает, как два модуля могут "использовать" друг друга. Основная программа, Circular, использует модуль Display. Display содержит одну программу, WriteXY, которая имеет три параметра: пару координат (X,Y) и текстовое сообщение для вывода. Если точка с координатами (X,Y) находится на экране, то WriteXY передвигает курсор в (X,Y) и выводит сообщение, в противном случае она вызывает простую программу - обработчик ошибок. До сих пор нет ничего сложного - WriteXY находится в Write. Где же здесь встречается круговая ссылка модулей и как программа-обработчик ошибок выведет свое сообщение об ошибке? Использованием WriteXY снова. Таким образом, имеется WriteXY, которая вызывает программу обработчик ошибок ShowError, которая вызывает WriteXY, чтобы поместить сообщение об ошибке на экран. Если ваша голова закружилась, посмотрите на исходный код примера, и вы увидите, что здесь нет ничего хитрого. Основная программа Circular, очищает экран и трижды вызывает WriteXY : program Circular; {выводит на экран текст, используя WriteXY} uses Crt, Display; begin ClrScr; WriteXY(1, 1, 'Верхний левый угол экрана'); WriteXY(100, 100, 'За пределами экрана'); WriteXY(81 - Length('Возврат на экран'), 15, 'Back to reality'); end. Посмотрите на координаты (X,Y) во втором вызове WriteXY. Трудно вывести текст в точке (100,100) на экран 80x25 строк. Далее, давайте посмотрим, как работает WriteXY. Приведем исходный текст модуля Display, содержащий процедуру WriteXY. Если координаты (X,Y) действительны, то он выводит на экран сообщение; в противном случае WriteXY выводит сообщение об ошибке : unit Display; {содержит простую программу вывода на экран} interface procedure WriteXY(X,Y: Integer; Message: String); implementation uses Crt, Error; procedure WriteXY(X,Y: Integer; Message: String); begin if (X in [1..80]) and (Y in [1..25]) then begin GoToXY(X, Y); Write(Message); end; else ShowError('Неправильные координаты WriteXY'); end; end. Процедура ShowError, вызываемая процедурой WriteXY, объявлена в модуле Еrror. ShowError всегда выводит свое сообщение об ошибке на 25 строке экрана : unit Error; {содержит простую программу-обработчик ошибок} interface procedure ShowError(ErrMsg: String); implementation uses Display; procedure ShowError(ErrMsg: String); begin WriteXy(1, 25, 'Error: ' + ErrMsg); end; end. Заметим, что предложение uses в разделах реализаций Display и Error ссылаются друг на друга. Эти два модуля могут ссылаться друг на друга в своих разделах реализации, потому что Turbo Pascal может компилировать полные разделы interface для обоих модулей, другими словами Turbo Pascal допускает ссылку на частично откомпилированный модуль А в разделе реализации модуля В, пока интерфейсные разделы модулей А и В не зависят друг от друга (и это следует строгим правилам Паскаля для порядка объявления). Разделение других объявлений. Предположим, что Вы захотели изменить WriteXY и ShowError так, чтобы ввести дополнительный параметр, который задает прямоугольное окно на экране : procedure WriteXY(SomeWindow: WindRec; X, Y: Integer; Message: String); Procedure ShowError (SomeWindow: WindRec; ErrMsg: String); Напомним, что эти процедуры находятся в различных модулях. Даже если вы объявите WindData в интерфейсе одного модуля, то нельзя законным путем сделать это объявление доступным в интерфейсе другого модуля. Решением этой проблемы является создание третьего модуля, содержащего только определение записи окна: unit WindData; interface type WindRec = record X1, Y1, X2, Y2: Integer; ForeColor, BackColor: Byte; Activ: Boolean; end; implementation end. Вдобавок к модификации кода WriteXY и ShowError для использования нового параметра, интерфейсные разделы и модуля Display, и модуля Error могут теперь "использовать" WindData. Это стало возможным потому, что модуль WindData не имеет зависимостей в предложении uses, а модуль Display и Error ссылаются друг на друга только в соответствующих разделах реализации.