Программирование на языке Pascal

         

Crt


Модуль Crt служит для организации "хорошего" вывода на экран. Подробнее о содержимом этого модуля мы расскажем в следующей лекции.



Dos


Модуль Dos позволяет обмениваться информацией с операционной системой. Системное время, прерывания, состояния параметров окружения, процедуры обработки процессов, работа с дисковым пространством - всем этим занимается модуль Dos.



Программирование на языке Pascal


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



Модульность программ


Модуль - это кусок программы, компилируемый отдельно от остальных ее частей. Именно возможность раздельной компиляции и является основным преимуществом модулей.

Простейшая модульность программы может достигаться за счет применения процедур и функций, однако этого не всегда достаточно. Если все подпрограммы содержатся в одном файле, то исправление единственной ошибки в какой-либо подпрограмме приведет к неизбежной перекомпиляции всего кода. А при современных размерах программ компиляция может длиться даже не минуты, а часы.

Кроме того, если коллектив программистов пишет одну большую программу (а именно в таких условиях работают сегодня все производители программного обеспечения), то каждому из них нужно обеспечить более или менее независимый "фронт работ". Даже два человека не могут одновременно исправлять один и тот же файл, иначе конфликт обновлений будет гарантирован. Что уж тут говорить о проектах, над которыми работают десятки и даже сотни человек! В такой ситуации модули, которые хранятся каждый в отдельном файле и могут быть отредактированы, откомпилированы и протестированы независимо от остальных частей программы, являются наилучшим решением этой проблемы.

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



Название


В отличие от заголовка программы (program <имя_программы>;), который может и отсутствовать, заголовок модуля (unit <имя_модуля>;) обязан присутствовать всегда.

Кроме того, очень полезно давать модулям и содержащим их файлам одинаковые имена. Иначе говоря, модуль с именем modul_1 желательно разместить в файле с именем modul_1.pas, и т.п.



Overlay




Модуль Overlay предоставляет возможность делать большие программы оверлейными (многократно использующими одну и ту же область памяти).



Передача аргументов из командной строки


В лекции 8 мы уже говорили о том, что любая процедура или функция, по сути, является полноценной программой. Но если подпрограммы способны получать аргументы извне, то почему этого не могут делать сами программы?

Однако в языке Pascal нет нарушений единообразия: любая программа тоже способна получать аргументы во время начала работы. Для этого нужно лишь вызывать ее из командной строки следующим образом:

<имя_программы> <список_аргументов>

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

В стандартном модуле System, автоматически подключаемом к любой программе на языке Pascal, имеются две функции, позволяющие программе принимать аргументы, переданные из командной строки.

Функция paramcount: word возвращает количество аргументов, переданных в программу из командной строки при вызове.

Функция paramstr(k: word): string возвращает k-й аргумент, переданный в программу из командной строки.

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

Например:

program arguments; var i,k,n: word; begin n:= paramcount; writeln('Из командной строки поступило ',n,' аргументов:'); for i:= 1 to n do writeln(i,'-й аргумент = ',paramstr(i)); end.

Если теперь вызвать эту программу из командной строки, скажем, с такими аргументами:

arguments abcd -36 +25.062 -t /&amp;

то на экране получится следующий результат:

Из командной строки поступило 5 аргументов: 1-й аргумент = abcd 2-й аргумент = -36 3-й аргумент = +25.062 4-й аргумент = -t 5-й аргумент = /&amp;

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

Чаще всего для отображения на экране внешней подсказки используются аргументы ? или /?.

Примером может служить краткая экранная подсказка хорошо известного архиватора arj.exe, которая появляется на экране в случае некорректного обращения к этой программе. Подсказка сообщает пользователю: а) имя программы; б) суть того, что именно появилось на экране; в) правила корректного вызова программы; г) примеры корректного вызова; д) краткий список возможных аргументов (команд и ключей).


ARJ 2.50a SHAREWARE Copyright (c) 1990- 95 ARJ Software. Dec 12 1995 List of frequently used commands and switches. Type ARJ -? for more help. Usage: ARJ <command> [-<sw> [-<sw>...]] <archive_name> [<file_names>...] Examples: ARJ a -e archive, ARJ e archive, ARJ l archive *.doc

<Commands> a: Add files to archive m: Move files to archive d: Delete files from archive t: Test integrity of archive e: Extract files from archive u: Update files to archive f: Freshen files in archive v: Verbosely list contents of archive l: List contents of archive x: eXtract files with full pathname

<Switches> c: skip time-stamp Check r: Recurse subdirectories e: Exclude paths from names s: set archive time-Stamp to newest f: Freshen existing files u: Update files (new and newer) g: Garble with password v: enable multiple Volumes i: with no progress Indicator w: assign Work directory m: with Method 0, 1, 2, 3, 4 x: eXclude selected files n: only New files (not exist) y: assume Yes on all queries

Подключение модулей


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

uses <имя_модуля>;

Если подключаемых модулей несколько, эта строка примет вид:

uses <имя_модуля_1>,...,<имя_модуля_N>;

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

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

Например, вы можете пользоваться функцией abs(), не объявляя и не описывая ее, поскольку эта функция включена в состав стандартного модуля System, автоматически подключаемого к любой программе на языке Pascal. Если же вы захотите очистить экран монитора перед выдачей результатов, вам придется подключить к вашей программе модуль crt и воспользоваться содержащейся в нем процедурой clrscr (см. лекцию 14).



Пример модуля


В качестве примера мы приведем модуль, содержащий арифметические функции min и max для целых чисел, а также функцию возведения в степень. Все эти функции отсутствуют в стандартном модуле System.

unit my_arifm; interface function min(a,b: longint): longint; function max(a,b: longint): longint; function deg(a,b: double): double;

implementation function min; begin if a>b then min:= b else min:= a; end;

function max; begin if a<b then max:= b else max:= a; end;

function deg; begin deg:= exp(b*ln(a)) end; end.

Теперь, подключив этот модуль к любой своей программе, вы сможете пользоваться этими тремя функциями. Остальные необходимые в работе подпрограммы (например, тригонометрические функции tg, ctg, sec, arcsin, arсcos, arctg, arcctg, arcsec) желающие могут добавить в этот модуль самостоятельно.

Аналогичным образом, полезно единожды написать и отладить подпрограммы, обрабатывающие динамические структуры данных (списки, деревья, стеки и т.п.), сохранить их в специальном модуле, а затем пользоваться ранее проделанной работой вновь и вновь, не тратя времени на повторное программирование. Такой подход очень распространен в программировании вообще и носит несколько неуклюжее название "Повторное использование ранее созданных компонентов"1).



Printer


Модуль Printer позволяет производить вывод информации не на консоль, а на принтер (под операционной системой DOS).



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


Секции инициализации всех подключенных к программе модулей исполняются один раз, перед началом работы основной программы:

begin <произвольные_операторы> end.

Если сразу несколько модулей содержат секции инициализации, то порядок выполнения этих секций будет следующим:

Если модуль А подключает модуль В (не важно, в какой именно секции), то секция инициализации модуля В будет выполнена раньше, чем секция инициализации, содержащаяся в модуле А.Если два модуля В и С подключаются на одном уровне (считаются равноправными), то их секции инициализации будут выполнены в том порядке, в каком имена этих модулей указаны в разделе uses.

Замечание: Последовательность подключения модулей соответствует обратному обходу орграфа связей (см. лекцию 12).

Если каждый модуль из тех, что составляют программу на рис. 13.1, имеет непустую секцию инициализации, то эти секции будут выполнены в следующей последовательности: C, D, F, A, B. Если же к головной программе модули будут подключены в другом порядке, например:

uses B,A,C;

то секции инициализации будут выполняться в другой последовательности: F, B, C, D, A.

Замечание: Если секция инициализации в модуле отсутствует (а так чаще всего и бывает), то ключевое слово begin указывать не обязательно. Однако end. обязан закрывать текст модуля в любом случае.



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


Этот раздел модуля содержит реализации всех подпрограмм, которые были объявлены в секции внешних связей. Как и в случае косвенной рекурсии (см. лекцию 9), здесь объявление подпрограммы оторвано от ее описания. Однако ключевое слово forward здесь является излишним.

Кроме того, в этой же секции объявляются и описываются внутренние (невидимые вне модуля) метки, константы, типы данных, переменные и подпрограммы.

implementation [uses <список_вспомогательных_модулей>;] [const <список_внутренних_констант>;] [type <список_внутренних_типов_данных>;] [var <список_внутренних_переменных>;] [procedure <описание_внешней_процедуры>;] [function <описание_внешней_функции>;] [procedure <объявление_и_описание_внутренней_процедуры>;] [function <объявление_и_описание_внутренней_функции>;]

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

unit mod_1; interface ... implementation uses mod_2; ...

unit mod_2; interface ... implementation uses mod_1; ...

Хороший пример реальной рекурсии при обращениях к модулям (позаимствованный, правда, из оригинальной документации) дается в книге М.В. Сергиевского и А.В. Шалашова "Турбо Паскаль 7.0. Язык. Среда программирования". Позволим себе привести (с небольшими изменениями) этот пример.

Смысл рекурсии здесь состоит в том, что вывод некоторого сообщения на заданную позицию экрана при помощи процедуры message, содержащейся в модуле mod_1, может сгенерировать ошибку, сообщение о которой (процедура _error из модуля mod_2) снова задействует процедуру message - но уже с другими параметрами.

unit mod_1; interface procedure message(x,y: byte; msg: string); implementation uses mod_2, crt; procedure message; begin if(x in [1..80-length(msg)]and(y in [1..25]) then begin gotoxy(x,y); {позиционирование курсора} write(msg) end else _error('Сообщение не входит в экран') {вызов процедуры из модуля mod_2} end; end.

unit mod_2; interface procedure _error(msg:string);

implementation uses mod_1; procedure _error; begin message(1,25,msg); {вызов процедуры из модуля mod_1} end; end.



Секция внешних связей


Эта секция содержит объявления тех типов данных, констант, переменных, подпрограмм и т.п., которые должны быть видны вне модуля.

Если для объявления какого-либо объекта нужны сведения об объекте, объявленном в другом модуле, то имя этого модуля необходимо указать в этой же секции:

interface [uses <список_вспомогательных_модулей>;] [const <список_внешних_констант>;] [type <список_внешних_типов_данных>;] [var <список_внешних_переменных>;] [procedure <объявление_внешней_процедуры>;] [function <объявление_внешней_функции>;]

Например, пусть у нас есть два модуля: mod_const, содержащий описания базовых констант и типов данных, и mod1, использующий эти описания (мы приводим только секции внешних связей):

unit mod_const; interface const sto = 100; type one_to_sto = 1..sto; ... unit mod1; interface uses mod_const; const dvesti = 2*sto; type massiv = array[1..dvesti] of byte; var a: massiv; b: one_to_sto; function min(x,y:one_to_sto):one_to_sto; ...


Рис. 13.1.  Пример структуры модульной программы

Теперь, если в каком-либо третьем модуле встретится строка

uses mod1;

то внутри этого третьего модуля можно будет использовать (без дополнительных объявлений) тип massiv, переменные a и b, а также функцию min. Необходимо отметить, что использовать константу sto третий модуль не сможет, поскольку в нем не было указано

uses mod_const;

Если в секциях связей нескольких модулей были определены константы или переменные с одинаковыми именами, но с разными значениями, то путаницы позволит избежать уже знакомый нам прием: указание имени модуля перед идентификатором:

<имя_модуля>.<идентификатор>

Если имя модуля не указано, то идентификатор считается принадлежащим текущему модулю. И только если в текущем модуле этот идентификатор не был объявлен, то начинается поиск в подключенных модулях.

Например, если модульная программа имеет структуру, изображенную на рис. 13.1, то таблица доступности переменных будет выглядеть так:

СвязиСпособ обращения к одноименным переменным
program prg;uses A,B,C;pa.pb.pc.pне виднане видна
unit A;uses C,D,F;не виднаpне виднаc.pd.pf.p
unit B;uses F;не виднане виднаpне виднане виднаf.p
unit C;-не виднане виднане виднаpне виднане видна
unit D;-не виднане виднане виднане виднаpне видна
unit F;-не виднане виднане виднане виднане виднаp



Создание модульной программы


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



Стандартные модули языка Pascal


Перечислим самые распространенные модули, входящие в состав стандартных библиотек языка Pascal. Подробное описание этих библиотек можно найти в любом справочном издании1).



Strings


Модуль Strings позволяет перейти от стандартных строк языка Pascal к строкам, ограниченным нулем. В отличие от обычных строк, чья длина не может превышать 255 символов, эти строки могут состоять из 65 535 символов, причем конец каждой такой строки помечен символом #0.



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


В состав модуля входят четыре секции (любая из них может быть пустой, но ее заголовок все равно обязан присутствовать):

unit <имя_модуля>; interface {секция внешних связей} implementation {секция реализаций} begin {секция инициализации} end.

Разберем каждую из этих секций отдельно.



System


Модуль System является основным: в нем содержатся все изученные нами стандартные процедуры и функции обработки арифметических выражений, множеств, строк и т.п. Специального подключения этот модуль не требует: его содержимым можно пользоваться по умолчанию.

Напомним, что этот модуль содержит следующие типы подпрограмм:

подпрограммы для обработки величин порядковых типов данных (dec, inc, odd, pred, succ);арифметические функции;функции преобразования типов данных (chr, ord, round, trunc);процедуры управления процессом выполнения программы (break, continue, exit, halt);подпрограммы обработки строк (concat, copy, delete, insert, length, pos, str, val);подпрограммы файлового ввода и вывода;подпрограммы динамического распределения памяти (dispose, freemem, getmem, new);функции для работы с указателями и адресами (addr);а также некоторые другие подпрограммы (например, exclude, include, random, randomize, upcase).



Взаимодействие модулей


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


Рис. 13.2.  Использование модуля определений



Wincrt


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



Windos


Модуль WinDos является аналогом модуля Dos для операционной системы Windows.



Winprn


Модуль WinPrn является аналогом модуля Printer для операционной системы Windows.