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

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.

Отметим, что  имя модуля может предшествовать любому идентификатору: константе, типу данных, переменной или подпрограмме.

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.EXE.

Кроме того, утилита TPUMOVER используется для удаления модулей из библиотечного файла стандартных  модулей  Borland Pascal,  благодаря чему уменьшается его размер и количество памяти,  необходимой для его загрузки.

Примечание: Более подробно  об  использовании  утилиты  TPUMOVER см. в "Руководстве по инструментальным средствам и  утилитам".

Как вы вероятно поняли,  писать собственные модули абсолютно  не сложно.  Хорошо написанный,  хорошо реализованный программный  модуль упрощает разработку программы;  проблемы  решаются только  один раз,  а не повторно для каждой новой программы.  Более того,  использование модулей обеспечивает простое средство для написания  больших программ.