Глава 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 ссылаются друг на
        друга только в соответствующих секциях реализации.

             Взаимозависимые модули могут быть полезны в отдельных ситуа-
        циях, но использовать их надо аккуратно. Если вы будете применять
        их так,  где это не требуется,  программу станет сложней обслужи-
        вать, и она будет больше подвержена ошибкам.
                       Назад | Содержание | Вперед