Глава 7. Модули Borland Pascal

             В данной главе разъясняется,  что такое модуль,  как он  ис-
        пользуется,  какие  встроенные модули доступны пользователю,  как
        писать собственные программные модули и как компилировать их.


Что такое модуль?

Borland Pascal обеспечивает вам доступ к большому числу встроенных констант, типов данных, переменных, процедур и функ- ций. Некоторые из них специфичны для Borland Pascal, другие спе- цифичны для приложений Windows. Их количество велико, однако, в своей программе вы редко используете их все сразу. Поэтому они разделены на связанные группы, называемые модулями. В этом случае можно использовать только те модули, которые необходимы в прог- рамме. Используя модули, вы можете разбивать программу на отдельные части и компилировать их отдельно. Программный модуль (unit) представляет собой набор констант, типов данных, переменных, про- цедур и функций, которые могут совместно использоваться несколь- кими программами. Каждый модуль аналогичен отдельной программе на Паскале: он может иметь основное тело, которое вызывается перед запуском вашей программы и осуществляет необходимую инициализа- цию. Все описания внутри модуля связаны друг с другом. Например, модуль Strings содержит все описания, необходимые для подпрограмм работы со строками с завершающим нулем. Borland Pascal предоставляет пользователю ряд стандартных модулей, таких как System, Crt WinCrt и др. Они поддерживают наши программы Borland Pascal и все записаны в одну из трех библиотех исполняющей системы (в зависимости от целевой платформы). Библиотеки исполняющей системы для целевой платформы Таблица 7.1 +-------------------------+------------------------+ | Имя библиотеки | Целевая платформа | +-------------------------+------------------------| | TURBO.TPL | DOS реального режима | | TPW.TPL | Windows | | TPP.TPL | DOS защищенного режима | +-------------------------+------------------------+ Ваша программа может использовать любую из процедур и функ- ций в этих модулях, и вам не потребуется писать их заново.

Структура модуля

Структура модуля аналогична структуре программы, однако есть несколько существенных различий. Например, рассмотрим модуль: unit <идентификатор>; interface uses <список модулей>; { Необязательный } { глобальные описания } implementation uses <список_модулей>; { Необязательный } { локальные описания } { реализация процедур и функций } begin { код инициализации } end. Заголовок модуля начинается зарезервированным словом unit, за которым следует имя модуля (идентификатор) точно так же, как и в случае имени программы. Следующим элементом в модуле является ключевое слово interface. Оно обозначает начало интерфейсной секции модуля - части, доступной всем другим модулям или програм- мам, в которых он используется. Программный модуль может использовать другие модули, для этого они определяются в операторе uses. Оператор uses (если он имеет место) может содержаться в двух местах. Во-первых он может следовать сразу после ключевого слова interface. В этом случае любые константы и типы данных, описанные в интерфейсной секции этих модулей, могут использоваться в любом описании в интерфейс- ной части данного модуля. Во-вторых, он может следовать немедленно за ключевым словом implementation. В этом случае все описания из этих модулей могут использоваться только в секции реализации.

Интерфейсная секция

Интерфейсная часть - "общедоступная" часть в модуле - начи- нается зарезервированным словом interface, следует сразу после заголовка модуля и заканчивается перед зарезервированным словом implementation. Интерфейс определяет, что является "видимым" (доступным) для любой программы (или модуля), использующей данный модуль. В интерфейсной части (секции) модуля можно определять константы, типы данных, переменные, процедуры и функции. Как и в программе, они могут быть расположены в любом порядке, и секции могут встречаться повторно (например, ваша программа может содер- жать секцию var, за которой следует секция const, а затем другая секция var). Процедуры и функции, видимые для любой программы, использующей данный модуль, описываются в секции интерфейса, однако их действительные тела - реализации - находятся в секции реализации. Вам не нужно использовать описания forward, и они не допускаются. В интерфейсной части перечисляются все заголовки процедуры и функции. Секция реализации содержит программную логи- ку процедур и функций.

Секция реализации

Секция реализации - "приватная" часть - начинается зарезервированным словом implementation. Все, что описано в секции интерфейса, является видимым в секции реализации: константы, типы, переменные, процедуры и функции. Кроме того, в секции реализации могут быть свои дополнительные описания, которые не являются видимыми для программ, использующих этот модуль. Программа не знает об их существовании и не может ссылаться на них или обращаться к ним. Однако, эти скрытые эле- менты могут использоваться (и, как правило, используются) "види- мыми" процедурами и функциями, то есть теми подпрограммами, чьи заголовки указаны в секции интерфейса. Оператор uses может содержаться в секции реализации (implementation) и должен непосредственно следовать за ключевым словом implementation. Обычные процедуры и функции, описанные в интерфейсной сек- ции, то есть те из них, которые не являются подставляемыми (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, а между ними - операто- ры, то получившийся составной оператор, очень похожий на основное тело программы, становится секцией инициализации модуля (initialization). Секция инициализации представляет собой место, где инициали- зируются структуры данных (переменных), которые использует прог- раммный модуль или которые он делает доступными программе, ис- пользующей данный модуль. Вы можете использовать эту секцию для открытия файлов, которые программа использует позднее. При выполнении программы, использующей некоторый модуль, секция инициализации этого модуля вызывается перед запуском ос- новного тела программы. Если программа использует более одного модуля, то секции инициализации всех модулей вызываются (в поряд- ке, указанном в операторе uses в программе) перед тем, как выпол- нить основное тело программы.

Как используются модули?

Модули, которые использует ваша программа, уже оттранслиро- ваны и хранятся, как машинный код, а не как исходный код на Пас- кале, поскольку они не являются включаемыми файлами. Даже интер- фейсная секция хранится в специальном двоичном формате таблицы идентификаторов, используемом в Borland Pascal. Более того, опре- деленные стандартные модули хранятся в специальном файле (TURBO.TPL, TPW.TPL или TPP.TPL) и автоматически загружаются в память вместе с Borland Pascal. В результате использование одного или нескольких модулей очень незначительно увеличивает время компиляции вашей программы (обычно менее, чем на секунду). Фактически, если модуль скомпилирован, его использование сохраняет вам время при перекомпиляции. Поскольку компилятор не перекомпилирует модуль, пока он не изменяется, использование мо- дулей в программе ускорит процесс ее построения. Как указывалось ранее, для использования специального модуля или набора модулей необходимо в начале программы поместить опера- тор uses, после которого указать список имен тех модулей, которые будут использоваться. Имена их должны разделяться запятыми: program MyProg; uses thisUnit, thatUnit, theOtherUnit; Когда компилятор встречает такой оператор uses, он прибавля- ет информацию из секции интерфейса каждого модуля к таблице иден- тификаторов и присоединяет машинный код, представленный в секции реализации, к самой программе. Модули присоединяются к таблице идентификаторов в указанном порядке. Порядок модулей в операторе uses значения не имеет. Если модуль thisUnit использует thatUnit или наоборот, вы можете опи- сать их в любом порядке, а компилятор определит, какой модуль нужно скомпоновать с программой MyProg первым. Фактически, если модуль thisUnit использует thatUnit, но MyProg не вызывает непос- редственно ни одну из подпрограмм в модуле thatUnit, вы можете "скрыть" подпрограммы модуля thatUnit, опустив его в операторе uses: unit thisUnit; uses thatUnit; . . . program MyProg; uses thisUnit, theOtherUnit; . . . В этом примере модуль thisUnit может вызывать любую подпрог- рамму модуля thatUnit, а программа MyProg может вызывать любую из подпрограмм модуля thisUnit или theOtherUnit. Однако, программа MyProg не может вызывать подпрограммы модуля thatUnit, поскольку thatUnit не указывается в операторе uses программы MyProg. Если в программе не указан оператор uses, Borland Pascal в лю- бом случае присоединит стандартный модуль System. Этот модуль обеспечит выполнение некоторых стандартных подпрограмм Borland Pascal, а также нескольких подпрограмм, специфических для Borland Pascal.

Ссылки на описания модуля

Как только вы включили модуль в свою программу, все констан- ты, типы данных, переменные, процедуры и функции, описанные в секции интерфейса этого модуля, становятся доступными для вашей программы. Например, допустим, имеется следующий модуль: unit MyStuff; interface const MyValue = 915; type MyStars = (Deneb,Antares,Betelgeuse); var MyWord : string[20]; procedure SetMyWord(Star : MyStars); function TheAnswer : integer; implementation . . . end. Как можно видеть здесь в интерфейсной части модуля, та часть модуля, которая находится в интерфейсной секции, является видимой для вашей программы (и может ею использоваться). С учетом этого можно написать следующую программу: program TestStuff; uses MyStuff; var I : integer; AStar : MyStars; begin Writeln(myValue); AStar := Deneb; SetMyWord(AStar); Writeln(MyWord); I := TheAnswer; Writeln(I) end. После включения в программу оператора uses MyStuff вы можете ссылаться на все идентификаторы, описанные в интерфейсной секции модуля М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 и TheAnswer. В действительности вы имели полное право написать первую программу следующим образом: 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 секции реализации

Borland Pascal позволяет вам размещать в секции реализации оператор uses. В случае его присутствия оператор uses должен сле- довать непосредственно за ключевым словом implementation (анало- гично тому, как в интерфейсной секции оператор uses должен следо- вать непосредственно за ключевым словом interface). Размещение в секции реализации оператора uses позволяет "скрыть" внутренние детали модуля, поскольку используемые в сек- ции реализации модули оказываются "невидимыми" для того, кто этот модуль использует. Более важным, однако, является то, что это позволяет вам строить взаимнозависимые модули. Поскольку программные модули в Borland Pascal не обязаны иметь строго иерархическую структуру, то допускается использовать циклические ссылки на модули. О циклических ссылках на модули рассказывается в Главе 10 "Справочного руководства по языку".

Стандартные модули

Модули библиотек исполняющей системы Borland Pascal загружа- ются в память вместе с Borland Pascal; вы всегда можете их ис- пользовать. Обычно библиотеки исполняющей системы (TURВО.TPL, TPW.TPL и TPP.TPL) находятся в том же каталоге, что и компилятор (TURBO.EXE. BPW.EXE и BP.EXE).

Создание ваших собственных модулей

Если вы хотите написать модуль, содержащий некоторые полез- ные подпрограммы, и использовать эти подпрограммы в своих прог- раммах, напишите модули и сохраните его под именем, заданным в заголовке модуля. Borland Pascal сохраняет файл с расширением .PAS, как и любой другой файл, созданный в редакторе Borland Pascal. В исходном файле может содержаться только один модуль.

Компиляция модуля

Скомпилировать модуль вы можете двумя способами. Вы можете: * Скомпилируйте модуль с помощью команды Compile|Compile. Вместо создания файла .EXE Borland Pascal создает файл .TPU, .TPW или .TPP. +------------------------+-------------------------+ | Целевая платформа | Расширение имени файла | | | модуля | +------------------------+-------------------------| | DOS реального режима | .TPU | | Windows | .TPW | | DOS защищенного режима | .TPP | +------------------------+-------------------------+ Например, если ваш модуль называется MYUNIT.PAS, если це- левой платформой является Windows, он компилируется в MYUNIT.TWP. * Для компиляции программы, которая включает в себя оператор uses, используйте команду Compile|Make или Compile|Build. В зависимости от целевой платформы, создается файл .TPU, .TPW или .TPP. Примечание: О том, как использовать оператор uses, рассказывается в следующем разделе.

Доступность модуля для программы

Скопируйте свой новый файл .TPU, .TPW или .TPP в каталог мо- дулей, заданный в диалоговом окне Options|Directories, или ис- пользуйте параметр командной строки /U при работе с компилятором режима командной строки. Если вы поместите свой модуль в заданный каталог модулей, то сможете ссылаться на этот модуль, даже если он не находится в те- кущем каталоге или в библиотеках исполняющей системы. Включите в любую программу, где вы хотите использовать свой новый модуль, оператор uses. Например, если ваш новый модуль на- зывается INTLIB.TPW, то задайте в своей программе оператор следу- ющего вида: uses IntLib; Чтобы найти модуль, имя которого указано в операторе uses, Borland Pascal проверяет его наличие в библиотеке исполняющей системы, загруженной в память в время инициализации. Примечание: О том, как поместить модуль в библиотеку исполняющей системы, рассказывается ниже. Если модуль в библиотеке исполняющей системы отсутствует, то компилятор ищет его на диске, сначала в текущем каталоге, затем в каталогах, заданных в качестве каталогов модулей (Options| Directories). Компилятор предполагает, что имя файла совпадает с именем модуля, а расширение имени файла - это .TPU, .TPW или .TPP. Исходный текст модуля имеет расширение .PAS.

Пример

Теперь напишем небольшой модуль. Назовем его 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, а за- тем скомпилируйте, задав в качестве целевой платформы защищенный режим DOS. В результате получим код модуля в файле INTLIВ.ТРP. Перешлем его в каталог модулей (если такой имеется), или оставив в том же каталоге, где находится следующая программа, которая ис- пользует модуль 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 }

Модули и большие программы

До сих пор мы говорили о модулях как о библиотеках - наборах полезных подпрограмм, которые могут использоваться несколькими программами. Однако, у модуля есть еще одна функция - разбивать большую программу на составные части. Два аспекта Borland Pascal способствуют использованию моду- лей в такой функции: * высокая скорость компиляции и компоновки; * способность работать с несколькими файлами одновременно, например, с программой и несколькими модулями. Обычно большая программа разбивается на модули, которые группируют процедуры по их функциям. Например, программа редакто- ра может быть разделена на части, выполняющие инициализацию, рас- печатку, чтение и запись файлов, форматирование и так далее. Так- же, как имеется основная программа, определяющая глобальные конс- танты, типы данных, переменные, процедуры и функции, так же может иметь место и "глобальный" модуль, который используется всеми другими модулями. Набросок большой программы-редактора может иметь вид: program Editor; uses WinCrt, String { стандартные модули из TPW.TPL } EditGlobals, { модули, написанные пользователем } EditInuit, EditPrint, EditRead, EditWrite, EditFormat; { описание, процедуры и функции программы } begin { основная программа } end. { конец программы Editor } Модули в данной программе могут содержаться в TPW.TPL, биб- лиотеке исполняющей системы Windows, или быть отдельными файлами .TPW. В последнем случае Borland Pascal выполняет за вас управле- ние проектом. Это означает, что при перекомпиляции программы Editor с помощью встроенного в компилятор средства формирования Borland Pascal сравнивает даты каждого файла .PAS и .TPW и пере- компилирует любой модуль, исходный код которого перекомпилирован. Другая причина использования модулей в больших программах состоит в ограничения кодового сегмента. Процессоры 8086 (и родс- твенные им) ограничивают размер сегмента кода 64 килобайтами. Это означает, что основная программа и любой данный сегмент на может превышать 64К. Borland Pascal интерпретирует это, создавая для каждого модуля отдельный сегмент кода. Без этого объем кода вашей программы не мог бы превышать 64К. Примечание: Подробнее о работе с большими программными проектами рассказывается в Главе 4 "Программирование в ин- тегрированной среде для DOS".

Утилита TPUMOVER

Допустим, вы хотите добавить стандартным модулям хорошо на- писанный и полностью отлаженный модуль с тем, чтобы он загружался в память при запуске компилятора. Переслать его в библиотечный файл стандартных модулей можно с помощью утилиты TPUMOVER.EXE. Кроме того, утилита TPUMOVER используется для удаления моду- лей из библиотечного файла стандартных модулей Borland Pascal, благодаря чему уменьшается его размер и количество памяти, необ- ходимой для его загрузки. Примечание: Более подробно об использовании утилиты TPUMOVER см. в "Руководстве по инструментальным средствам и утилитам". Как вы вероятно поняли, писать собственные модули абсолютно не сложно. Хорошо написанный, хорошо реализованный программный модуль упрощает разработку программы; проблемы решаются только один раз, а не повторно для каждой новой программы. Более того, использование модулей обеспечивает простое средство для написания больших программ.
                              Назад | Содержание | Вперед