- •Часть I. Язык Borland Pascal...................................12
- •Глава 1. Что такое программа Borland Pascal?...................12
- •Глава 2. Лексемы...............................................22
- •Глава 3. Константы.............................................32
- •Глава 4. Типы..................................................34
- •Глава 5. Переменные и типизированные константы.................72
- •Глава 6. Выражения.............................................93
- •Глава 7. Операторы............................................117
- •Глава 8. Блоки, локальность и область действия................133
- •Глава 9. Процедуры и функции..................................139
- •Глава 10. Программы и модули..................................170
- •Глава 11. Динамически компонуемые библиотеки..................180
- •Глава 12. Библиотеки исполняющей системы......................194
- •Глава 13. Стандартные процедуры и функции.....................199
- •Глава 14. Ввод и вывод........................................215
- •Глава 15. Использование сопроцессора 80x87....................248
- •Глава 16. Модуль Dоs..........................................259
- •Глава 17. Программирование в защищенном режиме dos............270
- •Глава 18. Строки с завершающим нулем..........................297
- •Глава 19. Использование графического интерфейса Borland.......308
- •Глава 20. Использование оверлеев..............................329
- •Часть III. В среде Borland Pascal.............................348
- •Глава 21. Использование памяти................................348
- •Глава 22. Вопросы управления..................................388
- •Глава 23. Автоматическая оптимизация..........................405
- •Часть IV. Использование Borland Pascal с языком ассемблера....411
- •Глава 24. Встроенный ассемблер................................411
- •Глава 25. Компоновка с программами на языке ассемблера........437
- •Часть II "Библиотеки исполняющей системы" содержит информа-
- •Часть I. Язык Borland Pascal
- •Глава 1. Что такое программа Borland Pascal?
- •Глава 2. Лексемы
- •Глава 3. Константы
- •Глава 4. Типы
- •Глава 5. Переменные и типизированные константы
- •Глава 6. Выражения
- •Глава 7. Операторы
- •Глава 8. Блоки, локальность и область действия
- •Глава 9. Процедуры и функции
- •Глава 10. Программы и модули
- •Глава 11. Динамически компонуемые библиотеки
- •Часть II. Глава 12. Библиотеки исполняющей системы
- •Глава 13. Стандартные процедуры и функции
- •Глава 14. Ввод и вывод
- •Глава 15. Использование сопроцессора 80x87
- •Глава 16. Модуль Dоs
- •Глава 17. Программирование в защищенном режиме dos
- •Глава 18. Строки с завершающим нулем
- •Глава 19. Использование графического интерфейса Borland
- •Глава 20. Использование оверлеев
- •Часть III. В среде Borland Pascal
- •Глава 21. Использование памяти
- •Глава 22. Вопросы управления
- •Глава 23. Автоматическая оптимизация
- •Часть IV. Использование Borland Pascal с языком ассемблера
- •Глава 24. Встроенный ассемблер
- •Глава 25. Компоновка с программами на языке ассемблера
Глава 10. Программы и модули
─────────────────────────────────────────────────────────────────
Синтаксис программ
─────────────────────────────────────────────────────────────────
Программа в Borland Pascal состоит из заголовка программы,
необязательного оператора uses и основного блока.
программа
│ ┌─────────┐ ┌───┐ ┌────┐ ┌───┐
└───┬─>│заголовок├──>│ ; ├───┬─────────────────>│блок├─>│ . ├─>
│ │программы│ └───┘ ^ │ ┌───────────┐ ^ └────┘ └───┘
│ └─────────┘ │ └─>│предложение├─┘
└──────────────────────┘ │ uses │
└───────────┘
Заголовок программы
─────────────────────────────────────────────────────────────────
Заголовок программы определяет имя программы и ее параметры.
заголовок программы
│
│ ┌───────┐ ┌─────────────┐
└──>│program├──>│идентификатор├─┬──────────────────────────────>
└───────┘ └─────────────┘ │ ┌───┐ ┌─────────┐ ┌───┐ ^
└─>│ ( ├─>│параметры├─>│ ) ├─┘
└───┘ │программы│ └───┘
└─────────┘
┌───────────────┐
параметры программы ────>│ список ├────>
│идентификаторов│
└───────────────┘
Если заголовок программы присутствует, он является чисто де-
коративной деталью и компилятор его игнорирует.
B.Pascal 7 & Objects/LR - 171 -
Оператор uses
─────────────────────────────────────────────────────────────────
Оператор uses идентифицирует все модули, используемые прог-
раммой, включая непосредственно используемые модули и модули, ис-
пользуемые этими модулями.
┌────┐ ┌─────────────┐ ┌───┐
предложение uses ──>│uses├──┬──>│идентификатор├─────>│ ; ├───>
└────┘ │ └─────────────┘ ^ └───┘
│ ┌───┐ │
└─────>│ , ├─────────┘
└───┘
Модуль System всегда используется автоматически. Для под-
держки таких средств, как файловый ввод-вывод, обработка строк,
операции с плавающей запятой, динамическое распределение памяти и
других этот модуль реализует весь нижний уровень, а также обслу-
живающие фоновые программы.
Паскаль, в свою очередь, обслуживает многие стандартные мо-
дули, такие, как Dos и Crt. Это не происходит автоматически: вы
должны обязательно включить их в оператор uses. Например:
uses Dos,Crt; { теперь могут быть доступны средства модулей
Dos и Crt }
Чтобы найти файл, содержащий скомпилированный модуль, компи-
лятор усекает указанное в операторе uses имя модуля до первых
восьми файлов и добавляет расширение файла. Если целевой платфор-
мой является DOS, расширением будет .TPU. Если целевая платформа
- Windows, то расширением файла будет .TPW. Если целевой платфор-
мой является защищенный режим DOS, то расширением файла будет
.TPP. Хотя имена файлов усекаются, в операторе uses должен указы-
ваться полный идентификатор модуля.
B.Pascal 7 & Objects/LR - 172 -
Синтаксис модулей
─────────────────────────────────────────────────────────────────
Модули являются основой модульного программирования. Они ис-
пользуются для создания библиотек, которые могут включаться в
различные программы (при этом становится необязательным иметь в
наличии исходный код), а большие программы могут подразделяться
на логически связанные модули.
┌─────────┐ ┌───┐ ┌──────────┐
модуль ─────>│заголовок├──>│ ; ├──>│интерфейс-├───┐
│ модуля │ └───┘ │ный раздел│ │
└─────────┘ └──────────┘ │
┌──────────────────────────────────────┘
│ ┌──────────┐ ┌─────────────┐ ┌───┐
└─>│ раздел ├───>│ раздел ├──>│ . ├──>
│реализации│ │инициализации│ └───┘
└──────────┘ └─────────────┘
Заголовок модуля
─────────────────────────────────────────────────────────────────
В заголовке модуля определяется имя модуля.
┌────┐ ┌────────────────────┐
заголовок модуля ───>│unit│──>│идентификатор модуля│────>
└────┘ └────────────────────┘
Имя модуля используется при ссылке на модуль в предложении
использования. Это имя должно быть уникальным, так как два модуля
с одним именем не могут одновременно использоваться.
Имя исходного файла модуля и двоичного файла должны совпа-
дать с идентификатором модуля, усеченным до первых 8 символов.
Если это не так, то компилятор не сможет найти исходный и/или
двоичный файл при компиляции использующей этот модуль программы.
B.Pascal 7 & Objects/LR - 173 -
Интерфейсная секция
─────────────────────────────────────────────────────────────────
В интерфейсной секции описываются те константы, типы, пере-
менные, процедуры и функции, которые являются глобальными, то
есть доступными основной программе (программе или модулю, которые
используют данный модуль). Основная программа имеет доступ к этим
элементам, как если бы они были описаны в модуле, являющимся вло-
женным по отношению к данной программе.
интерфейсная секция
│
│ ┌─────────┐
└─>│interfaсe├─┬─────────────────────────────────────────────┬─>
│ │ │ ┌───────────┐^ ^ │ ┌──────────────────┐ ^ │
└─────────┘ └─>│ оператор ├┘ │ ├─>│ раздел описания ├─┤ │
│ uses │ │ │ │ констант │ │ │
└───────────┘ │ │ └──────────────────┘ │ │
│ │ ┌──────────────────┐ │ │
│ ├─>│ раздел описания ├─┤ │
│ │ │ типов переменных │ │ │
│ │ └──────────────────┘ │ │
│ │ ┌──────────────────┐ │ │
│ ├─>│ раздел описания ├─┤ │
│ │ │ переменных │ │ │
│ │ └──────────────────┘ │ │
│ │ ┌──────────────────┐ │ │
│ └─>│раздел заголовков ├─┘ │
│ │процедур и функций│ │
│ └──────────────────┘ │
└───────────────────────────┘
раздел заголовков
процедур и функций
│ ┌─────────┐ ┌───┐
└────┬──>│заголовок├──────────>│ ; ├─┬───────────────────────>
│ │процедуры│ ^ └───┘ │ ┌─────────┐ ┌───┐ ^
│ └─────────┘ │ └─>│директива├──>│ ; ├─┘
│ ┌─────────────────┐ │ │ inline │ └───┘
└─>│заголовок функции├─┘ └─────────┘
└─────────────────┘
В том случае, если процедура или функция является процедурой
или функцией типа inline, в интерфейсной секции содержится только
список заголовков процедур или функций. Модуль процедуры или
функции следует дальше в секции реализации. Заметим, что заголо-
вок процедуры или функции может дублироваться и быть здесь таким
же, как в интерфейсной секции. Вам не нужно задавать здесь список
формальных параметров, но если вы это сделали и если описание в
интерфейсной секции и секции реализации не совпадают, то компиля-
тор во время компиляции выдаст сообщение об ошибке.
B.Pascal 7 & Objects/LR - 174 -
Секция реализации
─────────────────────────────────────────────────────────────────
В секции реализации определяются модули всех глобальных про-
цедур или функций. В ней также описываются константы, переменные,
процедуры и функции, являющиеся локальными, то есть недоступными
основной программе.
Секция реализации
│
│ ┌──────────────┐ ┌──────────────────┐
└─>│implementation├─┬──────────────────>│ раздел описаний ├──>
└──────────────┘ │ ┌───────────┐^ └──────────────────┘
└─>│ оператор ├┘
│ uses │
└───────────┘
По механизму действия описания процедур и функций в интер-
фейсная секция аналогична опережающему описанию, хотя директива
forward не указывается. Таким образом, эти процедуры и функции
могут быть определены (и к ним можно обращаться в любой последо-
вательности) в секции реализации.
Допускается дублирование заголовков процедур и функций из
интерфейсной части. Вам не нужно при этом задавать список фор-
мальных параметров, но если вы это делаете, компилятор на этапе
компиляции в случае несовпадения описаний в интерфейсной части и
секции реализации будет выдавать сообщение об ошибке.
B.Pascal 7 & Objects/LR - 175 -
Секция инициализации
─────────────────────────────────────────────────────────────────
Секция инициализации является последней секцией модуля. Она
может состоять либо из зарезервированного слова end (в этом слу-
чае модуль не содержит кода инициализации), либо из операторной
части, которая должна выполняться для инициализации модуля.
┌───┐
секция инициализации ───┬──>│end├──────────────────>
│ └───┘ ^
│ ┌─────────────────┐ │
└─>│операторная часть├─┘
└─────────────────┘
Секции инициализации модулей, которые используются програм-
мой, выполняются в том же порядке, в каком модули указаны в опе-
раторе 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 не доступен.
Для компиляции программы компилятор должен иметь возможность
B.Pascal 7 & Objects/LR - 176 -
находить все модули, от которых она прямо или косвенно зависит.
Поэтому, для компиляции Prog компилятор должен иметь возможность
найти и Unit1, и Unit2, иначе возникнет ошибка.
Когда в интерфейсную часть модуля вносятся изменения, другие
модули, использующие этот модуль, должны быть заново скомпилиро-
ваны. При использовании команд Make или Build компилятор делает
это автоматически. Однако, если изменения коснулись только секции
реализации или секции инициализации, то другие модули, в которых
используется этот модуль, перекомпилировать не нужно. В предыду-
щем примере, если интерфейсная часть модуля Unit1 изменилась
(например, с = 2), то модуль Unit2 нужно перекомпилировать. Изме-
нение же секции реализации (например, d = 1) не требует переком-
пиляции Unit2.
При компиляции модуля в Borland Pascal на основе контрольной
суммы интерфейсной секции вычисляется номер версии модуля. В пре-
дыдущем примере при компиляции модуля Unit2 в скомпилированной
версии модуля Unit2 сохраняется номер версии модуля Unit1. При
компиляции основной программы номер версии модуля Unit1 сравнива-
ется с номером версии, сохраненным в модуле Unit2. Если номера
версий не совпадают, что свидетельствует об изменении в интер-
фейсной части модуля Unit1 со времени последней компиляции модуля
Unit2, компилятор, в зависимости от режима компиляции, выдает со-
общение об ошибке или перекомпилирует модуль Unit2 (в зависимости
от режима компиляции).
B.Pascal 7 & Objects/LR - 177 -
Перекрестные ссылки на модули
─────────────────────────────────────────────────────────────────
Размещение в секции реализации оператора 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;
B.Pascal 7 & Objects/LR - 178 -
{ содержит простую программу вывода информации на экран }
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 и
модуля В не зависят друг от друга (и, следовательно, строго соб-
B.Pascal 7 & Objects/LR - 179 -
людаются правила Паскаля, касающиеся порядка описания).
В случае взаимозависимости интерфейсных секций модулей вы
получите ошибку из-за перекрестных ссылок.
Совместное использование описаний
─────────────────────────────────────────────────────────────────
Можно модифицировать процедуру 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 ссылаются друг на
друга только в соответствующих секциях реализации.
Взаимозависимые модули могут быть полезны в отдельных ситуа-
циях, но использовать их надо аккуратно. Если вы будете применять
их так, где это не требуется, программу станет сложней обслужи-
вать, и она будет больше подвержена ошибкам.
B.Pascal 7 & Objects/LR - 180 -
───────────────────────────────────────────────────────────────────────