Глава 10. Программы и модули
Синтаксис программ
Программа в Borland Pascal состоит из заголовка программы,
необязательного оператора uses и основного блока.
программа
¦ ---------- ---- ----- ----
L---T->¦заголовок+-->¦ ; +---T----------------->¦блок+->¦ . +->
¦ ¦программы¦ L---- ^ ¦ ------------ ^ L----- L----
¦ L---------- ¦ L->¦предложение+--
L----------------------- ¦ uses ¦
L------------
Заголовок программы
Заголовок программы определяет имя программы и ее параметры.
заголовок программы
¦
¦ -------- --------------
L-->¦program+-->¦идентификатор+-T------------------------------>
L-------- L-------------- ¦ ---- ---------- ---- ^
L->¦ ( +->¦параметры+->¦ ) +--
L---- ¦программы¦ L----
L----------
----------------
параметры программы ---->¦ список +---->
¦идентификаторов¦
L----------------
Если заголовок программы присутствует, он является чисто де-
коративной деталью и компилятор его игнорирует.
Оператор uses
Оператор uses идентифицирует все модули, используемые прог-
раммой, включая непосредственно используемые модули и модули, ис-
пользуемые этими модулями.
----- -------------- ----
предложение uses -->¦uses+--T-->¦идентификатор+----->¦ ; +--->
L----- ¦ L-------------- ^ L----
¦ ---- ¦
L----->¦ , +----------
L----
Модуль System всегда используется автоматически. Для под-
держки таких средств, как файловый ввод-вывод, обработка строк,
операции с плавающей запятой, динамическое распределение памяти и
других этот модуль реализует весь нижний уровень, а также обслу-
живающие фоновые программы.
Паскаль, в свою очередь, обслуживает многие стандартные мо-
дули, такие, как Dos и Crt. Это не происходит автоматически: вы
должны обязательно включить их в оператор uses. Например:
uses Dos,Crt; { теперь могут быть доступны средства модулей
Dos и Crt }
Чтобы найти файл, содержащий скомпилированный модуль, компи-
лятор усекает указанное в операторе uses имя модуля до первых
восьми файлов и добавляет расширение файла. Если целевой платфор-
мой является DOS, расширением будет .TPU. Если целевая платформа
- Windows, то расширением файла будет .TPW. Если целевой платфор-
мой является защищенный режим DOS, то расширением файла будет
.TPP. Хотя имена файлов усекаются, в операторе uses должен указы-
ваться полный идентификатор модуля.
Синтаксис модулей
Модули являются основой модульного программирования. Они ис-
пользуются для создания библиотек, которые могут включаться в
различные программы (при этом становится необязательным иметь в
наличии исходный код), а большие программы могут подразделяться
на логически связанные модули.
---------- ---- -----------
модуль ----->¦заголовок+-->¦ ; +-->¦интерфейс-+---
¦ модуля ¦ L---- ¦ный раздел¦ ¦
L---------- L----------- ¦
----------------------------------------
¦ ----------- -------------- ----
L->¦ раздел +--->¦ раздел +-->¦ . +-->
¦реализации¦ ¦инициализации¦ L----
L----------- L--------------
Заголовок модуля
В заголовке модуля определяется имя модуля.
----- ---------------------
заголовок модуля --->¦unit¦-->¦идентификатор модуля¦---->
L----- L---------------------
Имя модуля используется при ссылке на модуль в предложении
использования. Это имя должно быть уникальным, так как два модуля
с одним именем не могут одновременно использоваться.
Имя исходного файла модуля и двоичного файла должны совпа-
дать с идентификатором модуля, усеченным до первых 8 символов.
Если это не так, то компилятор не сможет найти исходный и/или
двоичный файл при компиляции использующей этот модуль программы.
Интерфейсная секция
В интерфейсной секции описываются те константы, типы, пере-
менные, процедуры и функции, которые являются глобальными, то
есть доступными основной программе (программе или модулю, которые
используют данный модуль). Основная программа имеет доступ к этим
элементам, как если бы они были описаны в модуле, являющимся вло-
женным по отношению к данной программе.
интерфейсная секция
¦
¦ ----------
L->¦interfaсe+-T---------------------------------------------T->
¦ ¦ ¦ ------------^ ^ ¦ ------------------- ^ ¦
L---------- L->¦ оператор +- ¦ +->¦ раздел описания +-+ ¦
¦ uses ¦ ¦ ¦ ¦ констант ¦ ¦ ¦
L------------ ¦ ¦ L------------------- ¦ ¦
¦ ¦ ------------------- ¦ ¦
¦ +->¦ раздел описания +-+ ¦
¦ ¦ ¦ типов переменных ¦ ¦ ¦
¦ ¦ L------------------- ¦ ¦
¦ ¦ ------------------- ¦ ¦
¦ +->¦ раздел описания +-+ ¦
¦ ¦ ¦ переменных ¦ ¦ ¦
¦ ¦ L------------------- ¦ ¦
¦ ¦ ------------------- ¦ ¦
¦ L->¦раздел заголовков +-- ¦
¦ ¦процедур и функций¦ ¦
¦ L------------------- ¦
L----------------------------
раздел заголовков
процедур и функций
¦ ---------- ----
L----T-->¦заголовок+---------->¦ ; +-T----------------------->
¦ ¦процедуры¦ ^ L---- ¦ ---------- ---- ^
¦ L---------- ¦ L->¦директива+-->¦ ; +--
¦ ------------------ ¦ ¦ inline ¦ L----
L->¦заголовок функции+-- L----------
L------------------
В том случае, если процедура или функция является процедурой
или функцией типа inline, в интерфейсной секции содержится только
список заголовков процедур или функций. Модуль процедуры или
функции следует дальше в секции реализации. Заметим, что заголо-
вок процедуры или функции может дублироваться и быть здесь таким
же, как в интерфейсной секции. Вам не нужно задавать здесь список
формальных параметров, но если вы это сделали и если описание в
интерфейсной секции и секции реализации не совпадают, то компиля-
тор во время компиляции выдаст сообщение об ошибке.
Секция реализации
В секции реализации определяются модули всех глобальных про-
цедур или функций. В ней также описываются константы, переменные,
процедуры и функции, являющиеся локальными, то есть недоступными
основной программе.
Секция реализации
¦
¦ --------------- -------------------
L->¦implementation+-T------------------>¦ раздел описаний +-->
L--------------- ¦ ------------^ L-------------------
L->¦ оператор +-
¦ uses ¦
L------------
По механизму действия описания процедур и функций в интер-
фейсная секция аналогична опережающему описанию, хотя директива
forward не указывается. Таким образом, эти процедуры и функции
могут быть определены (и к ним можно обращаться в любой последо-
вательности) в секции реализации.
Допускается дублирование заголовков процедур и функций из
интерфейсной части. Вам не нужно при этом задавать список фор-
мальных параметров, но если вы это делаете, компилятор на этапе
компиляции в случае несовпадения описаний в интерфейсной части и
секции реализации будет выдавать сообщение об ошибке.
Секция инициализации
Секция инициализации является последней секцией модуля. Она
может состоять либо из зарезервированного слова end (в этом слу-
чае модуль не содержит кода инициализации), либо из операторной
части, которая должна выполняться для инициализации модуля.
----
секция инициализации ---T-->¦end+------------------>
¦ L---- ^
¦ ------------------ ¦
L->¦операторная часть+--
L------------------
Секции инициализации модулей, которые используются програм-
мой, выполняются в том же порядке, в каком модули указаны в опе-
раторе uses.
Косвенные ссылки на модули
В операторе uses в основной программе должны содержаться
имена всех модулей, непосредственно или косвенно используемых ос-
новной программой. Рассмотрим следующий пример:
Program Prog;
uses Unit1, Unit2
const a = b;
begin
end.
end.
unit Unit2;
interface
uses Unit1;
const b = c;
implementation
end.
unit Unit1;
interface
const c = 1;
implementation
const d = 2;
end;
В данном примере Unit12 непосредственно зависит от Unit1, а
Prog непосредственно зависит от Unit2. Кроме того, Prog зависит
косвенно от Unit1 (через Unit1), хотя ни один из описанных в
Unit1 идентификаторов в Prog не доступен.
Для компиляции программы компилятор должен иметь возможность
находить все модули, от которых она прямо или косвенно зависит.
Поэтому, для компиляции Prog компилятор должен иметь возможность
найти и Unit1, и Unit2, иначе возникнет ошибка.
Когда в интерфейсную часть модуля вносятся изменения, другие
модули, использующие этот модуль, должны быть заново скомпилиро-
ваны. При использовании команд Make или Build компилятор делает
это автоматически. Однако, если изменения коснулись только секции
реализации или секции инициализации, то другие модули, в которых
используется этот модуль, перекомпилировать не нужно. В предыду-
щем примере, если интерфейсная часть модуля Unit1 изменилась
(например, с = 2), то модуль Unit2 нужно перекомпилировать. Изме-
нение же секции реализации (например, d = 1) не требует переком-
пиляции Unit2.
При компиляции модуля в Borland Pascal на основе контрольной
суммы интерфейсной секции вычисляется номер версии модуля. В пре-
дыдущем примере при компиляции модуля Unit2 в скомпилированной
версии модуля Unit2 сохраняется номер версии модуля Unit1. При
компиляции основной программы номер версии модуля Unit1 сравнива-
ется с номером версии, сохраненным в модуле Unit2. Если номера
версий не совпадают, что свидетельствует об изменении в интер-
фейсной части модуля Unit1 со времени последней компиляции модуля
Unit2, компилятор, в зависимости от режима компиляции, выдает со-
общение об ошибке или перекомпилирует модуль Unit2 (в зависимости
от режима компиляции).
Перекрестные ссылки на модули
Размещение в секции реализации оператора uses позволяет
"скрыть" внутренние детали модуля, поскольку используемые в сек-
ции реализации модули оказываются "невидимыми" для того, кто этот
модуль использует. Более важным, однако, является то, что это
позволяет вам строить взаимозависимые модули.
В следующей программе показаны два модуля, которые "исполь-
зуют" друг друга. Основная программа Circular использует модуль с
именем Display. Модуль Display содержит в своей интерфейсной сек-
ции одну программу WriteXY, которая имеет три параметра: пару ко-
ординат (x,y) и сообщение для вывода на экран. WriteXY перемещает
курсор в точку (x,y) и выводит там сообщение. В противном случае
она вызывает простую программу обработки ошибки.
Пока мы не видим здесь ничего интересного: процедура WriteXY
просто используется вместо процедуры Write. Однако далее, когда
программа обработки ошибки будет выводить сообщение на экран, на-
чинаются перекрестные ссылки (ведь при этом она снова использует
WriteXY). Таким образом, мы имеем процедуру WriteXY, вызывающую
процедуру обработки ошибки SwapError, которая в свою очередь вы-
зывает WriteXY для вывода сообщения на экран. Если у вас уже от
всего этого закружилась голова, не беда. Давайте рассмотрим ис-
ходный код в примере и увидим, что все это не столь уж запутано.
Основная программа Circular очищает экран и выполняет три
обращения к процедуре WriteXY:
program Circular;
{ выводит текст, используя WriteXY }
uses
WinCrt, 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);
implementation
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);
implementation
uses
Display;
procedure ShowError(ErrMsg :string);
begin
WriteXY(1,25, 'Ошибка: '+ ErrMsg);
end;
end.
Обратите внимание, что операторы uses в секции реализации
обоих модулей (Display и Error) ссылаются друг на друга. Эти два
модуля могут ссылаться друг на друга в секции реализации благода-
ря тому, что Borland Pascal может для обеих модулей выполнять
полную компиляцию интерфейсных секций. Другими словами, компиля-
тор воспринимает ссылку на частично скомпилированный модуль A в
секции реализации модуля В, если интерфейсные секции модуля A и
модуля В не зависят друг от друга (и, следовательно, строго соб-
людаются правила Паскаля, касающиеся порядка описания).
В случае взаимозависимости интерфейсных секций модулей вы
получите ошибку из-за перекрестных ссылок.
Совместное использование описаний
Можно модифицировать процедуру WriteXY таким образом, чтобы
она воспринимала дополнительный параметр, задающий прямоугольное
окно на экране:
procedure WriteXY(SomeWindow : WindRec;
X, Y : integer;
Message : string);
procedure ShowError(Somewindow : WindRec; ErrMsg : string);
Нужно учитывать, что две процедуры находятся в разных моду-
лях. Даже если вы описываете WindData в интерфейсной секции одно-
го модуля, то нет такого допустимого способа, с помощью которого
это описание могло бы быть доступно в другом модуле. Решение сос-
тоит в том, чтобы описать третий модуль, в котором содержится
только определение записи WindRec:
unit WindData;
interface
type
WindRec = record
X1, Y1, X2, Y2 : integer;
ForeColor,
BackColor : byte;
Active : boolean;
end;
implementation
end.
В добавление к тому, что модификация кода процедур WriteXY и
ShowError позволяет использовать новый параметр, в интерфейсной
секции модулей Display и Error теперь может использоваться
WindData. Это допустимо, так как модуль WindData не зависит от
своего оператора uses, а модули Display и Error ссылаются друг на
друга только в соответствующих секциях реализации.
Взаимозависимые модули могут быть полезны в отдельных ситуа-
циях, но использовать их надо аккуратно. Если вы будете применять
их так, где это не требуется, программу станет сложней обслужи-
вать, и она будет больше подвержена ошибкам.
Назад | Содержание | Вперед