Программа в 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+--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 ссылаются друг на друга только в соответствующих секциях реализации. Взаимозависимые модули могут быть полезны в отдельных ситуа- циях, но использовать их надо аккуратно. Если вы будете применять их так, где это не требуется, программу станет сложней обслужи- вать, и она будет больше подвержена ошибкам.
Назад | Содержание | Вперед