Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Косвенные и перекрестные ссылки на модули⇐ ПредыдущаяСтр 23 из 23
Вначале рассмотрим случай косвенного использования модуля. Например, пусть имеются два модуля:
Здесь, как ясно видно, модуль В использует модуль А, и если в вызывающей программе будет использовано предложение uses вида: USES B; = Uses B, A; то все равно при этом неявно(косвенно) будет использован и модуль А, и он, естественно, будет тоже прикомпонован к вызывающей программе. Для случая перекрестных ссылок вначале рассмотрим следующий пример. Пусть имеются два следующих модуля:
Здесь модули А и В с помощью косвенной рекурсии обращаются сами к себе. Такие отношения между модулями недопустимы, т.е. недопустимо прямое или косвенное обращение модуля к самому себе через интерфейсную секцию. В этом случае можно завести третий модуль и поместить в него все те типы, переменные, константы, процедуры и функции из первых двух модулей, которые ссылаются друг на друга. После этого надо удалить эти общие ресурсы из первых двух модулей и подключить заведенный третий модуль. Таким приемом часто пользуются, когда нужно какие-то типы, переменные и константы сделать общими для программы и/или разных модулей. Проблема здесь в том, что само понятие " модуль" было задумано для того, чтобы сделать модули независимыми от глобальных переменных в основной программе. Выход в данном случае состоит в том, чтобы создать модуль из одних только этих глобальных описаний и подключать его везде, где требуется обращение к общим переменным, типам и константам:
Однако допускаются перекрестные связи между модулями через секции реализации. В следующей программе показаны два модуля, которые " используют" друг друга. Основная программа Message использует модуль с именем Display. Модуль Display содержит в своей интерфейсной секции одну процедуру WriteXY, которая имеет три параметра: пару координат (x, y) и сообщение для вывода на экран. WriteXY перемещает курсор в точку (x, y) и выводит там сообщение. В противном случае она вызывает простую программу обработки ошибки. Пока мы не видим здесь ничего интересного: процедура WriteXY просто используется вместо процедуры Write. Однако далее, когда программа обработки ошибки будет выводить сообщение на экран, начинаются перекрестные ссылки (ведь при этом она снова использует WriteXY). Таким образом, мы имеем процедуру WriteXY, вызывающую процедуру обработки ошибки SwapError, которая в свою очередь вызывает WriteXY для вывода сообщения на экран. Пусть основная программа Message очищает экран и выполняет три обращения к процедуре WriteXY: program Message; { выводит текст, используя 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 Gotoxy(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 и модуля В не зависят друг от друга (и, следовательно, строго соблюдаются правила Паскаля, касающиеся порядка описания).
Пример модуля (стек) Рассмотрим пример построения модуля, объединяющего в себе средства работы со структурой данных " стек" . В общем случае стек (магазин) работает по принципу " первым пришел - последним ушел" (LIFO), причем доступ к элементам стека возможен только через одно место - через т.н. голову стека, которая изменяет свое положение после каждой записи/чтения в/из стек/стека. В зависимости от реализации стек м.б. бесконечным (при реализации в виде динамической связанной структуры, где память выделяется и освобождается динамически - при выполнении программы/модуля) и конечным (при реализации в виде статической структуры - массива, для которой память выделяется только один раз - при компиляции программы). Чаще всего стек используется в 2 случаях: 1) При интерпретации выражений, где до определенного момента операнды заталкиваются в стек. 2) При работе в таких ситуациях, где надо одновременно держать в памяти несколько поколений какого-то объекта, при этом доступен будет лишь самый «молодой» потомок. Пример – текстовые окна. Пусть в стек надо записать последовательность символов a, b, c,....... Обобщенный вид стека при выполнении действий:
Реализация стека в виде динамически связанной структуры: Голова Голова Голова
После записи a После записи b После записи c Реализация в виде массива из 3-х элементов:
Исходное состояние После записи a После записи b После записи c После чтения c Мы будем в нашем примере реализовывать стек с помощью массива (массив, как известный Паскалю тип данных будет использован в качестве носителя для значений неизвестного Паскалю типа данных). При этом способ реализации скроем в секции реализации, так что в программе, использующей наш модуль, ничего не изменится при изменении реализации стека. Замечания: 1) Голова стека будет указывать на ту ячейку, в которую должна выполняться текущая запись. 2) Условимся, что голова стека не может перескакивать за границу массива, т.е. значение головы стека должно быть не больше числа элементов массива. Итак, модель стека пусть имеет следующий вид:
Необходимые переменные Const n=10; {размер стека} Var j: byte; {голова стека} s: array[1..10] of byte; {носитель} FULL: boolean; {стек полон} EMPTY: boolean; {стек пуст } elem: byte; { то, что помещается в стек при Записи } Исходное состояние (надо уставливать в секции инициализ.): EMPTY: =true; FULL: =false; j: =1;
NB: Во всех случаях кроме того, когда стек полностью заполнен, j указывает на ячейку, в которую должна быть выполнена текущаязапись. Номер этой ячейки на единицу больше номера ячейки, из которой надо выполнять очередное считывание.
1) В секцию реализации 2) В основную программу Unit stack; Interface Var elem: byte; {то, что считывается и записывается в стек}
Implementation
begin { Инициализация }
end. Приведем программу, которая использует этот модуль. Она добавляет в стек три числа, потом одно удаляет и выводит на экран:
Program P; uses stack; begin push(1); push(2); push(3); pop(elem); writeln('Считанный элемент = ', elem); end.
Модули и большие программы До сих пор мы говорили о модулях как о библиотеках - наборах полезных подпрограмм, которые могут использоваться несколькими программами. Однако, у модуля есть еще одна функция - разбивать большую программу на составные части. Два аспекта Borland Pascal способствуют использованию модулей в такой функции: · высокая скорость компиляции и компоновки; · способность работать с несколькими файлами одновременно, например, с программой и несколькими модулями. Обычно большая программа разбивается на модули, которые объединяют процедуры по их функциям. Например, программа редактора может быть разделена на части, выполняющие инициализацию, распечатку, чтение и запись файлов, форматирование и так далее. Так же как имеется основная программа, определяющая глобальные константы, типы данных, переменные, процедуры и функции, так же может иметь место и " глобальный" модуль, который используется всеми другими модулями. Набросок большой программы-редактора может иметь вид: program Edit; uses WinCrt, Strings, { стандартные модули из TPW.TPL } Edit_Globals, { модули, написанные пользователем } Edit_Init, Edit_Print, Edit_Read, Edit_Write, Edit_Format; { описание процедур и функций программы } begin { основная программа } end. { конец программы Edit } Модули в данной программе могут содержаться в TPW.TPL, библиотеке исполняющей системы Windows, или быть отдельными файлами.TPW. В последнем случае Borland Pascal выполняет за вас управление проектом. Это означает, что при перекомпиляции программы Edit с помощью встроенного в компилятор средства формирования Borland Pascal сравнивает даты каждого файла.PAS и.TPW и перекомпилирует любой модуль, исходный код которого перекомпилирован. Другая причина использования модулей в больших программах состоит в ограничения кодового сегмента. Процессоры 8086 (и родственные им) ограничивают размер сегмента кода 64 килобайтами. Это означает, что основная программа и любой сегмент данных не может превышать 64К. Borland Pascal интерпретирует это, создавая для каждого модуляотдельный сегмент кода. Без этого объем кода вашей программы не мог бы превышать 64К. Популярное:
|
Последнее изменение этой страницы: 2016-07-12; Просмотров: 551; Нарушение авторского права страницы