Вы находитесь на странице: 1из 177

УЧЕБНОЕ ПОСОБИЕ

по дисциплине

Основы алгоритмизации
и программирования

Часть 2

Для студентов первого курса


специальности I-40 01 01
«Программное обеспечение информационных
технологий»

Минск 2006
Автор – Глухова Лилия Александровна, канд.техн.наук, доцент кафедры
«Программное обеспечение информационных технологий» Белорусского
государственного университета информатики и радиоэлектроники.

2
СОДЕРЖАНИЕ
Раздел 1. Подпрограммы ...........................................................................................6
1.1. Общие сведения................................................................................................6
1.2. Процедуры ........................................................................................................7
1.2.1. Описание процедур ...................................................................................7
1.2.2. Вызов процедур .........................................................................................8
1.2.3. Процедуры без параметров ......................................................................9
1.2.4. Процедуры с параметрами......................................................................10
1.2.5. Параметры-значения ...............................................................................11
1.2.6. Параметры-переменные..........................................................................13
1.2.7. Параметры-константы.............................................................................14
1.2.8. Параметры-переменные без типа...........................................................14
1.2.9. Параметры процедурного типа ..............................................................20
1.2.10. Использование производных типов в качестве параметров
подпрограмм .........................................................................................................25
1.2.11. Принцип локализации имен ...................................................................26
1.3. Функции ..........................................................................................................27
1.3.1. Описание функций ..................................................................................27
1.3.2. Вызов функции ........................................................................................29
1.4. Рекурсивные подпрограммы .........................................................................31
1.5. Директивы .......................................................................................................33
1.6. Библиотечные модули пользователя............................................................36
1.6.1. Общие сведения.......................................................................................36
1.6.2. Структура модуля Unit............................................................................37
1.6.3. Особенности работы с модулями ..........................................................45
1.6.4. Подключение к программе внешнего файла ........................................47
Раздел 2. Простейший ввод-вывод.........................................................................49
2.1. Процедуры ввода из стандартного текстового файла Input.......................49
2.2. Процедуры вывода в стандартный текстовый файл Output.......................51
Раздел 3. Записи .......................................................................................................55
3.1. Структура записи ...........................................................................................55
3.2. Записи без вариантной части ........................................................................57
3.3. Записи с вариантами ......................................................................................60
3.4. Оператор присоединения With .....................................................................63
3.5. Константа-запись............................................................................................66
Раздел 4. Множества................................................................................................69
4.1. Общие сведения..............................................................................................69
4.2. Конструктор множества ................................................................................70
4.3. Задание множественного типа ......................................................................71
4.4. Операции над множествами..........................................................................72
4.5. Ввод / вывод значения множественной переменной..................................76

3
4.6. Типизованные константы-множества ..........................................................77
Раздел 5. Файлы .......................................................................................................79
5.1. Общие сведения..............................................................................................79
5.2. Процедура Assign ...........................................................................................81
5.3. Файлы с типом................................................................................................83
5.4. Текстовые файлы............................................................................................90
5.5. Сравнительная характеристика представления информации в файлах с
типом и текстовых файлах .....................................................................................99
5.6. Файлы без типа .............................................................................................103
5.7. Проверка операций ввода-вывода ..............................................................106
Раздел 6. Ссылочный тип (тип указатель)..........................................................108
6.1. Общие сведения............................................................................................108
6.2. Методы работы с динамическими переменными .....................................111
6.2.1. Процедуры New и Dispose....................................................................111
6.2.2. Процедуры Getmem и Freemem ...........................................................119
6.2.3. Процедуры Mark и Release ...................................................................121
Раздел 7. Динамические структуры данных .......................................................124
7.1. Динамические цепочки................................................................................124
7.1.1. Структура динамической цепочки ......................................................124
7.1.2. Формирование цепочки ........................................................................126
7.1.3. Поиск элемента в цепочке ....................................................................128
7.1.4. Удаление элемента из цепочки ............................................................129
7.1.5. Вставка элемента в цепочку .................................................................131
7.1.6. Линейный однонаправленный список ................................................132
7.2. Двунаправленные списки ............................................................................133
7.2.1. Вставка элемента ...................................................................................136
7.2.2. Создание двунаправленного кольцевого списка с заглавным звеном
138
7.2.3. Удаление элемента ................................................................................139
7.2.4. Поиск элемента ......................................................................................141
7.3. Очереди и стеки............................................................................................142
7.3.1. Очередь LIFO .........................................................................................143
7.3.2. Очередь FIFO .........................................................................................146
7.4. Таблицы.........................................................................................................149
7.4.1. Общие сведения.....................................................................................149
7.4.2. Способы организации таблиц ..............................................................150
7.5. Двоичные деревья ........................................................................................154
7.5.1. Структура двоичного дерева ................................................................154
7.5.2. Построение дерева.................................................................................156
7.5.3. Поиск записи в дереве...........................................................................159
7.5.4. Включение записи в дерево..................................................................160
7.5.5. Удаление записи из дерева ...................................................................162
Раздел 8. Оверлеи...................................................................................................169

4
8.1. Общие сведения............................................................................................169
8.2. Правила оформления оверлейных программ ............................................169
8.3. Инициализация работы оверлеев................................................................171
8.3.1. Включение администратора оверлеев.................................................171
8.3.2. Обработка ошибок администратора ....................................................172
8.3.3. Размещение оверлейного файла в EMS-памяти................................174
8.3.4. Управление оверлейным буфером.......................................................175

5
РАЗДЕЛ 1. ПОДПРОГРАММЫ

1.1. Общие сведения

В программировании часто возникает необходимость в повторении


отдельных участков вычислений несколько раз в различных точках программы
при разных исходных данных. Чтобы не повторять одни и те же операторы
несколько раз, используются подпрограммы.
Подпрограмма – поименованная логически законченная группа
операторов языка, которую можно вызвать для выполнения по имени любое
количество раз из различных мест программы.
В общем случае программный модуль состоит из заголовка и тела
модуля. Тело модуля называют еще блоком. Блок состоит из раздела описаний
и раздела операторов.
Блок может содержать в себе другие блоки. Блок, который не входит ни в
какой другой блок, называется глобальным. Блоки, входящие в состав других
блоков, называются локальными или вложенными.
Глобальный блок – это основная программа (программный модуль).
Локальные блоки – это подпрограммы.
В соответствии с этим все элементы программы (типы, переменные,
константы и т.д.) подразделяются на глобальные и локальные.
Объявление называется локальным для подпрограммы, если оно
содержится в данной подпрограмме. Объявление называется глобальным, если
оно содержится в объемлющей подпрограмме или модуле.
С каждым объявлением имени (идентификатора) объекта программы
связана область его действия. Вне своей области действия имя не существует.
Областью действия имени является часть модуля или блока от точки
объявления и до конца текущего уровня вложенности, включая вложенные
блоки, за исключением тех вложенных подпрограмм, в которых имеются
другие объявления этого же имени. Не допускаются повторные объявления
имени на одном уровне вложенности.
Считается, что хороший стиль программирования предполагает
минимальное использование глобальных переменных.
В идеальном случае программа на языке Паскаль состоит только из
подпрограмм, которые вызываются для выполнения из раздела операторов
основной программы.
Существуют два вида подпрограмм – процедуры и функции.
Все процедуры и функции подразделяются на две группы – встроенные и
определенные пользователем.

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

1.2. Процедуры

1.2.1. Описание процедур

Процедура должна быть описана в разделе объявления процедур и


функций программы. Формат ее объявления представляет рисунок 1.1.

<Объявление_процедуры> ::=

<Заголовок_процедуры> ;

<Тело_процедуры> ;

Рисунок 1.1 – Синтаксическая диаграмма


объявления процедуры

Процедура состоит из заголовка и тела.


Заголовок процедуры определяет ее имя и список формальных
параметров. Его формат иллюстрирует рисунок 1.2.
Формальные параметры определяют тип данных, передаваемых
процедуре при ее вызове, и способ передачи данных. Список формальных
параметров имеет формат, который представляет рисунок 1.3.
Тело процедуры состоит из объявлений локальных для нее
идентификаторов и составного оператора, описывающего действия процедуры.

7
<Заголовок_процедуры> ::=

Procedure <Идентификатор>

<Список_формальных_параметров>

Рисунок 1.2 – Синтаксическая диаграмма


объявления заголовка процедуры

<Список_формальных_параметров>::=

( <Формальный_параметр> )

Рисунок 1.3 – Синтаксическая диаграмма


списка формальных параметров

1.2.2. Вызов процедур

Вызов процедуры осуществляется с помощью оператора вызова


процедуры, имеющего формат, который представляет рисунок 1.4.
Оператор вызывает процедуру с указанным именем и передает ей
фактические параметры (аргументы), если они заданы.
В качестве фактических параметров могут быть использованы выражения
типа, совместимого с типом формальных параметров.
Количество аргументов должно совпадать с количеством формальных
параметров. При этом первому формальному параметру соответствует первый
аргумент, второму – второй и т.д.

8
<Вызов_процедуры >::=

<Идентификатор_процедуры>
( <Аргумент> )

Рисунок 1.4 – Синтаксическая диаграмма


оператора вызова процедуры

Выполнение процедуры прекращается при достижении конца составного


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

1.2.3. Процедуры без параметров

Если в операторе вызова процедуры и в заголовке процедуры нет


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

Пример 1.1.
Вычисление расстояний между N парами точек на плоскости.
Использование процедуры без параметров.

Program R1;
Var
X1, Y1, X2, Y2, D: Real; {Объявление глобальных переменных}
N, I: Integer; {Объявление глобальных переменных}
{Процедура без параметров}
Procedure Rast1;
Begin

9
D:=Sqrt (Sqr (X1 - X2) + Sqr (Y1 - Y2)) {D – расстояние между точками;
X1, Y1, X2, Y2 – координаты первой и второй точек}
End;
{Вызывающая программа}
Begin
Writeln (‘Введите количество пар точек’);
Readln (N);
For I:=1 To N Do
Begin
Writeln (‘Введите координаты первой точки в виде: абсцисса
ордината’);
Readln (X1,Y1);
Writeln (‘Введите координаты второй точки в виде: абсцисса
ордината’);
Readln (X2, Y2);
Rast1; {Оператор вызова процедуры Rast1}
Writeln (D);
End
End.

В данном примере связь между вызывающей программой и процедурой


Past1 осуществляется через глобальные переменные X1, X2, Y1, Y2, D.

Недостаток процедур без параметров – жесткая фиксация исходных


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

1.2.4. Процедуры с параметрами

Использование параметров повышает удобство использования процедур,


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

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

<Формальный_параметр>::=

<Идентификатор>
: <Тип_параметра>
Var

Const

Рисунок 1.5 − Синтаксическая диаграмма


задания формальных параметров

В языке Паскаль существует пять типов параметров:


1) параметры-значения;
2) параметры-переменные;
3) параметры-константы;
4) параметры без типа;
5) параметры процедурного типа.

1.2.5. Параметры-значения

Параметры-значения – это группа параметров, перед которыми не


используется зарезервированное слово Var или Const и за которыми следует
тип. Язык не допускает параметры-значения файловых типов.
Для каждого параметра-значения при вызове подпрограммы создается
локальная переменная. Эти переменные существуют только во время
выполнения подпрограммы, после чего они прекращают свое существование.
При входе в подпрограмму этим переменным присваиваются значения
соответствующих фактических параметров вызова подпрограммы. Данные
значения локальных переменных используются при выполнении
подпрограммы.
В качестве фактического параметра может использоваться любое
выражение, тип которого совместим по присваиванию с типом параметра-
значения.
При применении параметров-значений фактические параметры
используются только при входе в подпрограмму, с целью передачи задаваемых
ими значений соответствующим локальным переменным. После этого

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

Пример 1.2.
Вычисление расстояний между N парами точек на плоскости.
Использование процедуры с параметрами-значениями.

Program R2;
Var
X1, Y1, X2, Y2, D: Real; {Объявление глобальных переменных}
N, I: Integer; {Объявление глобальных переменных}
{Процедура с параметрами-значениями}
Procedure Rast2 (Xx1, Xx2, Yy1, Yy2: Real); {Xx1, Xx2, Yy1, Yy2 –
параметры-значения типа Real}
Begin
D := Sqrt (Sqr (Xx1 - Xx2) + Sqr (Yy1 - Yy2)) {D – глобальная
переменная}
End;
{Вызывающая программа}
Begin
Writeln (‘Введите количество пар точек’);
Readln (N);
For I:=1 To N Do
Begin
Readln (X1, X2, Y1, Y2);
Rast2 (X1, X2, Y1, Y2); {Оператор вызова процедуры Rast2}
Writeln (D);
End
End.

Данная программа выполняет те же вычисления, что и предыдущая (см.


пример 1). Однако для вычисления расстояния между точками используется
процедура Rast2 с параметрами-значениями Xx1, Xx2, Yy1, Yy2. Для
представления результата работы процедуры применена глобальная переменная

12
D, поскольку данный результат должен быть передан в вызывающую
программу.

1.2.6. Параметры-переменные

Параметры-переменные – это группа параметров, перед которой стоит


зарезервированное слово Var и за которой следует тип. Фактическим
параметром может быть только переменная (но не константа и не выражение)
того же типа, что и тип формального параметра. В подпрограмму передается
адрес этой переменной. Поэтому подпрограмма может непосредственно
использовать и изменять значение этой переменной и передавать в основную
программу вырабатываемые ею результаты.
Таким образом, изменение формального параметра-переменной приводит
к изменению соответствующего фактического параметра.

Пример 1.3.
Вычисление расстояний между N парами точек на плоскости.
Использование процедуры с параметрами-переменными.

Program R3;
Var
X1, X2, Y1, Y2, D: Real; {Объявление глобальных переменных}
I: Interger;
Const
N=10; {Количество пар точек на плоскости}
{Процедура с параметрами-значениями и параметрами-переменными}
Procedure Rast3 (Xx1, Xx2, Yy1, Yy2: Real; Var Dd: Real);
{Xx1, Xx2, Yy1, Yy2 – параметры-значения типа Real;
Dd – параметр-переменная}
Begin
Dd := Sqrt (Sqr (Xx1 - Xx2) + Sqr (Yy1 - Yy2))
End;
{Вызывающая программа}
Begin
For I:=1 To N Do
Begin
Readln (X1, X2, Y1, Y2);
Rast3 (X1, X2, Y1, Y2, D); {Оператор вызова процедуры Rast3}
Writeln (D)
End

13
End.

Данная программа отличается от предыдущей тем, что в ней для


представления результата работы процедуры используется параметр-
переменная Dd.

1.2.7. Параметры-константы

Параметры-константы - это группа параметров, перед которой стоит


зарезервированное слово Const и за которой следует тип. При их использовании
в подпрограмму передается адрес фактического параметра.
Язык запрещает изменять значения параметров-констант в теле
подпрограммы. В противном случае возникает сообщение об ошибке (в
отличие от параметров-значений, изменение которых в теле подпрограммы
возможно, но передать их в вызывающую программу нельзя). Связано это с
тем, что при входе в подпрограмму локальные переменные, соответствующие
параметрам-константам, не создаются.
Поэтому подпрограммы, использующие параметры-константы,
выполняются значительно быстрее, чем использующие параметры-значения.
Таким образом, в тех подпрограммах, в которых отсутствует необходимость в
изменении значений параметров, предпочтение следует отдавать параметрам-
константам перед параметрами-значениями.
Например, поскольку в примере 1.3 в теле процедуры значения ее
формальных параметров не изменяются, то заголовок процедуры может быть
описан так:
Procedure Rast4 (Const Xx1, Xx2, Yy1, Yy2: Real; Var Dd: Real);

1.2.8. Параметры-переменные без типа

Параметры-переменные без типа — это группа параметров, перед


которыми стоит ключевое слово Var и за которыми не следует тип.
Фактическими параметрами при вызове подпрограммы в данном случае могут
быть переменные любого типа.
Поскольку у нетипизованных параметров-переменных тип отсутствует,
то изначально они несовместимы с переменными всех типов.
Обеспечение совместимости с фактическими параметрами может быть
достигнуто одним из двух способов.

14
СПОСОБ 1.
Внутри подпрограммы объявляется локальная переменная нужного типа,
налагаемая на переданный фактический параметр. Для описания такой
переменной используется зарезервированное слово Absolute.
Синтаксическая диаграмма описания налагаемой переменной имеет вид,
который представляет рисунок 1.6.
Этот способ применения нетипизованных параметров-переменных
иллюстрирует пример 1.4.

<Описание налагаемой переменной>::=

<Имя_переменной> : <Тип_переменной>

Absolute <Имя_параметра_без_типа>

Рисунок 1.6 - Синтаксическая диаграмма


описания налагаемой переменной

Пример 1.4.

Использование процедуры с параметрами без типа (1-й способ).


Программа полного и частичного сравнения значений элементов массивов и
записей.
Program Ravno1;
Type
Vector = Array [1..6] Of Integer;
Point = Record
Х, У: Integer
End;
Var {Объявление глобальных переменных}
Vec1, Vес2: Vector; {Vec1, Vec2 — массивы из 6 целых чисел}
N, I: Integer;
Р: Point; {Р - запись из двух целых полей Х и У}
Rеz: Boolean; {Rez — глобальная переменная, куда заносится
результат выполнения процедуры Equal1}
15
{Процедура Equal1 сравнивает любые две переменные Source и Dest любого
размера Size}
Procedure Equal1 (Vаr Source, Dest; Size: Word; Var Rez: Boolean);
{Sourсе, Dest – сравниваемые переменные
(параметры-переменные без типа); Size – размер
полей, занимаемых сравниваемыми переменными
в памяти машины; Rez – результат выполнения
процедуры Equal1}
Var
N: Integer; {N — локальная переменная}
S: Array [0 .. Maxint] Of Byte Absolute Source;
{На переданный параметр Source наложен массив байтов S}
D: Array [0 .. Maxint] Of Bуtе Аbsolute Dest;
{На переданный параметр Dest наложен массив байтов D}
Begin
N := 0;
While (N < Size) And (S [N] = D [N]) Do Inc (N);
{Встроенная функция Inc (N) увеличивает значение своего
аргумента на единицу. Наращивание производится в
пределах размеров сравниваемых переменных в байтах (N <
Size) дo тех пор, пока N-е байты массивов D и S равны}
Rez := N = Size;
{Rez равен True, если значения D и S в пределах длины Size
равны (N достигло значения Size), в противном случае Rеz
равен False}
End; {Конец процедуры Equal1}
Begin {Вызывающая программа}
Writeln (‘Введите значения переменных в последовательности:’);
Writeln (‘Vес1 [1] Vec1 [2] ... Vес1 [6]’);
Writeln (‘Vec2 [1] Vec2[2] ... Vec2 [6]’);
Fог I:= 1 Tо 6 Do Read (Vec1 [I]); {Ввод массива Vec1}
Read1n;
For I:=1 Tо 6 Dо Rеаd (Vес2 [I]); {Ввод массива Vеc2}
Readln;
Equal1 (Vec1, Vес2, Sizeof (Vector), Rez);
{Первый оператор вызова процедуры — выполняется полное
сравнение массивов Vec1 и Vес2 по всей длине
Sizeof' (Vector); стандартная функция Sizeof возвращает
размер памяти в байтах, занимаемый своим аргументом}
Writeln (‘Результат полного сравнения массивов = ‘, Rez); {Печать Rez}
Writeln (‘Введите количество N сравниваемых элементов массивов’);
Rеаdln (N); {Ввод N }
Equal1 (Vес1, Vес2, Sizeof( Integer) * N, Rez);

16
{Второй оператор вызова процедуры — выполняется
побайтное сравнение первых N элементов типа Integer
массивов Vеc1 и Vеc2}
Writeln (‘Результат сравнения ‘, N, ‘ элементов =‘, Rez); {Печать Rez}
Equal1 (Vec1, Vес2 [3], Sizeof (Integer) * 2, Rez);
{Третий оператор вызова процедуры — выполняется
побайтное сравнение первых двух элементов типа Integer
массива Vec1 с 3-м и 4-м элементами массива Vec2}
Writeln (‘Результат сравнения 1-го и 2-ro элементов Vec1 с 3-м и 4-м ’ +
‘элементами Vес2 =’, Rez); {Печать Rez}
Writeln ('Введите значения полей Х и У записи Р в виде: Х У’);
Readln (P.Х, Р.У);
Equal1 (Vес1 [5], Р, 4, Rez);.
{Четвертый оператор вызова процедуры — выполняется
побайтное сравнение 5-ro и 6-го элементов массива Vес1 с
полями записи Р.Х и Р.У}
Writeln (‘Результат сравнения 5-го и 6-ro элементов массива Vec1 с ’ +
‘полями записи Р.Х и Р.У ’, Rez); {Печать Rez }
End.

Данная программа иллюстрирует возможности сравнения значений


произвольных типов на примере сравнения массивов Vec1 и Vес2, состоящих из
шести целых чисел, между собой и с записью Р, состоящей из двух полей
целого типа Х и Y. Массивы Vес1 и Vес2 сравниваются как в полном объеме,
так и по отдельным частям.
Для организации сравнения переменных любого типа использована
процедура Equal1 с четырьмя параметрами — Source, Dest, Size, Rez.
Процедура выполняет сравнение переменных Source и Dest. Размер
памяти, занимаемый этими переменными, определяется параметром-значением
Size типа Word. Логический результат работы процедуры присваивается
параметру-переменной Rez типа Boolean.
Переменные Source и Dеst оформлены в виде параметров-переменных без
типа. Для обеспечения их совместимости с фактическими параметрами любого
типа в теле процедуры Equal1 введены локальные переменные S и D типа
массив (состоящий из Maxint+1 байта), наложенные на параметры Source и Dest
соответственно (Maxint — это встроенная в язык константа, равная
максимальному числу типа Integer, представимому в компьютере). При вызове
процедуры переменные S и D накладываются на конкретную область памяти,
занимаемую фактическими переменными, передаваемыми в качестве значений
Source и Dest. Таким образом, фактические переменные, независимо от их
действительного типа, будут рассматриваться как массивы байтов. Конкретное
количество байтов в массиве при работе процедуры ограничивается величиной
Size, передаваемой на вход процедуры в качестве параметра-значения.

17
Обратите внимание на то, что в данном примере использованы две
различные переменные с одним и тем же именем N. Глобальная переменная N
типа Integer введена в разделе Var вызывающей программы и представляет
собой количество элементов входных массивов Vес1, Vec2, в пределах которых
мы хотим сравнить данные массивы. Область действия глобальной переменной
N — все тело программы, начиная от точки ее определения, за исключением
тела процедуры Equal1, поскольку в данной процедуре введена локальная
переменная типа Integer под тем жe именем N. Областью действия локальной
переменной N является тело процедуры Equal1. При выходе из процедуры
локальная переменная N прекращает свое существование, ее значение теряется.
В теле процедуры Equal1 последовательно сравниваются на равенство
соответствующие элементы массивов S и D. При этом фактически
сравниваются соответствующие байты параметров без типа Source и Dest. При
их равенстве значение локальной переменной N увеличивается на единицу.
Если в результате последовательного сравнения соответствующих
элементов массивов S и D окажется, что в пределах поля Size S = D,
параметру-переменной Rez будет присвоено значение True, в противном случае
- False.
В вызывающей программе выполняется четыре вызова процедуры Equal1.
При первом вызове процедуры Equal1 (Vес1, Vес2, Sizeof (Vесtог), Rez)
осуществляется сравнение массивов Vec1 и Vec2 в пределах размера памяти,
определяемого значением встроенной функции Sizeof (для аргумента Vector
значение функции Sizeof равно 12 байт — напомним, что переменная типа
Integer занимает поле памяти, равное двум байтам). Таким образом,
выполняется полное сравнение массивов Vec1 и Vес2.
При втором вызове процедуры Equal1 (Vес1, Vec2, Sizeof (Integer) * N,
Rez) выполняется сравнение первых N элементов типа Integer массивов Vес1 и
Vec2.
При третьем вызове процедуры Equal1 (Vec1, Vec2 [3], Sizeof (Integer) *2,
Rez) сравниваются первые два элемента массива Vес1 с третьим и четвертым
элементами массива Vес2.
При четвертом вызове процедуры Equal1 (Vec1[5], P, 4, Rez) размер
сравниваемых полей памяти ограничен четырьмя байтами. Поэтому
сравниваются пятый и шестой элементы массива Vес1 с полями записи Р.Х и
Р.У соответственно.
Помимо двух переменных с одним именем N в данной программе
применены две переменные с именем Rеz. Переменная Rez, используемая в
качестве фактического параметра в операторах вызова процедуры Equal1,
является глобальной переменной. Она объявлена в разделе объявления
глобальных переменных вызывающей программы и предназначена для
занесения результата работы процедуры Equal1. Областью действия данной
переменной является вся программа от точки ее объявления, исключая

18
процедуру Equal1, где такое же имя использовано в качестве параметра-
переменной. Областью действия параметра Rez является тело процедуры.

СПОСОБ 2.
Для обеспечения совместимости с фактическими параметрами внутри
процедуры вводится нужный тип. Данный тип ставится в соответствие
параметру-переменной без типа с помощью присваивания типа переменной.
Этот способ применения нетипизованных параметров-переменных
иллюстрирует пример 1.5.

Пример 1.5.
Использование процедуры с параметрами без типа (2-й способ).
Программа полного и частичного сравнения значений элементов массивов и
записей.

Program Ravno2;
Type
Vector = Array [1 .. 6] Of Integer;
Point = Record
Х,У: Integer;
End;
Var {Объявление глобальных переменных}
Vеc1, Vec2: Vector; {Vес1, Vec2- массивы из 6 целых чисел}
N, I: Integer;
Р: Point; {Р — запись из двух целых полей Х и Y}
Rez: Boolean; {Rez — глобальная переменная, куда заносится
результат выполнения процедуры Equal}
{Процедура Equal сравнивает любые две переменные Source и Dest
любого размера Size}
Procedure Equal (Var Source, Dest; Size: Word; Var Rez: Boolean);
{Source, Dest - сравниваемые переменные (параметры-
переменные без типа); Size — размер полей, занимаемых
сравниваемыми переменными в памяти машины; Rez –
результат выполнении процедуры Equal}
Type
Bytes = Аггау[0 .. Maxint] Of Byte;
{Тип массив байтов вводится для последующего
присваивания параметрам без типа Source и Dest}
Var

19
N: Integer; {N - локальная переменная}
Begin
N:= 0;
While (N < Size) And (Bytes (Dest) [N] = Bytes (Source) [N]) Do Inc (N);
{Наращивание N производится в пределах размеров
сравниваемых переменных в байтах (N<Size) до тех пор,
пока N-е байты переменных Dest и Source, представленных в
виде массива байтов, равны}
Rеz := N = Size;
{Rez равен True, если значения Dest и Source в пределах
длины Size равны (N достигло значения Size), в прoтивнoм
случае Rez равен Fаlse}
End; {Конец процедуры Equal1}
Begin {Вызывающая программа}

В данном примере приведен фрагмент программы, выполняющей те же


действия, что и в предыдущем примере. Процедура Equal по своему
назначению аналогична процедуре Equal1. Однако совместимость параметров
без типа Source и Dest с фактическими параметрами достигнута иным
способом. В теле процедуры введен тип Bytes, представляющий собой массив,
состоящий из Maxint+1 байта. Данный тип присваивается параметрам без типа
Source и Dest с помощью присваивания типа:

Bytes (Source)
Bytes (Dеst).

в заголовке оператора While процедуры.


Вызывающая программа в данном примере аналогична вызывающей
программе примера 1.4.

1.2.9. Параметры процедурного типа

Параметры процедурного типа - это группа параметров, перед


которыми не используется зарезервированное слово Var или Const и за
которыми следует процедурный тип.
Паскаль позволяет организовывать подпрограммы, использующие в
качестве параметра другие процедуры или функции. С этой целью
применяются процедурные типы.

20
Описание процедурного типа вводится в разделе Туре. Синтаксис
описания совпадает с заголовком подпрограммы (процедуры или функции),
однако имя подпрограммы в описании опускается.
Например:

Pr = Procedure (Х: Integer; Var Y: Real);


Pr1 = Procedure (А, В: String; Р: Pr);

Здесь объявлены два процедурных типа — Pr, Pr1. В типе Pr1 в качестве
параметра используется переменная Р процедурного типа Pr.
Имена параметров в описании процедурного типа играют чисто
иллюстративную роль. Никакого влияния на это описание данные имена не
оказывают.
Процедурные типы допускается использовать в любом контексте. В
частности, могут быть введены переменные этого типа.
Например, с учетом вышеприведенных описаний типа вводятся две
переменные P и P1 процедурного типа:

Var
Р: Pr;
P1: Pr1;

Процедурной переменной может быть присвоено значение другой


процедурной переменной, имя процедуры или функции. При этом переменная
слева и значение в правой части оператора присваивания должны быть
совместимы по присваиванию. Для обеспечения такой совместимости
подпрограмма, если ее имя нужно присвоить процедурной переменной, должна
удовлетворять следующим требованиям:
1) она должна компилироваться в состоянии {$f+} (дальний вызов,
переход между сегментами памяти); это значит, что в исходном тексте
программы перед описанием такой подпрограммы должна быть
размещена директива компилятора {$f+}, а после окончания описания
– {$f-};
2) она не должна быть стандартной процедурой или функцией;
3) она не должна быть вложенной;
4) она не должна быть подпрограммой типа Inline;
5) она не должна быть подпрограммой прерывания (Interrupt);
6) ее заголовок долден соответствовать заголовку процедурной
переменной.
На физическом уровне при присваивании процедурной переменной
имени подпрограммы в данную переменную заносится адрес подпрограммы.
Поэтому процедурная переменная аналогична указателям, т. к. она содержит
ссылку на процедуру (или функцию).

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

Пример 1.6.
Использование процедуры с параметрами процедурного типа. Программа
вычисления значений натуральных логарифмов (Ln) или экспонент e X (Ехр)
элементов одномерного массива. Процедура Log вычисляет логарифмы
элементов массива. Процедура Step вычисляет экспоненты элементов массива.
Процедура Vizov вызывает нужную процедуру Log или Step и выводит
результаты их выполнения.

Program Proced;
Const
Razmer = 4;
Type
Mas = Array [1 .. Razmer] Of Real;
Proc = Procedure (Х: Mas; Var Х1: Mas); {Объявление процедурного
типа}
Var
Vec1: Mаs; {Vec1 - массив из четырех вещественных чисел}
Priznak, N, I: Integer; {Priznak — признак выбора нужной процедуры Log
или Sтер}
{$f+} {Директива компилятора включения дальнего
вызова в памяти}
{Процедура вычисления Ln элементов одномерного массива}
Procedure Log (У: Mas; Var У1: Mas);
Var
J: Integer; {J — локальная переменная}
Begin
Writeln (' Вызвана процедура Log. Значения Ln (Vec1[J]): ');
For J := 1 To Razmer Dо
Y1[J]:= Ln (Y[J]);
End;
22
{Процедура вычисления Ехр элементов одномерного массива}
Procedure Step (У: Mas; Var У1: Mas);
Var
N: Integer; {N — локальная переменная}
Begin
Writeln (' Вызвaна процедура Step. Значения ЕХР (Vec1[J]): ');
For N := 1 To Razmer Do
У1[N] := Ехр (Y[N]);
End;
{$f-} {Директива компилятора выключения дальнего
вызова в памяти}
{Процедура вызова вычисляющих процедур и вывода результатов их
работы}
Procedure Vizov (G: Mas; Proced: Рrос); {Proced — параметр
процедурного типа}
Var
N: 1 .. Razmer; {N, Rez — локальные переменные }
Rez: Mas;
Begin
Proced (G , Rez); {Вызов вычисляющей процедуры}
For N := 1 To Razmer Do Write (Rez [N]); {Вывод результатов работы
вызванной процедуры}
Writeln;
End;
Begin {Вызывающая программа}
Writeln(' Введите значения элементов массива в последовательности:');
Writeln ('Vес1[1] Vecl[2] ... Vec1[', Razmer, ']');
For I := 1 To Razmer Dо
Read (Vec1[I]); {Ввод массива Vec1}
Readln;
Writeln ( 'Введите признак вида вычислений: ');
Writeln (' 0 — для вычисления Ln(Vес1), 1 — для вычисления Exp(Vec1)');
Rеаdln (Priznak); {Ввод признака вида вычислений}
If Priznak = 0 Then
Vizov (Vес1, Log) {Вызов процедуры Vizov. Фактические
параметры - массив Vec1 и имя процедуры Log}
Else
If Priznak = 1 Then
Vizov (Vес1, Step) {Вызов процедуры Vizov. Фактические
параметры – массив Vec1 и имя процедуры Step}
Else
Writeln ('Ошибка признака'); {Неверно указано значение признака

23
вычислений}
End.

Программа, приведенная в данном примере, выполняет обработку


элементов одномерного массива Vec1 типа Mas, состоящего из четырех
вещественных чисел. В зависимости от значения переменной Priznak
вычисляются натуральные логарифмы или экспоненты элементов массива.
Результаты вычислений выводятся на печать.
Данная программа содержит три процедуры - Log, Step, Vizov.
Процедура Log вычисляет значения натуральных логарифмов элементов
массива Vec1, передаваемого на вход процедуры с помощью параметра-
значения Y типа Mas. Результаты работы процедуры присваиваются
параметру-переменной Y1 типа Mas.
Процедура Step вычисляет значение экспоненты элементов массива
Vec1. Для передачи исходного массива на вход процедуры также
используется параметр-значение Y типа Mas, а для формирования и
хранения ее результатов - параметр-переменная Y1 того же типа.
Процедура Vizov служит для вызова необходимой вычисляющей
процедуры и вывода результатов вычислений. Параметрами процедуры
Vizov являются параметр-значение G типа Mas и параметр Proced
процедурного типа Ргос

Ргос = Procedure (Х: Mas; Var Xl: Mas);

объявленного в разделе Type вызывающей программы.


В вызывающей программе вводятся значения четырех элементов
массива Vec1 и значение Priznak признака вида вычислений (логарифм или
экспонента).
Затем вызывается процедура Vizov. В качестве первого фактического
параметра при этом используется Vec1. Вторым фактическим параметром в
зависимости от значения переменной Priznak является имя процедуры Log
или Step.
Так как процедуры Log, Step передаются в качестве фактического
параметра, то они должны компилироваться с директивой компилятора
{$f+} (данная директива записана перед первой из вышеназванных процедур
- Log). Для отмены данной директивы после второй из процедур (Step)
помещена директива компилятора {$f-}.
Таким образом, при вызове процедуры Vizov используется оператор
вызова вычисляющей процедуры

Proced (G, Rez);

24
В зависимости от конкретного значения процедурной переменной
Proced будет вызвана к исполнению либо процедура Log, либо процедура
Step. Результаты работы вызванной процедуры помещаются в массив Rez,
являющийся локальной переменной процедуры Vizov, и выводятся на печать.

1.2.10. Использование производных типов


в качестве параметров подпрограмм

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


подпрограмм. Они могут применяться как параметры-переменные, параметры-
значения или параметры-константы. Во всех случаях фактическими
параметрами могут быть только переменные соответствующего типа, но не
выражения (за исключением строковых констант).
В заголовке подпрограмм для указания типов формальных параметров
могут использоваться только имена типов, но не их задание.
Например, нельзя написать:

Procedure X (Y: Array [1..10] Of Integer; I: 1..100);

Нужно предварительно определить типы:

Type
Mas = Array [1..10] Of Integer;
Diap = 1..10;
Procedure X (Y: Mas; I: Diap);

При использовании массива в качестве параметра-значения при входе в


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

25
1.2.11. Принцип локализации имен

Трудоемкость разработки многих реальных программ составляет десятки


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

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

Области действия имен


Program Q;
Var Q, Y CГ, XГ
Y: Real;
X: Char;
Const
C=10;
Procedure Al (X, Z: Real);
Var CЛ, XЛ, Z
C: Real;
Begin
C := X + Z;
X := 2 * X;
Y := 1;
Writeln (‘C = ‘, C, ‘ X = ‘, X, ‘Y = ‘, Y)
End;
Begin
X := ’A’; CГ, XГ
Y := 0.5;
Al (Y, 0.1);
Writeln (‘C = ‘, C, ‘ X = ‘, X, ‘Y = ‘, Y)
End.

26
В данной программе используются идентификаторы Q, Y, C, X, Z, причем
идентификаторы C, X используются как локальные и как глобальные
переменные. Справа изображены области действия используемых имен. Здесь
через CГ, XГ обозначены глобальные переменные C, X, через CЛ, XЛ – локальные
переменные C, X.
В программе имеется два оператора вывода Writeln. Первым из них будет
выполнен оператор, находящийся в подпрограмме Al. В результате будут
выведены значения С, X, Y, равные:

С=0.6 X=1 Y=1.

Сразу за ним выполняется оператор Writeln, находящийся в вызывающей


программе. В результате будут выведены значения:

C=10 X=’A’ Y=1.

1.3. Функции

1.3.1. Описание функций

Объявление функции размещается в разделе процедур и функций того


блока, в котором эта функция вводится в употребление.
Объявление функции аналогично объявлению процедуры за исключением
следующих отличий.
Первое отличие.
Заголовок функции имеет следующий вид, который иллюстрирует
рисунок 1.7.
Заголовок функции определяет ее имя, список формальных параметров и
тип возвращаемого значения. Заголовок начинается служебным словом
Function.
<Список_формальных_параметров> определяется точно так же, как и при
описании процедуры. В качестве формальных параметров могут
использоваться параметры-значения, параметры-переменные, параметры-
константы, параметры без типа, параметры процедурного типа. Их описание
приведено в пп.1.2.5 – 1.2.9.
Заголовок функции завершается указанием имени типа возвращаемого
значения функции (<Идентификатор_типа>). При его описании может быть
использовано только имя типа, но не его задание. Поэтому тип возвращаемого
значения должен быть стандартным или предварительно описанным.

27
<Заголовок_функции> ::=

Function <Идентификатор>

<Список_формальных_параметров>

: <Идентификатор_типа>

Рисунок 1.7 – Синтаксическая диаграмма


заголовка функции

Второе отличие.
Как и процедуры, функции могут изменять значения глобальных
переменных, параметров-переменных и параметров без типа. Но кроме этого
каждая функция вычисляет значение, называемое возвращаемым значением
функции. Данное значение передается в точку вызова функции. Чтобы
установить возвращаемое значение, необходимо присвоить это значение
идентификатору функции. Поэтому в теле функции обязательно должен
присутствовать хотя бы один оператор присваивания, в левой части которого
записано имя описываемой функции без параметров:

<Идентификатор_функции> := <Выражение>

Хотя бы один оператор такого вида должен быть выполнен.


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

Пример 1.8.
Объявление функции. Функция Simvol определяет наличие искомого
символа в анализируемой строке.

Function Simvol (Const S: String; C: Char): Boolean;


Var
28
I: 1 .. 255;
Begin
For I := 1 To Length (S) Do
If S[I] = C Then
Begin
Simvol := True; {Возвращаемое значение}
Exit;
End;
Simvol := False; {Возвращаемое значение}
End;

Данная функция посимвольно анализирует значение строки,


поступающей на вход ее формального параметра S. Если очередной символ
строки равен искомому символу, поступающему на вход формального
параметра С, то возвращаемое значение функции устанавливается в True. Если
среди всех символов строки нет искомого символа, возвращаемое значение
функции устанавливается в False.

1.3.2. Вызов функции

Для обращения к функции в вызывающей программе используется вызов


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

<Вызов_функции> [(Список_фактических_параметров)].

Количество фактических параметров должно быть равно количеству


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

Simvol (X, Y),

где фактический параметр X должен иметь тип String, а фактический параметр


Y – тип Char.
Поскольку функция задает правило вычисления возвращаемого значения,
то вызов функции представляет собой данное значение. Это значение
используется обычно в качестве операнда какой-либо операции. Таким
образом, вызов функции может быть записан везде, где по синтаксису

29
допускается использование выражений (например, в правой части оператора
присваивания).
Применительно к примеру 1.8 вызов функции в вызывающей программе
может быть использован, например, так:

Z := Simvol (X, Y) Or Simvol (A, B);

Здесь в выражении используется два вызова функции Simvol. При его


вычислении в точки вызова передаются возвращаемые значения функции.
Далее данные значения участвуют в вычислении выражения.

Пример 1.9.
Вычисление расстояний между N парами точек на плоскости.
Использование функции с параметрами-значениями.

Program R5;
Var
X1, Y1, X2, Y2: Real; {Объявление глобальных переменных}
I: Integer; {Объявление глобальных переменных}
Const
N = 10;
{Функция с параметрами-значениями и возвращаемым значением типа Real}
Function Rast5 (Xx1, Xx2, Yy1, Yy2: Real): Real;
Begin
Rast5 := Sqrt (Sqr (Xx1 - Xx2) + Sqr (Yy1 - Yy2)) {Возвращаемое
значение}
End;
{Вызывающая программа}
Begin
For I:=1 To N Do
Begin
Readln (X1, X2, Y1, Y2);
{*} Writeln (Rast5 (X1, X2, Y1, Y2)) {Вызов процедуры Rast2}
End
End.
Данная программа выполняет те же вычисления, что и программы,
приведенные в примерах 1.1 – 1.3. Однако для вычисления расстояния между
точками используется функция Rast4. Вычисленное функцией расстояние
между точками передается в точку вызова с помощью возвращаемого значения.
Точка вызова находится в списке фактических параметров оператора вызова

30
стандартной процедуры вывода Writeln, поскольку по синтаксису языка
Паскаль в списке вывода могут использоваться значения выражений.
Вместо оператора {*} в тексте программы можно использовать,
например, такой фрагмент:

X1 := Rast4 (X1, X2, Y1, Y2);


Writeln (Х1) .

1.4. Рекурсивные подпрограммы

Рекурсия – это определение какого-либо понятия через само это понятие.


Классический пример рекурсивной функции – факториал n!:

⎧1 , если n = 0 ;
n! = ⎨
⎩ n ⋅ ( n − 1 )! при n > 0.

Здесь n! определяется через (n-1)!.


Особенность рекурсивного описания подпрограммы – то, что в ее теле
содержится обращение к ней самой.

Пример 1.10.
Рекурсивная функция. Вычисление n!.

Function Fakt (N: Integer): Longint;


Begin
If N = 0 Then {Ветвь, определяющая конец рекурсии}
Fakt := 1 {Возвращаемое значение функции}
Else
{*} Fakt := N * Fakt (N - 1) {Возвращаемое значение функции}
End;

Запись имени функции Fakt в левой части оператора присваивания {*}


показывает, что это возвращаемое значение функции. Вызов функции Fakt в
правой части оператора {*} говорит об обращении к этой же функции, т.е. о
рекурсивности.

Существует два вида рекурсии - явная и неявная (взаимная).

31
Явная рекурсия – это рекурсия, при которой обращение к подпрограмме
содержится в теле самой подпрограммы.
Неявная (взаимная) рекурсия – это рекурсия, при которой обращение к
подпрограмме содержится в теле другой подпрограммы, к которой
производится обращение из данной подпрограммы.
Возможность рекурсивного обращения обеспечивается специальными
средствами, которые при каждом повторном (рекурсивном) вызове подпрограммы
создают новый экземпляр ее памяти, делая временно недоступным прежний
экземпляр памяти (т.е. значения локальных переменных и фактических
параметров, существующих на момент рекурсивного вызова). Все экземпляры
памяти рекурсивной подпрограммы помешаются в стек. Всегда доступен только
текущий экземпляр памяти, созданный последним. При каждом возврате из
рекурсивной подпрограммы текущий экземпляр памяти удаляется из стека и
становится доступным предыдущий экземпляр.
В нерекурсивной подпрограмме механизм выделения памяти в стеке
отсутствует.
Рекурсивность является не свойством подпрограммы, а лишь свойством
ее описания. Ту же подпрограмму можно организовать и без рекурсии.
Например, функцию из предыдущего примера можно реализовать без рекурсии
следующим образом.

Пример 1.11.
Нерекурсивная функция. Вычисление n! по формуле:

n! = 1 ⋅ 2 ⋅ 3 ⋅ ... ⋅ ( n − 1 ) ⋅ n .

Function Fakt (N: Integer): Longint;


Var
K, I: Integer;
Begin
K:=1;
For I := 1 To N Do K := K * I;
Fakt := K; {Возвращаемое значение функции}
End;

Достоинство рекурсивной записи – в общем случае запись программы


короче и нагляднее, чем нерекурсивная запись.
Недостатки – требуется больше памяти и машинного времени (за счет
повторных обращений к функции), чем при нерекурсивной записи.

32
1.5. Директивы

В предыдущих разделах рассмотрены подпрограммы, тело которых


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

<Тело_подпрограммы> ::=

Блок
Forward
Interrupt ;
External
Inline

Рисунок 1.8 – Синтаксическая диаграмма


тела подпрограммы

В описании подпрограммы перед блоком может указываться директива


прерывания Interrupt. Подпрограмма в этом случае рассматривается как
подпрограмма прерывания.
Вместо блока в описании подпрограммы может быть записано
опережающее описание (директива Forward), внешнее описание (директива
Eхternal) или внутреннее описание (директива Inline).
Внешними подпрограммами следует пользоваться при необходимости в
объединении большого количества объектных модулей. Описания External
позволяют подключать отдельно скомпилированные подпрограммы,
написанные на языке ассемблера. С помощью команды "L Имя_файла" для
внешней подпрограммы должны быть установлены связи с программой или
модулем на языке Паскаль.
Директивы Inline позволяют записывать вместо раздела операторов
инструкции в машинном коде.
Описание подпрограмм типа Interrupt, Ecternal, Inline в данном учебном
пособии не рассматривается.
Описание подпрограммы, содержащее вместо блока директиву Forward,
называется опережающим описанием. После этого описания подпрограмма

33
должна определяться с помощью определяющего описания. Определяющее
описание - это описание, использующее тот же заголовок подпрограммы, что и
в опережающем описании, но без списка формальных параметров, и в которое
включен блок (разделылокальных описаний и операторов).
Опережающее и определяющее описания представляют собор полное
описание подпрограммы. Подпрограмма считается описанной с помощью
опережающего описания.
Определяющее описание может быть внешним описанием, но не может
быть внутренним описанием или другим опережающим описанием.
Определяющее описание не может содержать директиву Interrupt.
Между опережающим и определяющим описаниями могут описываться
другие подпрограммы, которые могут обращаться к подпрограмме с
опережающим описанием (рисунок 1.9). Таким образом может быть
реализована взаимная (неявная) рекурсия.
Использование процедур со взаимной рекурсией при опережающем
описании одной из процедур иллюстрирует программа, приведенная в примере
1.12.

Опережающее
описание
подпрограммы 1

Подпрограмма 2

Определяющее
описание
подпрограммы 1

Рисунок 1.9 – Организация взаимной рекурсии

34
Пример 1.12.
Использование процедур со взаимной рекурсией. Применение директивы
Forward. Программа вычисления N-ых членов последовательностей, заданных
рекуррентными формулами:

X i = 0 ,5 ( X i − 1 + Pi − 1 ); X 0 = 1;
Pi = X i − 1 − Pi − 1 ; P0 = 2.

Program VzRec;
Var
N: Integer; {N, Xi, Pi - глобальные переменные}
Xi, Pi: Real;
{Процедура Form_Xi вычисляет значение X}
{*} Procedure Form_Xi (I: Integer; Var Xi: Real); {Опережающее описание
Forward; процедуры Form_Xi}
{Процедура Form_Pi вычисляет значение Р}
Procedure Form_Pi (I: Integer; Var Pi: Real); {Процедура с саморекурсией
(вызывает сама себя) и взаимной рекурсией
(вызывает процедуру Form_Xi)}
Var
Xi: Real; {Описание локальной переменной Xi}
Begin
If I = 0 Then
Pi := 2 {Начальное значение Pi}
Else
Begin
Form_Xi (I - 1, Xi); {Вызов процедуры Form_Xi }
Form_Pi (I - 1, Pi); {Рекурсивный вызов процедуры Form_Pi}
Pi := Xi – Pi {Вычисление i-го члена последовательности Р}
End
End; {Конец процедуры Form_Pi }
Procedure Form_Xi; {Определяющее описание процедуры Form_Xi}
Var
Pi: Real; {Описание локальной переменной Pi}
Begin
If I = 0 Then
Xi := 1 { Начальное значение Xi}
Else
Begin
Form_Xi (I - 1, Xi); {Рекурсивный вызов процедуры Form_Xi}
Form_Pi (I - 1, Pi); {Вызов процедуры Form_Pi}

35
Xi := 0.5 * (Xi+Pi) {Вычисление I-го члена последовательности Х}
End
End;
Begin {Вызывающая программа}
Write ('Введите N:');
Read (N); {Чтение номера N вычисляемых членов
последовательностей}
Form_Xi (N, Xi); {Вызов процедуры Form_Xi}
Form_Pi (N, Pi); {Вызов процедуры Form_Pi}
Writeln ('X', N, '=', Xi); {Печать результатов значений глобальных
Writeln ('P', N, '=', Pi) переменных Xi и Pi}
End.

В данной программе использованы две процедуры - Form_Xi, Form_Pi. В


теле обеих процедур содержится как вызов их самих (явная рекурсия), так и
взаимный вызов друг друга (неявная рекурсия). В связи с последним одна из
процедур (в данном случае Fоrm_Xi) должна быть определена с помощью
опережающего описания (см. {*} в программе данного примера).
Определяющее описание процедуры Form_Xi содержит раздел
объявлений локальных переменных и раздел операторов. В отличие от
обычного описания в заголовке процедуры Form_Xi при определяющем
описании отсутствует список формальных параметров.
Между определяющим и опережающим описаниями процедуры Form_Xi
описана процедура Form_Pi. Данная процедура описана обычным образом, без
директив.
В первый раз процедуры Form_Xi и Form_Pi вызываются из головной
программы. Затем осуществляются последовательные вызовы процедурами
самих себя и друг друга.

1.6. Библиотечные модули


пользователя

1.6.1. Общие сведения

В Паскале существует три типа файлов:


а) файлы, содержащие исходные тексты программ (типа pas);
б) готовые к выполнению exe-файлы – это файлы, получаемые в
результате компиляции исходного текста программы;

36
в) tpu-файлы – это файлы, получаемые в результате компиляции
библиотечных модулей (TPU – Turbo Pascal Unit).
В Турбо Паскале имеется ряд стандартных библиотечных модулей
(System, Graph, Dos, Crt, Printer, Overlay, Strings, Turbo3, Graph3),
поддерживающих работу программных модулей.
Часто используемые процедуры и функции удобно хранить в библиотеках
подпрограмм пользователя. Это позволяет использовать их вызовы в различных
программах. Средством создания библиотек подпрограмм служат
библиотечные модули.
Библиотечный модуль – это результат трансляции в режиме Compile
специальной структурной единицы Unit с установленной директивой
Destination = Disk.
В результате такой компиляции создается tpu-файл.
Введение tpu-файлов позволяет создавать программы (exe-файлы),
превышающие 64К байтов. Однако каждый tpu-файл не может превышать 64К
байта. Сумма объемов модулей, составляющих программу, ограничена лишь
объемом оперативной памяти компьютера.
Модуль в Паскале является основой модульного программирования. В
отличие от программы модуль не может быть запущен на выполнение
самостоятельно, он может только участвовать в построении программы или
другого модуля. Модули компилируются независимо от использующей их
программы.
Чтобы подключить модуль к программе (или другому модулю),
необходимо и достаточно указать его имя в директиве Uses программы или
использующего его модуля.

1.6.2. Структура модуля Unit

Модуль Unit имеет структуру, которую иллюстрирует рисунок 1.10.


Структура модуля в общем виде также может быть представлена
следующим образом:

Unit <Имя_модуля>; {Раздел Unit


(заголовок модуля)}
Interface {Интерфейсная секция}
Uses <Список_используемых_Unit>
{Описание глобальных программных элементов}
Type

Const

Var

37

{Заголовки процедур и функций с указанием параметров}
Procedure …
Function …
Implementation {Секция реализации}
<Описание локальных типов, констант, переменных, процедур и функций,
описанных в секции Interface, и внутренних процедур и функций>
Begin {Секция инициализации}
<Операторы>
End.

<Модуль>::=

<Заголовок_модуля> ;

<Интерфейсный_раздел>

<Раздел_реализации>

<Раздел_инициализации> .

Рисунок 1.10 – Синтаксическая диаграмма модуля Unit

Таким образом, модуль Unit состоит из разделов, которые называются


также секциями.
В разделе Unit (так называется заголовок модуля) содержится имя
библиотечного модуля. Оно должно совпадать с именем файла, где содержится
исходный текст модуля. Например, если исходный файл имеет имя Arifm.pas, то
имя модуля Unit Arifm.
Синтаксическая диаграмма раздела Unit имеет вид, который представляет
рисунок 1.11.
В секции Interface (интерфейсном разделе) описываются внешние
элементы – глобальные типы, константы, переменные, процедуры и функции.
Это те элементы, которые доступны основной программе или другому модулю.
Описания могут следовать в любом порядке (как и в программном Паскаль-
модуле).

38
<Заголовок_модуля>::=

Unit <Имя_модуля>

Рисунок 1.11 – Синтаксическая диаграмма раздела Unit

Синтаксическая диаграмма интерфейсной секции имеет вид, который


иллюстрирует рисунок 1.12.
Описания процедур и функций представляют собой их заголовки, с
указанием параметров и возвращаемых значений функций. Тела процедур и
функций в интерфейсной секции не указываются. Основная программа и
другие модули имеют доступ к элементам, описанным в интерфейсной секции,
как если бы они были описаны в них самих.

<Интерфейсный_раздел>::=

Interface

<Предложение_Uses>

<Раздел_констант>

<Раздел_типов>

<Раздел_переменных>

<Раздел_заголовков_процедур_и_функций>

Рисунок 1.12 - Синтаксическая диаграмма


интерфейсного раздела

39
Синтаксическая диаграмма раздела заголовков процедур и функций
имеет вид, который изображает рисунок 1.13.
В интерфейсной секции недопустимы заголовки подпрограмм с
директивами Interrupt и Forward. Само описание заголовков процедур и
функций в интерфейсной секции является аналогом опережающего описания
(аналогом директивы Forward).
Если при объявлении типов, данных или подпрограмм используются
типы, введенные в других модулях, то эти модули должны быть перечислены в
директиве Uses сразу после слова Interface. В директиве Uses данной секции
рекомендуется указывать только те модули, которые необходимы именно в
этой секции.

<Раздел_заголовков_процедур_и_функций>::=

<Заголовок_процедуры> ;

<Заголовок_функции>

<Директива_Inline> ;

Рисунок 1.13 - Синтаксическая диаграмма


раздела заголовков процедур и функций

В секции Implementation (разделе реализации) описываются локальные


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

40
другим модулям. Это скрытая часть модуля.
Описанные в разделе реализации типы, константы, переменные являются
глобальными по отношению к подпрограммам этого раздела, а также
операторам раздела инициализации.
Если в телах подпрограмм или при объявлении типов, переменных в
разделе реализации используются имена, объявленные в других модулях, и эти
модули не попали в предложение Uses раздела Interface, то они перечисляются
в предложении Uses после слова Implementation.
Если в модуле содержатся обращения к внешним процедурам или
функциям, описанным на языке Assembler, то после заголовка такой
подпрограммы вместо ее тела в разделе реализации записывается директива
External. Данная директива говорит о том, что тело процедуры или функции
описано в другом модуле (на языке Assembler, файл имеет расширение obj).

<Раздел_реализации>::=

Implementation

<Предложение_Uses>

<Раздел_меток>
<Раздел_констант>
<Раздел_типов>
<Раздел_переменных>

Раздел_описаний_процедур_и_функций

Рисунок 1.14 - Синтаксическая диаграмма раздела реализации

В секции инициализации содержатся операторы, которые выполняются


до операторов из тела программы (обычно это установки некоторых стартовых
значений).

41
Синтаксическая диаграмма раздела инициализации имеет вид, который
содержит рисунок 1.15.
Синтаксическая диаграмма раздела операторов имеет вид, который
представляет рисунок 1.16.
Если программный модуль использует несколько модулей Unit, то их
части инициализации будут выполняться в порядке их перечисления в
предложении Uses программы до операторов тела программы. Если
операторная часть в секции инициализации отсутствует, то секция состоит из
слова End.
Все разделы модуля являются необязательными, но служебные слова,
начинающие разделы, должны присутствовать обязательно.

<Раздел_инициализации>::=

End

<Раздел_операторов>

Рисунок 1.15 - Синтаксическая диаграмма раздела инициализации

<Раздел_операторов>::=

Begin <Оператор> End

Рисунок 1.16 - Синтаксическая диаграмма


раздела операторов

Пример 1.13.
Структура пустого модуля. Модуль содержит только служебные слова,
которые должны присутствовать в Unit обязательно.

42
Unit Hollow; {Пустой модуль}
Interface
Implementation
End.

Пример 1.14.
Абстрактная программа, отражающая правила оформления модуля Unit и
программного модуля. Подчеркнуты служебные слова, которые должны
присутствовать в Unit обязательно.

Unit U1; {Раздел Unit модуля U1}


Interface {Интерфейсный раздел}
Uses Dos;
Const
MyValue = 724;
Type
Ned = (Pn, Vt, Sr, Ch, Pt, Sb, Vs);
Var
D: Ned;
Procedure SetNed (Var Den: Ned);
Function WeekEnd: Boolean;
Implementation {Раздел реализации}
Uses U2;
Var
D1, D2, D3: Ned;
Procedure SetNed;
Begin
<Тело процедуры SetNed>
End;
Function WeekEnd;
Begin
<Тело функции WeekEnd>
End;
Begin {Раздел инициализации}
D := Pn
End. {Конец модуля U1}

Program Main; {Программный модуль (главная программа)}


Uses U1; {Предложение использования в программном модуле}
Var
X: Ned;
43
B: Boolean;
I: Integer;
...
Begin
...
SetNed (X); {Вызов процедуры, описанной в модуле U1}
...
B := WeekEnd; {Вызов функции, описанной в модуле U1}
...
I := MyValue; {Обращение к константе, описанной в модуле U1}
...

Написанный модуль U1 должен быть помещен в файл U1.pas, затем


откомпилирован с директивой

Destination = Disk.

Результат компиляции – файл U1.tpu.

Чтобы модуль можно было использовать в программе, его имя нужно


записать в предложении Uses вызывающего программного модуля.
Чтобы найти модуль, указанный в предложении Uses программы,
компилятор вначале просматривает библиотеку Turbo.tpl (это библиотека, в
которой хранятся стандартные модули языка Паскаль – Dos, Crt и т.д.). Если
нужного модуля здесь нет, то он ищется в текущем каталоге (в том, где
находится программный модуль), а затем – в каталоге модулей, заданном
командой Options / Directories / Unit Directories интегрированной среды Турбо
Паскаль. Поэтому желательно файл U1.tpu поместить или в текущий каталог,
или в каталог модулей (его можно задать в окне ввода Options / Directories /
Unit Directories).
Если файл модуля находится в другом месте или имя файла не совпадает
с именем модуля (например, модуль Unit U1, а имя файла, в котором он
находится – Unit1.pas), то компилятору необходимо передать нужное имя
файла (если он в другом месте – то с путем к нему) с помощью директивы
компилятора

{$U <Имя_файла>}

Этой директиве передается имя файла с расширением pas. Данная


директива должна быть помещена непосредственно перед именем модуля в
предложении Uses. Например,

44
Uses Dos, Crt, {$U Unit1.pas} U1;

1.6.3. Особенности работы с модулями

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

Вторая особенность.
Порядок подключения влияет на доступность библиотечных типов,
данных, процедур и функций. Например, имеются два модуля U1 и U2. В
каждом из этих модулей в интерфейсной секции описаны одноименные тип
Ned, переменная D, процедура SetNed. Но они реализованы по-разному.
Если в программе записано предложение использования

Uses U1, U2;

то обращения к элементам Ned, D, SetNed будут эквивалентны обращениям к


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

U1.Ned, U1.D, U1.SetNed(X)

Таким образом, если вводят одинаковые идентификаторы, то необходимо


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

Третья особенность.
Решение проблемы закольцованности (циклических ссылок модулей друг
на друга). Например, модуль U1 использует элементы из модуля U2, а модуль
U2 использует элементы из модуля U1. Решение данной проблемы зависит от
того, в каком разделе возникла закольцованность.
Если оба модуля подключают друг друга в разделе реализации:

Unit U1;
Interface

45
Implementation
Uses U2;

Unit U2;
Interface
Implementation
Uses U1;

то закольцованность автоматически разрешается компилятором, поскольку


Паскаль для обоих модулей может выполнять полную компиляцию
интерфейсных секций (а описания в интерфейсных секциях аналогичны
опережающему описанию процедур и функций с помощью директивы
Forward).
Но если хотя бы один из модулей подключает другой в разделе Interface,
то проблема закольцованности может быть решена только программным путем.
В этом случае обычно используется третий модуль. В него помещаются все
типы, переменные или подпрограммы, которые ссылаются друг на друга в
первых двух модулях. Затем они удаляются из первых двух модулей, и к
данным модулям подключается третий модуль, независящий от своего
предложения Uses.

Пример 1.15.
Пусть Unit U1 из примера 1.14 в секции реализации подключает модуль
U2. Пусть модуль U2 имеет следующий интерфейсный раздел:

Unit U2;
Inteface
Uses U1;
Procedure Dni (Var Dd: Ned);

Тип Ned объявлен в модуле U1. Поскольку на него имеется ссылка в


интерфейсной секции модуля U2, то в директиве Uses модуля U2 необходимо
подключить модуль U1.
Транслятор такую ситуацию закольцованности не пропускает. Решение –
в создании третьего модуля U3, куда будет вынесено объявление типа Ned:

Unit U3;
Intereface
Type

46
Ned = (Pn, Vt, Sr, Ch, Pt, Sb, Vs);
Implementation
End.

Из модуля U1 (см. пример 1.14) необходимо убрать объявление типа Ned.


Из предложения Uses модуля 2 – убрать подключение модуля U1. В модулях
U1 и U2 в интерфейсных разделах необходимо подключить модуль U3. В
результате предложения Uses в интерфейсных разделах модулей U1 и U2
примут вид:

Uses Dos, U3; {В модуле U1}


Uses U3; {В модуле U2}

Достоинства использования модулей Unit:


1) Наличие модулей позволяет использовать модульное
программирование, то есть представлять программу в виде модулей и при
необходимости корректировать отдельные модули, а не всю программу в
целом.
2) Модули компилируются независимо друг от друга; при подключении
модуля к другой программе он не компилируется заново. Таким образом,
сокращается время компиляции больших программ. Это же справедливо и при
корректировке отдельных модулей – заново компилируются только зависящие
от них модули.
3) Наличие модулей позволяет создавать большие программы с
суммарным размером исполнимого кода большим, чем 64К.

1.6.4. Подключение к программе внешнего


файла

Вторым способом подключения внешних процедур и функций к


программе является использование директивы компилятора «Включение в
программу внешнего текстового файла»

{$I <Имя_файла>}

Данная директива сообщает компилятору о необходимости включить в


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

47
Примеры включения исходных файлов:

{$I F1.pas} (или эквивалентно {$I F1})


{$I C:\Dir1\Proc1.ini}

По умолчанию расширением <Имени_файла> является pas.


Включаемый файл должен удовлетворять условиям:
1) при его включении на место директивы {$I …} он должен вписаться в
структуру и смысл программы без ошибок;
2) он должен содержать законченный смысловой фрагмент, то есть блок
от Begin до End (например, тело процедуры) должен храниться целиком в
одном файле;
3) включаемый файл не может быть указан в середине раздела
операторов.
Включаемые файлы сами могут содержать директивы {$I …}.
Максимальный уровень такой вложенности равен восьми.
К недостаткам такого подключения к программе внешнего файла по
сравнению с использованием библиотечных модулей можно отнести
следующее:
а) подключаемые файлы каждый раз компилируются заново. Это
увеличивает время компиляции;
б) размер программы не может превышать 64К.

48
РАЗДЕЛ 2. ПРОСТЕЙШИЙ ВВОД-
ВЫВОД

2.1. Процедуры ввода из стандартного


текстового файла Input

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


текстовым файлом Input, устройство вывода данных – со стандартным
текстовым файлом Output.
Устройство ввода – это, обычно, клавиатура дисплея, а устройство
вывода – экран дисплея.
Операторы вызова процедур ввода из стандартного текстового файла
Input записываются следующим образом:

Read (X1, X2, …, Xn);


Readln (X1, X2, …, Xn);

X1, X2, .. , Xn – это переменные, являющиеся элементами списка ввода


(фактические параметры процедур ввода).
Процедуры читают символьные данные из файла Input и присваивают их
переменным Xi, при этом символьные данные преобразуются к типу
переменных X. Допустимы следующие типы переменных X:
• целочисленные типы или их диапазоны;
• Char или его диапазон;
• вещественные типы;
• String;
• Array Of Char.
Процедура Readln после чтения элементов списка ввода осуществляет
переход к следующей строке файла Input. Поэтому по следующей процедуре
Read или Readln будут читаться данные из следующей строки входного файла.
Для процедуры Readln список ввода может быть опущен.

Пример 2.1.
Пусть имеется следующий фрагмент программы:

Var X, Y, Z: Integer;

49
...
{*} Readln (X, Y);
{**} Readln (Z);
...
Пусть во входном файле Input имеются следующие значения:
248 −15 4 70 значения 1-й строки
11 значения 2-й строки

Тогда после выполнения процедур ввода {*} и {**} переменные X, Y, Z


примут значения:

X = 248 Y = -15 Z = 11

Процедура Read после чтения элементов списка ввода осуществляет


переход на число прочитанных символов данной строки.
Для примера 2.1 выполнение процедур {*} и {**} даст результат

X = 248 Y = -15 Z = 4.

Особенности ввода:
1) Тип вводимого данного из входного файла должен соответствовать
типу переменной Xi из списка фактических параметров процедур Read, Readln.
2) Если в процедуре ввода в списке переменных присутствует несколько
переменных, то во входном файле они должны отделятся друг от друга
разделителями.
При вводе числовых значений они обычно разделяются пробелами.
Несколько идущих подряд переменных типа Char во входном файле
должны быть записаны без разделителей и без окаймляющих апострофов.

Пример 2.2.
Ввод переменных типа Char.

Var
A, B, C: Char;
-------------------------
Read (A, B, C);
-------------------------

Пусть во входном файле Input записано значение

50
End

Тогда после выполнения процедуры Read(A, B, C) переменные примут


значения:

A = ’E’ B = ’n’ C = ’d’

Для переменных Xi типа String чтение символов из входного файла


продолжается, пока не будет достигнута максимальная длина Xi, или до конца
строки файла. Переход на новую строку процедура Read не выполняет.
Поэтому для ввода данных типа String нужно использовать процедуру Readln.
Для переменных типа Array Of Char каждому элементу массива
присваивается очередной прочитанный символ.

2.2. Процедуры вывода в стандартный


текстовый файл Output

Процедуры вывода в стандартный выходной файл Output имеют вид:

Write (E1, E2, …, En);


Writeln (E1, E2, …, En); .

Здесь Ei – элементы списка вывода, в качестве которых могут


использоваться выражения, строковые константы или строковые переменные.
Для Writeln список E может быть опущен.
Допускаются следующие типы выражения Ei:
• целочисленные или их диапазоны;
• Char или его диапазон;
• вещественные;
• Boolean;
• String;
• Array Of Char.
Данные типы при передаче в выходной файл преобразуются в
символьный вид и разбиваются на строки фиксированной длины. При выводе
на экран дисплея строка содержит 80 позиций, на печать – 128 позиций.
Для вывода используется буфер, в котором предварительно формируется
строка символов, подлежащая выводу.
Если используется процедура Write, то выходная строка выводится
только после заполнения буфера. Переход на следующую выходную строку
осуществляется только после заполнения текущей строки.

51
При использовании процедуры Writeln переход на следующую строку
осуществляется после вывода всех элементов из списка ее фактических
параметров.
Процедура Writeln без параметров используется для перехода на новую
строку.
Количество позиций поля, отводимого в строке выходного файла для всех
типов выходных данных, кроме вещественных, равно минимально
необходимому. Для вывода вещественных типов отводится фиксированное
количество позиций (например, для типа Real отводится 17 позиций), причем
вещественное число будет выведено в виде мантиссы и порядка.
Если тип переменной вывода Еi – Boolean, то на печать выводится
значение True или False.
Недостаток процедур вывода с элементами списка вывода вида Еi –
жесткая фиксация выводимых значений по позициям строки.
Для управления выводом по позициям строки используются элементы
вывода вида

Е: L1
или
E: L1: L2

Здесь: L1, L2 – выражения целого типа.


L1 – длина поля, отводимого для элемента вывода Е.
Если значение L1 больше необходимого количества позиций, данные
выравниваются по правой границе поля (т.е. поле при необходимости
дополняется пробелами слева).
Если поле L1 меньше необходимой длины, оно автоматически
расширяется до минимально необходимого.
L2 может использоваться только в том случае, если элемент вывода Еi –
выражение вещественного типа. Если в этом случае L2 отсутствует, то
вещественное число будет выведено в виде мантиссы и порядка (рисунок 2.1).
На данном рисунке R – разрядность дробной части мантиссы. При L1 большем
необходимого количества позиций выводимое вещественное число занимает то
же фиксированное число позиций, которое принято по умолчанию (17 для типа
Real) и поле дополняется пробелами слева. При 17 ≥ L1 ≥ 8

R = L1 – 7.

При L1 < 8 поле вывода вещественного числа расширяется до 8 позиций.


Если L2 указано, используется представление вещественного числа в
форме с фиксированной точкой, причем значение L2 определяет количество
цифр в дробной части числа.
Например, пусть имеется оператор

52
Write (1.2546: 10: 2);

Данный оператор в выходной файл поместит значение, которое


представляет рисунок 2.2.

zm m0.m1m2…mREzpp2p1

Знак мантиссы Мантисса Знак 2-хразрядный


(минус или (R+1)- порядка целый порядок
пробел) разрядная

Рисунок 2.1 – Вид выводимого вещественного числа типа Real


в форме с плавающей точкой

1 . 2 5

2 позиции

10 позиций

Рисунок 2.2 – Значение, выводимое выходной файл


оператором Write (1.2546: 10: 2)

На данном рисунке символом обозначен пробел.

Пример 2.3.
Пусть Y и Z представляют собой массивы. Значения Y(X) и Z(X)
подсчитаны предварительно. Значение Х изменяется в диапазоне от 1 до 40.
Необходимо вывести на экран таблицу значений в виде, который представляет

53
рисунок 2.3, отводя под столбцы и значения заданное количество позиций
(поз.) вывода.

ТАБЛИЦА 1.
9 поз. 23 поз. 24 поз.

Х … Y(X) … … Z(X) …
4 поз. 4 поз. 10 поз. 9 поз. 11 поз. 9 поз.

1 Значение Y(Х) Значение Z(Х)


2
3 20 поз. 20 поз.
… …

40

60 поз.

Рисунок 2.3 – Вид выводимой таблицы

Фрагмент программы, выполняющий формирование таблицы, имеет вид:

--------------------------------------------
Writeln (‘ ТАБЛИЦА 1.’);
For I := 1 To 60 Do Write (‘_’); {Верхняя строка отчеркивания шапки}
Writeln; {Переход на новую строку}
Writeln (‘|’, ’X’: 5, ’|’: 5, ’Y(X)’: 14, ’|’: 10, ’Z(X)’: 15, ’|’: 10);
{Вывод шапки таблицы}
For I := 1 To 60 Do Write (‘_’); {Нижняя строка отчеркивания шапки}
Writeln; {Переход на новую строку}
For X := 1 To 40 Do
Writeln (‘|’, X: 5, ’|’: 5, Y(X): 20: 6, ’|’: 4, Z(X): 20: 6, ’|’:5);
{Вывод содержимого таблицы}
For I:=1 To 60 Do Write (‘_’); {Строка подчеркивания таблицы}
Writeln; {Переход на новую строку}
---------------------------------------------

54
РАЗДЕЛ 3. ЗАПИСИ

3.1. Структура записи

Записи называют еще комбинированным типом данных или типом


Record.
Запись – это структура данных, состоящая в общем случае из
иерархически упорядоченных разнородных компонентов. В отличие от
массивов компоненты записей могут иметь различные типы, и доступ к ним
осуществляется не по индексам, а по именам.
Компоненты записей называются полями. На тип поля записи никаких
ограничений не накладывается, поэтому компонентой записи может быть тоже
запись. В этом случае говорят об иерархической записи. Уровень иерархии
(вложенности) не должен превышать 9.
Пример простой (не иерархической) записи – представление
комплексного числа a + b*i – содержит рисунок 3.1.

Komplex запись

Re Im поля

Рисунок 3.1 – Пример неиерархической записи

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


Cинтаксис задания структуры данных типа запись имеет вид, который
иллюстрирует рисунок 3.3
Список полей определяется с помощью синтаксической диаграммы,
которую содержит рисунок 3.4.
Из синтаксической диаграммы (см. рисунок 3.4) видно, что запись может
состоять из общей части и вариантной части. Любая из этих частей является
необязательной.

55
Anketa

Fio Pol Data_R Prof

Fam Im Ot God Mes Den

Рисунок 3.2 - Пример иерархической записи Anketa

<Тип_Record>::=

Record

<Список_полей> End

Рисунок 3.3 – Cинтаксическая диаграмма задания данных типа запись

< Список_полей>::=

<Общая_часть> ; <Вариантная_часть> ;

Рисунок 3.4 – Синтаксическая диаграмма списка полей

56
Поле записи обозначается идентификатором. К полю обращаются по
имени. Областью действия полей записи является сама запись. Имя каждого
поля внутри записи должно быть уникальным.

3.2. Записи без вариантной части

Такие записи содержат только общую часть.


Cинтаксис задания общей части записи имеет вид, который содержит
рисунок 3.5.

<Общая_часть>::=

<Поле> : <Тип>
,

Рисунок 3.5 – Синтаксическая диаграмма


задания общей части записи

Каждому полю записи дается свое имя и задается тип значения этого
поля.

Пример 3.1.
Объявление записи, структуру которой представляет рисунок 3.1.

Type
Komplex = Record
Re: Real;
Im: Real
End;

или эквивалентно

Type
Komplex = Record
Re, Im: Real;

57
End;

В разделе Var необходимо ввести переменные типа Komplex:

Var
X, Y: Komplex;

Тип поля записи может быть определен двумя способами:


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

Пример 3.2.
Объявление записи, структуру которой представляет рисунок 3.2.

Type
Data = Record
God: 1900..2000;
Mes: (Jn, Fb, Mr, Ap, Ma, Jn, Jl, Ag, Sp, Oc, Nv, Dc);
Den: 1..31
End;
Anketa = Record
Fio: Record
Fam: String [20];
Im: String [10];
Ot: String [20]
End;
Pol: (Man,Woman);
Data_R: Data;
Prof: String [20]
End;

В разделе Var необходимо ввести переменную типа Anketa:

Var
An1, An2: Anketa;
D1, D2: Data;

Объем памяти необходимый для записи, складывается из длин полей


нижнего уровня.

58
Переменная, имеющая тип записи верхнего уровня, называется полной
переменной.
Обращение к значению поля осуществляется с помощью идентификатора
полной переменной, идентификаторов всех полей (с учетом их иерархии), в
состав которых входит поле, и имени данного поля, разделенных точкой. Такое
имя называется составным именем.
Например, применительно к примерам 3.1 и 3.2, An1, An2, X, Y – это
полные переменные (полные имена). Составные имена записываются
следующим образом: X.Re, X.Im, An1.Pol, An1.Fio.Fam, An1.Data_R.God и т.д.
Для полных переменных одного и того же комбинированного типа
существует только одна операция – операция присваивания (в качестве
выражения в правой части оператора присваивания может быть использована
только переменная того же типа запись). Например, для примеров 3.1, 3.2
можно записать:

X := Y;
An1 := An2;

К этому моменту записи Y, An2 уже должны быть определены.


Составное имя может использоваться везде, где допустимо применение
типа поля. Для присваивания полям значений используется оператор
присваивания или ввода.

Пример 3.3.
Присвоение значений полям переменной D1 (к примеру 3.2).

D1.God := 1970;
D1.Mes := Jn;
D1.Den := 15;

Для определения значения полной переменной необходимо присвоить


значения всем полям, образующим это значение.
Составные имена можно использовать в операторах ввода-вывода.
Например:

Read (D1.God, D1.Den);


Write (A1.Fio.Fam);

Зачастую удобно пользоваться массивами из записей.


Например, применительно к примеру 3.2 может быть объявлен
следующий массив:
59
Var
Spisok: Array [1..100] Of Anketa;

Такое объединение позволяет хранить анкетные данные на 100 человек,


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

For I:=1 To 100 Do


Writeln (Spisok [I].Fio.Fam, Spisok [I].Fio.Im, Spisok [I].Fio.Ot)

3.3. Записи с вариантами

Записи, содержащие только общую часть, имеют строго определенную


структуру.
Записи с вариантами позволяют объединить описания записей, которые
похожи, но не идентичны по форме.
Они состоят из необязательной общей части и вариантной части. Общая
часть записи описывает поля, которые присутствуют в каждой переменной
определяемого типа.
Вариантная часть начинается зарезервированным словом Case и
описывает несколько вариантов структуры записи. Синтаксис вариантной части
представляет рисунок 3.6.

<Вариантная часть> ::=

Case <Поле> : <Ид_типа> Of

<Диапазон> : ( <Список_полей> )

Рисунок 3.6 – Синтаксическая диаграмма вариантной части записи


60
Диапазон (см. рисунок 3.6) имеет формат, который иллюстрирует
рисунок 3.7.

Диапазон ::=

<Константное_выражение>

.. <Константное_выражение>

Рисунок 3.7 – Синтаксическая диаграмма диапазона

В каждый момент выполнения программы один из вариантов структуры


является активным в зависимости от значения признака варианта.
Признак варианта описывается непосредственно после
зарезервированного слова Case. Признак является самостоятельным полем
общей части записи.
Поле признака может отсутствовать. В этом случае в Case указывается
только идентификатор типа, и программист обязан сам следить за тем, какой
вариант записи является активным.
Список полей каждого варианта заключается в скобки.
Например, пусть вриантная часть записи имеет вид:

а) Case I: Integer Of ...

Здесь идентификатор I определяет поле признака, которое является


самостоятельным полем общей части записи.

б) Case Integer Of ...

Здесь поле признака отсутствует. Варианты определяются целыми


значениями в списке вариантов.

Пример 3.4.
Запись с вариантной частью без поля признака.

Var
Z: Record
61
Case Integer Of
1: (I1: 1..10);
2: (J1: Char);
3: (K1: Boolean)
End;

В данном примере вариантная часть содержит три варианта. Будет


активен вариант, соответствующий имени поля. Например, если в программе
имеется обращение к полю Z.I1, то вариантная часть воспримется как поле типа
1..10. При обращении к вариантной части по имени Z.J1 она воспримется как
поле типа Char.

У части Case нет отдельного служебного слова End. Одно слово End
заканчивает всю конструкцию записи с вариантами.

Пример 3.5.
Объявление записи с вариантами, содержащей поле признака.

Type
Anketa1 = Record
{Общая часть}
Fio: Record
Fam: String[20];
Im: String[10];
Ot: String[20];
End;
{Вариантная часть}
Case Pol: (Men, Women) Of
Men: (Vozr1: 20..30);
Women: (Vozr2: 18..25)
End;

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

62
<Диапазон>: ( )

Например, если в предыдущем примере отсутствуют поля при значении


признака, равном Women, то этот вариант запишется так:

Women: ( )

Объем памяти, необходимый для записи с вариантами, складывается из


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

3.4. Оператор присоединения With

При работе с полями в их составном имени необходимо писать путь к


полю через все уровни иерархии, начиная от полного имени записи. С учетом
того, что количество уровней иерархии может достигать девяти, составное имя
поля может содержать девять компонент. Работа с такими именами неудобна, а
программа оказывается очень громоздкой.
Для сокращения составного имени поля может быть использован
оператор присоединения With. Данный оператор имеет формат, который
содержит рисунок 3.8.

Оператор_With::=

With <Переменная> Do

<Оператор>

Рисунок 3.8 – Синтаксическая диаграмма


оператора присоединения

63
На данном рисунке <Оператор> - это любой оператор языка, допустимый
для соответствующих типов полей.
В операторе With указывается список переменных типа Record.
Оператор With облегчает доступ к полям этих записей и минимизирует
повторные адресные вычисления. Внутри <Оператора>, вложенного в оператор
With, к полям этих записей можно обращаться как к простым переменным.

Пример 3.6.
В примере 3.2 объявлена переменная D1: Data. Используя оператор With,
вместо примера 3.3 можно записать:

With D1 Do
Begin
God:=1970;
Mes:=Yan;
Den:=15
End;

Пример 3.7.
Используя оператор With, к полям записи An1 (см. пример 3.2) можно
обратиться следующим образом:

With An1 Do
Begin
Fio.Fam := ’Иванов’;
Fio.Im := ’Петр’;
Fio.Ot := ’Степанович’;
Pol := Man;
Data_R.God := 1970;
Data_R.Mes := Yan;
Data_R.Den := 25;
Prof := ’Студент’
End;

Без применения оператора With перед всеми именами полей необходимо


было бы написать имя записи An1.

Адрес переменной типа Record вычисляется до выполнения оператора


With. Любые модификации переменных, влияющие на вычисленное значение

64
адреса, до завершения оператора With не отражаются на значении
вычисленного ранее адреса.
Указание списка переменных типа запись в операторе With (см.
предыдущую синтаксическую диаграмму) является сокращенной формой
оператора With. Сокращенная форма оператора:

With Z1, Z2, Z3, … , Zn Do <Оператор>

эквивалентна следующей полной форме оператора With:

With Z1 Do
With Z2 Do
With Z3 Do
...
With Zn Do <Оператор>

Из полной формы оператора With видно, что идентификатор поля в


<Операторе> обозначает компонент записи из ближайшего объемлющего
оператора With, в котором указана переменная с таким полем.

Пример 3.8.
Применительно к примеру 3.2 для полного устранения необходимости в
составных именах полей (см. пример 3.7) может быть использована следующая
сокращенная форма оператора With:

With An1, Fio, Data_R Do


Begin
Fam := ’Иванов’;
Im := ’Петр’;
Ot := ’Степанович’;
Pol := Man;
God := 1970;
Mes := Yan;
Den := 25;
Prof := ’Студент’;
End;

Если две переменные из списка записей оператора With имеют поля с


одним и тем же идентификатором, то внутри оператора With этот
идентификатор обозначает поле той переменной, которая указана в списке
позже.
65
Имена отдельных полей записи в программе могут совпадать с именами
переменных. Имя переменной в операторе With недоступно.

Пример 3.9.
Использование совпадающих идентификаторов.

Var
{Запись}
V: Record
V2: Integer;
V1: Record
A: Real
End;
A: Integer
End;
{Переменная}
A: Char;
...
With V, V1 Do
Begin
V2 := 1; {значение поля V.V2 равно 1}
A := 1.0; {значение поля V.V1.A равно 1.0}
V.A := 1; {значение поля V.A равно 1}
End;
A := ’A’;

В данном примере есть три элемента с именем А – поля V.V1.A, V.A и


переменная А типа Char. Указание имени поля А в <Операторе> оператора With
приведет к обращению к полю V.V1.A, поскольку поле А содержится в записи
V1, находящейся в списке записей сокращенной формы оператора With после
записи V. Чтобы обратиться к полю V.A, нужно использовать его составное имя
или вынести оператор, работающий с данным полем, за пределы оператора
With.
За пределами оператора With поля записи становятся недоступными, а
переменная А типа Char становится доступной.

3.5. Константа-запись

Одним из видов структурных типизованных констант являются


константы-записи.

66
Синтаксис задания константы-записи иллюстрирует рисунок 3.9.

<Константа_запись> ::=

( <Поле> : <Типизированная_константа> )

Рисунок 3.9 – Синтаксическая диаграмма


задания константы-записи

Как и остальные типизованные константы, константы-записи могут


использоваться в качестве инициированных переменных типа запись
(переменных, которым при запуске программы присваивается начальное
значение).

Пример 3.10.
Объявление константы-записи.

Type
Fam = (Ivanov, Petrov);
Data = Record
God: 1900..2000;
Mes: (Jn, Fb, Mr, Ap, Ma, Jn, Jl, Ag, Sp, Oc, Nv, Dc);
Den: 1..31
End;
Ank = Array[Fam] Of Data;
Const
D: Data = (God: 1950; Mes: Jn; Den: 3);
A: Ank = ((God: 1970; Mes: Dc; Den: 7), (God: 1945; Mes: Ma; Den: 15));

В данном примере D – это типизованная константа-запись, А – массив из


двух типизованных констант-записей А[Ivanov] и А[Petrov]. В константе А
внутренние скобки относятся к константам-записям, внешние – к
типизованным константам-массивам.
В типизованных константах-записях поля должны указываться в том же
порядке, как они следуют в объявлении типа запись.
67
Если запись содержит вариант, то можно указывать только поля
выбранного варианта. Если вариант содержит поле признака, то его значение
должно быть определено.
Использование компонент файлового типа в структурных константах-
записях запрещено.

Задание для самостоятельной подготовки. Константы-записи с


вариантами (примеры объявления и использования).

68
РАЗДЕЛ 4. МНОЖЕСТВА

4.1. Общие сведения

Множественный тип (тип Set) соответствует понятию множества в


математике.
В Паскале допускаются только конечные множества. Максимальное
количество элементов в множестве – 256. Все элементы множества должны
быть значениями одного типа. Тип элементов множества называется базовым
типом множества.
Базовым типом множества может быть любой скалярный тип, за
исключением вещественных типов. Целочисленные типы в качестве базового
типа могут использоваться только в виде диапазона 0 .. 255 или его
поддиапазонов.
Данные типа Set хранятся в памяти в унитарном коде и могут занимать от
одного до 32-х байтов. Объем памяти, занимаемый одним элементом
множества, равен одному биту. Каждому элементу во множестве жестко
соответствует свой номер бита.

Пример 4.1.
Пусть множество может содержать набор элементов 0..7, а его конкретное
значение равно множеству элементов 0, 3, 4. В памяти компьютера оно будет
представлено в виде, который иллюстрирует таблица 4.1.

Таблица 4.1 – Представление множества в памяти компьютера

0-й бит 1-й бит 2-й бит 3-й бит 4-й бит 5-й бит 6-й бит 7-й бит

1 0 0 1 1 0 0 0

Таким образом, элементу 0 соответствует 0-й бит поля памяти,


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

69
4.2. Конструктор множества

Значением переменной множественного типа является множество.


Конкретные значения множественного типа задаются с помощью конструктора
множества. Его синтаксис определяет рисунок 4.1.

<Конструктор_множества> ::=

[ <Выражение> ]

.. <Выражение>

Рисунок 4.1 – Синтаксическая диаграмма


конструктора множества

Как следует из синтаксической диаграммы, конструктор множества –


это список разделенных запятой выражений или диапазонов, заключенный в
квадратные скобки. При указании диапазона все значения из диапазона входят
в результирующее множество. Все значения из списка должны быть
одинакового типа – любого скалярного, кроме вещественных. Множество не
может содержать более 256 элементов.

Пример 4.2.
Примеры множеств.

[] – пустое множество (не содержит элементов);


[2, 3, 5, 7, 11] – множество простых чисел от 2 до 11.
[‘A’ .. ‘Z’] – множество латинских букв.
[1..10, 100..110] – множество целых чисел от 1 до 10 и от 100 до 110.
[Pn, Vt, Sr] – множество из 3-х элементов перечислимого типа.
[X .. 5*X] – множество целых чисел от текущего значения X до
значения выражения 5*X.

70
Конструктор множества – это фактически множественная константа.
Порядок перечисления элементов в множестве не играет роли. Каждый
элемент учитывается только один раз.
Например, множества [1, 2, 3] и [1, 3, 2] – это одно и тоже множество.

4.3. Задание множественного типа

При задании множественного типа необходимо задать базовый тип. В


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

<Тип_Set>::=

Set Of <Базовый_скалярный_тип>

Рисунок 4.2 – Синтаксическая диаграмма задания множественного типа

Как уже отмечалось, <Базовый_скалярный_тип> - это любой


перенумерованный тип, значения которого попадают в диапазон 0 .. 255.
Например:
Set Of 1..3 – значениями этого множественного типа могут быть
множества [], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3].
Set Of Boolean – значениями этого множественного типа могут быть
множества [True], [False], [True, False], [].
В качестве базового типа может быть использовано имя типа или задание
типа.
Задание типа Set аналогично представлению множества с помощью
массива:

Array [<Базовый_скалярный_тип>] Of Boolean.

Обработка таких массивов в Паскале неэффективна, поэтому


используется тип Set.

71
Пример 4.3.
Объявление множественного типа.

Type
Ned = (Pn, Vt, Sr, Ch, Pt, Sb, Vs);
Denned = Set Of Ned; {Используется имя базового типа}
Log = Set Of Boolean; {Используется имя базового типа}
Var
Den: Denned;
L1, L: Log;
I1, I2, I: Set Of 1..10; {Используется задание базового типа}
B: Boolean;

В данном примере Den, L1, L, I1, I2, I – это множественные переменные


(переменные-множества).

Для присваивания значений множественным переменным используются


множественные выражения.
Множественное выражение – это выражение, значением которого
является множество. Частным случаем множественных выражений являются
множественные переменные и конструкторы множества.

Пример 4.4.
Использование множественных выражений. Для переменных,
объявленных в примере 4.3, можно записать:

L1 := [True];
L := L1; {Значение L1 к этому моменту должно быть определено}
I := [1, 3, 5];
Den := [Sub, Vos];

4.4. Операции над множествами

Над множествами определены операции, которые представляет таблица


4.2.

72
Таблица 4.2 – Операции над множествами

Операция Описание операции Тип результата

= Равно

<> Не равно
Результат операции равен True, если левое
<=
множество является подмножеством правого Boolean
Результат операции равен True, если правое
>=
множество является подмножеством левого
Результат операции равен True, если некоторое
In скалярное значение (левый операнд) является
элементом множества (правый операнд)
Not *) Дополнение множества (одноместная операция)

+ Объединение множеств

* Пересечение множеств Set

– Разность множеств A – B = A * Not B


Исключающее объединение множеств
Xor *)
A Xor B = A + B – A * B

Здесь *) – операции, определенные не во всех версиях языка Паскаль.


Над множественными переменными определена одна встроенная
функция – Sizeof(X), указывающая количество байт для представления значения
X множественной переменной.
Операция In представляет собой операцию проверки вхождения элемента
в множество. Левый операнд должен принадлежать базовому типу, правый
операнд – множественному типу, построенному на основе этого базового типа.

Пример 4.5.
Операции над множествами. Пусть имеются объявления, приведенные в
примере 4.3.

I := [1, 3, 5];
B := 2 In I {в B значение False}
B := [3, 5]<=I {в B значение True}
B := [4, 5]<=I {в B значение False}

73
I := Not I {в I значение [2, 4, 6..10] – дополнение множества}

В двухместных операциях оба операнда должны принадлежать одному и


тому же множественному типу значений.
Объединение множеств (I2 := I + I1) – это множество, состоящее из
элементов, входящих хотя бы в одно из исходных множеств I и I1.
Объединение множеств иллюстрирует рисунок 4.3.

Рисунок 4.3 – Объединение множеств

Пересечение множеств (I2 := I * I1) – это множество, состоящее из


элементов, входящих в оба исходных множества I и I1. Пересечение множеств
иллюстрирует рисунок 4.4.

Рисунок 4.4 – Пересечение множеств

Разность множеств (I2 := I – I1) – это множество, состоящее из


элементов множества I, не входящих в множество I1. Разность множеств
иллюстрирует рисунок 4.5.

74
Рисунок 4.5 – Разность множеств

Пример 4.6.
Операции над множествами. Пусть имеются объявления, приведенные в
примере 4.3.

I1 := [1, 2, 3];
I := [1, 3, 5];
I2 := I1 + I; {в I2 значение [1, 2, 3, 5]}
I2 := I1*I; {в I2 значение [1, 3]}
I2 := I – I1; {в I2 значение [5]}

Выражения, приведенные в примере 4.6, представляют собой


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

[1, 2, 5, 6, 7] * [2 .. 6] + [3, 9]

является множество

[2, 3, 5, 6, 9].

75
Пример 4.7.
Использование множественного типа. Подсчитать общее количество букв
X, Y, Z в исходном тексте, оканчивающемся точкой.

Program Mno;
Var
Kol: Integer;
B: Char;
Begin
Kol := 0;
Read (B); {Чтение первой буквы текста}
While B<>’.’ Do
Begin
If B In [‘X’, ‘Y’, ‘Z’] Then {Если значение буквы входит в
множество [‘X’, ‘Y’, ‘Z’]}
Kol := Kol + 1;
Read (B); {Чтение очередной буквы текста}
End;
Writeln (Kol);
End.

4.5. Ввод / вывод значения


множественной переменной

Для ввода значения множественной переменной используется операция


объединения множеств.

Пример 4.8.
Ввод значения переменной типа множества больших латинских букв из
входного файла. Признак окончания ввода множества – точка.

Var
B: Char;
Mn: Set Of ‘A’..‘Z’; {Тип элементов вводимого множества}
Begin
Mn := []; {Начальное значение множества – пустое множество}
Repeat
Read (B); {Чтение очередного элемента множества}

76
Mn := Mn + [B] {Объединение множеств}
Until B = ’.’; {‘.’ – признак конца текста}
...

Во входном файле необходимо подряд записать все значения,


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

Пример 4.9.
Продолжение примера 4.8. Вывод значения переменной типа множества
латинских букв.

For B := ’A’ To ‘Z’ Do {Организация цикла по базовому типу множества}


If B In Mn Then {Анализ вхождения очередного значения базового
типа в значение множественной переменной}
Write (B: 5);

4.6. Типизованные константы-


множества

Типизованная константа-множество представляет собой, с точки зрения


синтаксиса, конструктор множества, то есть значение множественной
величины.
Синтаксис константы-множества определяет рисунок 4.6.
Синтаксическая диаграмма <Константы_элемент> (см. рисунок 4.6) имеет
вид, который иллюстрирует рисунок 4.7.
Таким образом, <Константа_элемент> представляет собой значения или
диапазоны значений базового типа множества.
Константа-множество может использоваться как инициированная
переменная типа множество.

77
<Константа_множество>::=

[ ]
<Константа_элемент>

Рисунок 4.6 – Синтаксическая диаграмма


константы-множества

<Константа_элемент>::=

<Константа>

.. <Константа>

Рисунок 4.7 – Синтаксическая диаграмма


константы-элемента

Пример 4.10.
Объявление типизованной константы-множества.

Const
Dig: Set Of 0..9 = [1, 3, 5];
Dig1: Set Of 0..9 = [];
Ch: Set Of ‘A’..‘Z’ = [‘A’..‘E’, ‘I’, ‘P’, ‘T’];

78
РАЗДЕЛ 5. ФАЙЛЫ

5.1. Общие сведения

Для всех рассмотренных ранее производных типов было характерно


наличие заранее заданного числа компонент.
Файловый тип – это произвольная последовательность элементов, длина
которой заранее не определена, а конкретизируется в процессе выполнения
программы.
Это определение логического файла, т.е. того, который используется в
программе (файл с точки зрения программиста).
Физический файл (набор данных) – это поименованная область памяти
на внешнем носителе, в которой хранится некоторая информация (файл с точки
зрения пользователя).
Элементы файла записаны в нем последовательно (рисунок 5.1).

F F1 F2 F3 …

длина не
файл элементы файла
зафиксирована

Рисунок 5.1 – Структура файла

В Паскале возможны два способа обращения к элементам файла:


последовательный доступ и прямой доступ.
При последовательном доступе по файлу можно двигаться только
последовательно, начиная с первого его элемента. У последовательного файла
доступен лишь очередной его элемент. Чтобы добраться до n-го элемента
файла, необходимо начать с первого элемента и пройти через предыдущие n – 1
элементов.
При прямом доступе можно обратиться непосредственно к элементу
файла с номером n, минуя предварительный просмотр n - 1 элемента файла.
Файловый тип - это единственный тип значений, посредством которого
данные, обрабатываемые программой, могут быть получены извне, а
результаты могут быть переданы человеку. Это единственный тип значений,
который связывает программу с периферийными устройствами компьютера.
79
В Паскале имеется три типа файлов (три вида переменных файлового
типа, т.е. файловых переменных):
• текстовые файлы;
• файлы с типом;
• файлы без типа.
Синтаксис задания файлового типа имеет вид, который представляет
рисунок 5.2.

<Файловый_тип>::=

File Of <Тип>

Text

Рисунок 5.2 – Синтаксическая диаграмма


задания фалового типа

Над значениями файлового типа не определены никакие операции (даже


присваивание файловой переменной значения другой файловой переменной).
Все операции могут производиться лишь с элементами файлов.
Множество операций над элементами файла определяется типом элементов.
Для доступа к отдельным элементам файлов в Паскале существуют
специальные стандартные процедуры и функции. Их называют процедурами и
функциями ввода-вывода. Обращение к ним осуществляется обычным
образом.
Для удобства описания действия этих процедур используется понятие
«окно файла» («окно», текущая позиция файла, указатель файла). Окно
определяет позицию доступа, т.е. тот элемент файла, который доступен для
чтения или для записи.
Позиция файла, следующая за последним элементом файла (или первая
позиция пустого файла) помечается специальным маркером конца файла
(Ctrl - Z при создании текстового файла с клавиатуры). С помощью этого
маркера определяется конец файла.

80
5.2. Процедура Assign

Любым другим процедурам ввода-вывода предшествует процедура


Assign.
Формат ее заголовка:

Assign (Var F; Name: String)

Здесь F – имя файловой переменной любого типа; Name – выражение


строкового типа.
Назначение процедуры – организует связь между конкретным
физическим файлом на внешнем устройстве (конкретным набором данных) и
файловой переменной программы (логическим файлом) F.
Имя конкретного набора данных определяется переменной Name. Name –
это полное имя физического файла. В общем случае оно имеет вид:

<Диск>:\<Имя_каталога>\…\<Имя_каталога>\<Имя_файла>

<Диск> задается символом от A до Z (символ логического устройства).


Если он опущен, то подразумевается логическое устройство, принятое по
умолчанию.
\<Имя_каталога>\…\<Имя_каталога>\ - это путь через подкаталоги к
фактическому имени файла. Если они опущены, то считается, что файл
находится в текущем каталоге.
<Имя_файла> - фактическое имя файла. Оно может иметь максимально 8
символов. Затем может идти уточнение (тип файла) – максимально 3 символа,
отделенное от имени точкой.
Например, можно определить такие имена файлов:

Rez Rez.pas Rez.exe Rez.txt Rez.dat

Уточнение помогает программисту, пользователю, системе


программирования или операционной системе работать с файлами.
Пример полного имени файла:

A:\Katalog1\Katalog2\Rez.pas

Максимальная длина полного имени файла – 79 символов.


Чтобы связать файловую переменную (логический файл) F с физическим
файлом Rez.dat нужно записать процедуру Assign со следующими параметрами:

Assign (F, ‘Rez.dat’);

81
или организовать связь в диалоге:

Var
St: String;
...
Begin
...
Writeln (‘Введите имя файла’);
Readln (St);
Assign (F, St);

Вместо имени физического файла в качестве параметра Name в списке


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

1) Con
Con – устройство консоли (при вводе – это клавиатура, при выводе –
экран дисплея).
Например, процедура

Assign (F, ‘Con’);

означает ввод в переменную F с клавиатуры или вывод из F на экран.


По умолчанию стандартные текстовые файлы Input и Output связаны с
консолью, что соответствует следующему фрагменту программы:

Assign (Input, ‘Con’);


Assign (Output, ‘Con’);

2) Lpt1, Lpt2, Lpt3


Lpt1, Lpt2, Lpt3 – устройства печати. Если подключено одно устройство
печати, то используется либо имя Lpt1 либо Prn:

Assign (F, ‘Prn’);


Assign (Output, ‘Prn’);

С данными логическими устройствами может использоваться только имя


выходного файла.

82
3) Com1, Com2
Com1, Com2 – устойства последовательного ввода-вывода, используемые
для обмена данными между компьютерами. Вместо Com1 может быть
использовано имя ‘Aux’.

4) Nul
Nul – нулевое устройство. Для него при выводе не осуществляется
никаких действий. При попытке чтения возникает ситуация конца файла.

5) Crt
Crt – устройство текстового ввода-вывода. Аналогично устройству Con,
но имеет ряд дополнительных функций управления экраном (например,
установка цветов, указание места на экране для вывода и т.п.). Crt не
поддерживается операционной системой.

6) ‘’
‘’ – использование пустой строки вместо имени Name. В этом случае
файловая переменная F связывается с Con (по аналогии с пунктом а)).
Например,

Assign (F, ‘’);

Процедура Assign всегда предшествует другим процедурам ввода-вывода.


Ее нельзя применять к уже открытому файлу.

5.3. Файлы с типом

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

Пример 5.1.
Примеры объявления файлов с типом.

Type
Zap = Record
I: Integer;
R: Real
End;
Var

83
F1: File Of Real;
F2: File Of Char;
F3: File Of String[50];
F4: File Of Zap;
F5: File Of Integer;

Здесь F1 - F5 – это файловые переменные.

Для работы с типизованными файлами существуют следующие


процедуры и функции ввода-вывода:

1) Процедура Assign
Процедура Assign – связывает файловую переменную с внешним файлом
на диске. Описана в п.5.2.

2) Процедура Rewrite (F)


Процедура Rewrite (F) – создает и открывает новый файл F.
Окно (позиция доступа) устанавливается на первую позицию файла.
Если файл был ранее открыт, то он предварительно закрывается, а затем
повторно открывается.
Если ранее в файл были записаны какие-либо элементы, они становятся
недоступными.
Результат выполнения процедуры Rewrite(F) изображает рисунок 5.3.

Окно

Рисунок 5.3 – Результат выполнения процедуры Rewrite(F)

Перед использованием процедуры Rewrite файл F должен быть связан с


внешним файлом (набором данных) процедурой Assign.
Таким образом, процедура Rewrite создает новый внешний файл с
именем, присвоенным переменной F процедурой Assign.

84
3) Процедура Write (F, V1 [, V2, … , VN])

Процедура Write (F, V1 [, V2, … , VN]) – записывает в ту позицию файла,


на которую указывает окно, очередной элемент файла, равный значению
переменной Vi. После записи переменной Vi окно сдвигается на следующую
позицию файла. Если окно достигает конца файла, файл расширяется.
Результат выполнения процедуры Write (F, V1, V2) изображает рисунок
5.4.

F После записи первого


F1
элемента

Окно

F F1 F2 После записи второго элемента

Окно

Рисунок 5.4 – Результат выполнения процедуры Write (F, V1, V2)

Перед первым выполнением в программе процедуры Write файл


обязательно должен быть открыт.

Пример 5.2.
Пример создания файла.

Var
F1: File Of Char;
X: Char;
Begin
Assign (F1, ‘Newfile’);
Rewrite (F1);
For I:=1 To 100 Do
Begin
<Операторы программы>
Write (F1, X) {X – переменная, получаемая при

85
выполнении программы}
End;
...

4) Процедура Reset (F)

Процедура Reset (F) – открывает существующий файлF.


Фактически при этом открывается внешний файл с именем, присвоенным
переменной F процедурой Assign. Если файл с данным именем не существует,
возникает сообщение об ошибке.
Окно файла устанавливается на первую позицию файла. Если файл был
предварительно открыт, то он закрывается и повторно открывается.
Процедура Reset(F) может быть применена к файлу любое количество
раз. При выполнении этой процедуры содержимое файла не изменяется.
Результат выполнения процедуры Reset(F) изображает рисунок 5.5.

F F1 F2 F3 …

Окно

Рисунок 5.5 – Результат выполнения процедуры Reset(F)

5) Процедура Read (F, V [, V2, …, VN])

Процедура Read (F, V [, V2, …, VN]) – обеспечивает чтение текущего


элемента файла F (того элемента, на который указывает окно) в переменную Vi
и передвигает окно на следующую позицию файла.
Чтение из файла с помощью процедуры Read можно производить только
для предварительно открытого файла.
Файлы с типом всегда допускают как чтение, так и запись,
независимо от того, были они открыты с помощью процедуры Reset или
Rewrite.
Таким образом, доступна только та компонента файла, которая
определена положением окна. Если необходима компонента, которая
предшествует окну, то при последовательном доступе нужно установить окно
на начало файла с помощью процедуры Reset, а затем, последовательно

86
перебирая компоненты с помощью процедуры Read, дойти до нужной
компоненты.
Результат выполнения процедуры Read (F,V1,V2) изображает рисунок 5.6.

F F1 F2 F3 …

После чтения первого


Окно элемента

V1 F1

F F1 F2 F3 …

После чтения второго


Окно элемента

V2 F2

Рисунок 5.6 - Результат выполнения процедуры Read (F, V1, V2)

6) Функция Eof(F)

Функция Eof(F) (End Of File) – служит для определения факта выхода при
чтении за пределы файла.
Функция Eof возвращает значение признака конца файла. Если достигнут
конец файла F (окно указывает на маркер конца файла – позицию, следующую
за последней компонентой файла), или если файл пустой, то значение функции
Eof равно True. В противном случае функция Eof возвращает значение False.
Если значение функции Eof равно True, то использование процедуры
Read недопустимо.
Если в заголовке функции Eof опущено имя файла, то предполагается
файл Input. Например,

L := Eof;

Здесь L – переменная типа Boolean.


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

87
используется логическое выражение Not Eof(F), а в теле цикла ведется
обработка компонент файла F.

Пример 5.3.
Чтение из файла. Пусть имеется набор данных Old.

Var
F2: File Of Char;
X: Char;
Begin
Assign (F2, ‘Old’);
Reset (F2);
While Not Eof (F2) Do
Begin
Read (F2, X);
<Операторы обработки переменной Х>
End
...

7) Процедура Seek (F, N)


Процедура Seek (F, N) – осуществляет прямой доступ к элементам файла
F.
Здесь N – номер элемента файла. Элементы файла нумеруются
последовательно, начиная с нуля.
N может быть положительным целым выражением типа Longint.
Процедура Seek позиционирует файл на указанный номер элемента, то
есть устанавливает окно файла на указанный элемент.
Файл должен быть предварительно открыт.
Процедура Seek не производит операций ввода-вывода. Следующий за
Seek вызов процедуры Read или Write оперирует со следующими по порядку
элементами файла.
Текстовые файлы процедурой Seek не обрабатываются.

Пример 5.4.
Запись в файл F2 элементов файла F1, начиная с номера 100 (фактически
это 101-ый элемент файла F1).

88
Var
C: Char;
F1, F2: File Of Char;
Begin
Assign (F1, ‘Old’);
Assign (F2, ‘New’);
Reset (F1);
Rewrite (F2);
Seek (F1, 100);
While Not Eof (F1) Do
Begin
Read (F1, C);
Write (F2, C);
End;

8) Функция Filepos (F)


Функция Filepos (F) – возвращает номер текущей позиции (окна) файла F.
Если окно установлено на начало файла, то функция возвращает значение 0.
Если окно установлено на конец файла (Eof(F) = True) , то функция возвращает
значение, равное размеру файла (то есть результату функции Filesize (F)).
Тип результата функции Filepos – Longint.
Функция не может использоваться для текстовых файлов.
Файл должен быть предварительно открыт.

9) Функция Filesize(F)
Функция Filesize(F) – возвращает текущий размер файла (число элементов
в файле). Тип результата – Longint. Для пустого файла возвращается значение 0.
Файл должен быть предварительно открыт.
Функция не может быть использована для текстовых файлов.

Пример 5.5.
Расширение файла F (добавление элементов в конец файла).

...
Seek (F, Filesize (F));
Write (F, C);
...

89
В данном примере процедура Seek помещает окно файла за последним
элементом файла (функция Filesize возвращает количество элементов, но так
как они нумеруются с нуля, то значение функции Filesize совпадает с номером
следующей компоненты после последней).

10) Процедура Close (F)


Процедура Close (F) – предназначена для закрытия открытого файла F.
Для внешнего файла, связанного с файловой переменной F, полностью
выполняется его обновление (остатки данных из буфера ввода-вывода
заносятся в файл). Затем файл закрывается и может быть повторно
использован.

Пример 5.6.
Определение размера файла. Закрытие файла.

Assign (F, ‘Old’);
Reset (F);
Write (‘Размер файла Old ’, Filesize (F)); {Вывод на экран размера файла}
Close (F);

5.4. Текстовые файлы

Текстовый файл представляет собой последовательность символов.


Однако он не эквивалентен файлу типа

T = File Of Char.

Файл типа T – это единая последовательность символов.


Особенность текстовых файлов – то, что содержащиеся в них символы
разбиваются на строки. Строки могут быть различной длины (и пустые). В
конце каждой строки помещается специальный управляющий символ – маркер
конца строки. Обычно это два управляющих символа: возрат каретки (#13 или
^M – международное обозначение CR) и перевод строки (#10 или ^J –
международное обозначение LF).
С наличием этого маркера связана логическая функция Eoln (End Of Line).

90
Функция Eoln(F) принимает значение True, если окно (текущая позиция
доступа) установлено на маркер конца строки, и False – в противном случае.
Текстовые файлы – это переменные типа Text.
Тип Text относится к предопределенным структурным типам.
Переменные типа Text описываются c помощью указания имени типа:

Var
X, Y: Text;

Стандартные файлы Input и Output являются текстовыми файлами. Эти


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

Assign (Input, ’’);


Assign (Output, ’’);
Reset (Input, ’’);
Rewrite (Output, ’’);

Для работы с текстовыми файлами определены следующие процедуры и


функции ввода-вывода.

1) Процедура Assign (F, Name)


Процедура Assign (F, Name) – связывает файловую переменную с
внешним файлом на диске. Описана в подразд. 5.2.

2) Процедура AssignСrt(F)
Процедура AssignСrt(F) – связывает текстовые файлы с Crt (с дисплеем).
Процедура аналогична процедуре Assign. Однако второй параметр Name в
ней не используется.
Данная процедура определена в модуле Crt, который реализует более
быстрый ввод-вывод по сравнению со стандартным вводом-выводом и с
большими функциональными возможностями.

Пример 5.7.
Вывод текстового файла F на принтер или экран.

Uses Crt;
Var
F: Text;
K: (P, C); {К –признак вывода, P – принтер, С – Crt}

91
Begin

If K = P Then
Assign (F, ’Prn’)
Else
Assigncrt (F);

3) Процедура Append (F)


Процедура Append (F) – открывает существующий текстовый файл для
добавления. Предварительно файл F должен быть связан с внешним файлом
процедурой Assign.
Если не существует внешнего файла с указанным именем, то в результате
выполнения процедуры Append возникает сообщение об ошибке ввода-вывода.
Если файл F уже был открыт, необходимо его предварительно закрыть.
В результате выполнения процедуры Append текущая позиция (окно)
файла устанавливается в конец файла, на место маркера конца файла (#26 – код
ASCII 26 или Ctrl-Z).
После вызова процедуры Append(F) файл F становится доступным
только для записи, а значение функции Eof(F) всегда будет равно True.
Процедура Append определена только для текстовых файлов.

4) Процедура Rewrite (F)


Процедура Rewrite (F) – открытие нового файла F. Данная процедура
описана выше для файлов с типом (см.подразд. 5.3).
Отличие для текстовых файлов: файл F открывается только для записи
(если текстовый файл F был открыт процедурой Rewrite, то из него читать
нельзя, в него можно только писать).
После вызова процедуры Rewrite(F) значение функции Eof(F) всегда
равно True.

5) Процедура Reset (F)


Процедура Reset (F) – открытие существующего файла F. Данная
процедура описана выше для файлов с типом (см.подразд. 5.3).
Отличие для текстовых файлов: файл открывается только для чтения
(из него можно только читать, писать в него нельзя).

92
6) Процедура Read ([F,] V1 [, V2, …, VN])
Процедура Read ([F, ] V1 [, V2, …, VN]) – считывает одно или несколько
значений из текстового файла F в одну или несколько переменных Vi. Данная
процедура описана выше для файлов с типом (см. подразд. 5.3).
Отличия при работе с текстовыми файлами:
• файл должен быть открыт для чтения (процедурой Reset);
• первый параметр может быть опущен (например, Read(X, Y)), в этом
случае подразумевается стандартный входной текстовый файл Input;
• при выполнении процедуры Read осуществляется преобразование
очередного элемента файла из символьного представления к типу переменной
Vi. Vi может иметь символьный, целочисленные, вещественные, строковый тип,
тип массива символов или тип диапазона данных типов.
• Если переменная V имеет тип Char, то из файла F в V считывается
очередной символ, включая символы-разделители «Конец файла»
(значение Chr(26) – #26), и «Конец строки» (Chr(13) – #13, Chr(10) –
#10). Следующая процедура Read начинается со следующего
символа в файле.
• Если переменная V имеет арифметический тип, то пропускаются
пробелы, символы табуляции, маркеры конца строки до появления
цифровой комбинации. Считывание прекращается при достижении
пробела, символа табуляции, маркера конца строки или маркера
конца файла. Следующее чтение начинается с вышеперечисленных
символов. Если в цифровой комбинации встретились запрещенные
символы (например, буква вместо цифры), то возникает сообщение
об ошибке ввода-вывода.
• Если переменная V имеет тип String, то в нее передается столько
символов, какова длина переменной V при объявлении (если длина
V меньше длины текущей строки файла) или строка файла до
маркера конца строки или маркера конца файла (если длина V
больше длины строки файла). Маркер в строку не заносится. В
первом случае по следующей операции чтения в переменную V
будет передана очередная последовательность символов из первой
строки файла в соответствии с длиной переменной V. Во втором
случае следующая операция считывания начинается маркером
конца строки, завершающим предыдущую строку. Но процедура
Read не осуществляет переход на следующую строку файла после
чтения. Таким образом, нельзя использовать последовательные
вызовы процедуры Read для чтения последовательных строк, так
как никогда не осуществится переход из первой строки во вторую.
Первый вызов процедуры Read считает первый элемент,
последующие вызовы будут возвращать строку нулевой длины.

93
Поэтому при чтении из текстового файла в переменные типа String
необходимо использовать не процедуру Read, а процедуру Readln.

7) Процедура Readln [([F] [,] [V1, V2, …, VN])]


Процедура Readln [([F, ] [V1, V2, …, VN])] – выполняет процедуру Read, а
затем переходит в начало следующей строки файла. Таким образом, после
прочтения в переменные Vi данных из файла пропускаются все оставшиеся
символы в текущей строке и маркер конца строки.
Если при вызове процедуры отсутствует список переменных V
(Readln(F)), то происходит переход к следующей строке (если она существует)
или к концу файла F.
Если имя файла в процедуре Readln опущено, то подразумевается
стандартный входной текстовый файл Input.
Например,

Readln (X, Y, Z);


Readln;

Процедура Readln (F, V) определена только для текстовых файлов.

8) Процедура Write ([F,] E1 [, E2, …, EN])


Процедура Write([F,] E1 [, E2, … , EN]) – записывает одно или несколько
значений Ei в файл F. В общем случае E может быть выражением.
Процедура описана для файлов с типом (см. подразд. 5.3).
Отличия для текстовых файлов:
• Файл должен быть открыт процедурой Rewrite или Append.
• Если первый параметр (F) опущен, то подразумевается стандартный
выходной текстовый файл Output. Например, Write(A, B).
• При выполнении процедуры Write осуществляется преобразование
выводимого значения из типа выражения E к символьному типу. Возможны
следующие типы Ei: символьный, арифметические (целочисленные и
вещественные), строковый, массив исмволов, логический и их диапазоны.
• Для данных типа Char и String выводится непосредственно их
значение.
• Если Ei представляет собой арифметическое значение, то перед
выводом в текстовый файл оно предварительно преобразуется из
внутреннего значения в десятичную систему счисления, а затем
представляется в коде ASCII (по байту на десятичную цифру).
• Если Ei имеет тип Boolean, то выводится строка True или False.
• Если не указан формат вывода, то под вывод всех типов Ei отводится
столько символов, сколько минимально необходимо. Для вывода вещественных

94
типов отводится 24 позиции, причем вещественное число будет выведено в
виде мантиссы и порядка.
• Значения Ei могут сопровождаться указанием формата вывода:

E: L1[: L2] .

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


Подробно данные возможности описаны в подразд. 2.2.

9) Процедура Writeln([F,][E1,E2, …,EN])


Процедура Writeln([F,][E1,E2, …,EN]) – выполняет процедуру Write, и
затем записывает маркер конца строки в файл F.
Если отсутствует список выражений Ei, то записывается только маркер
конца строки.
Если опущено имя файла, то подразумевается файл Output. Например,

Writeln(F);
Writeln(A, B, C);
Writeln;

Процедура определена только для текстовых файлов.

10) Процедура Close(F)


Процедура Close(F) – закрывает открытый файл F. Рассмотрена ранее при
рассмотрении файлов с типом (см. подразд. 5.3). Особенностей для текстовых
файлов нет.

Пример 5.8.
Последовательность использования процедур при работе с текстовым
файлом.

Var
F: Text;

Begin
Assign (F, ’Data.txt’);
Rewrite (F); {Открытие файла F только для записи}

Write (F, A); {Запись в файл F значения некоторой переменной А,
полученной в ходе вычислений}

95

Reset (F); {Открытие файла F только для чтения. Перед этим
файл не обязательно должен быть закрыт. Это
выполняется автоматически процедурой Reset}

Read (F, B); {Чтение из файла F очередного значения в некоторую
переменную В}

Close (F); {Обязательное явное закрытие файла F перед
использованием процедуры Append. Связь с внешним
файлом при этом сохраняется. Поэтому повторное
использование процедуры Assign не нужно}

Append (F); {Открытие файла F для дополнения}

Write (F, C); {Запись в конец файла F значения некоторой
переменной С, полученной в ходе вычислений}

Close (F); {Закрытие файла F в конце работы с ним}

End.

11) Процедура SetTextBuf (F, Buf [, Size])


Процедура SetTextBuf (F, Buf [, Size]) – определяет буфер для текстового
файла.
Процедуру следует вызывать после процедуры Assign, но до других
процедур ввода-вывода. Здесь F – имя текстового файла, Buf – любая
переменная (в качестве формального параметра используется параметр-
переменная без типа), Size – необязательное выражение типа Word.
Обмен информацией между программой и внешним набором данных
осущетвляется через буфер ввода-вывода. Это участок оперативной памяти.
Размер стандартного буфера ввода-вывода, принятый по умолчанию, – 128
байт. Каждому открытому файлу назначается свой буфер.
Процедуры Write и Writeln записывают очередные элементы файла
последовательно в буфер. После того, как буфер будет полностью заполнен,
произойдет физическая запись содержимого буфера во внешний файл. После
этого буфер освобождается для приема следующей порции информации.
Аналогично при чтении. Из внешнего файла одновременно считывается
количество элементов, помещающееся в буфер. Процедуры Read и Readln
читают элементы последовательно из буфера.

96
Использование буфера ввода-вывода позволяет существенно повысить
скорость обмена информацией с внешними файлами (например, за счет
уменьшения времени перемещения магнитных головок в дисководах).
Операции обмена данными через буфер ввода-вывода осуществляет
специальный обработчик файлов (для каждого файла имеется свой обработчик
файлов, он назначается при открытии файла).
Для большинства прикладных программ размер стандартного буфера
ввода-вывода (128 байт) оказывается достаточным. Однако, если в программе
имеется большое количество операций ввода-вывода, то более эффективным
оказывается использование буфера большего размера, так как это позволяет
сократить время обращения к внешним наборам данных.
Процедура SetTextBuf назначает текстовому файлу F свой буфер ввода-
вывода, определяемый параметром Buf. Размер буфера в байтах определяется
параметром Size.
Если параметр Size опущен, то по умолчанию размер буфера принимается
равным Sizeof (Buf), то есть вся область памяти, занимаемая параметром Buf,
используется как буфер. Если параметр Size не опущен, он не должен
превышать размеры переменной Buf.
Процедуру SetTextBuf нельзя применять к открытому файлу (она должна
применяться после процедуры Assign и до процедур Reset, Rewrite или Append).
Процедура SetTextBuf определена только для текстовых файлов.

Пример 5.9.
Назначение буфера ввода-вывода текстовому файлу F. Передача данных
из файла F в стандартный текстовый файл Output

Var
F: Text;
C: Char;
Buf: Array [1..10240] Of Char; {Буфер размером 10 килобайт}
Begin
Assign (F, ’A:\MET\Metod.txt’);
SetTextBuf (F, Buf); {Назначение буфера ввода-вывода Buf
текстовому файлу F. Размер буфера равен
размеру переменной Buf – 10240 байт }
Reset (F);
While Not Eof (F) Do {Цикл чтения из файла F и записи в файл
Output}
Begin
Read (F, C);
Writeln (C);
End;
97

12) Процедура Flush (F)


Процедура Flush (F) – очищает буфер текстового файла, открытого для
вывода процедурой Rewrite или Append. По данной процедуре информация из
буфера, независимо от степени его заполнения (заполнен он полностью или
частично), записывается во внешний файл.
Данная процедура используется редко в прикладных программах – для
очень важных результатов, если нужно подтверждение о физической записи во
внешний файл. Процедура определена только для текстовых файлов.

13) Функция Eof (F)


Функция Eof (F) – конец файла.
Рассмотрена ранее при рассмотрении файлов с типом (см.подразд. 5.3).

14) Функция Eoln (F)


Функция Eoln (F) – конец строки.
Рассмотрена в начале данного подраздела.

15) Функция Seekeof (F)


Функция Seekeof (F) – устанавливает файл F в состояние “конец файла”.
Аналогична функции Eof, но пропускает все пробелы, метки табуляции и
маркеры конца строки. Обычно может использоваться при считывании
числовых значений из текстового файла.
Определена только для текстовых файлов. Файл должен быть открыт.

16) Функция Seekeoln (F)


Функция Seekeoln (F) – устанавливает файл F в состояние “конец строки”.
Аналогична функции Eoln, но пропускает все пробелы и метки
табуляции. Используется при считывании существующего текстового файла
числовых данных.
Определена только для текстовых файлов. Файл должен быть открыт.

98
5.5. Сравнительная характеристика
представления информации
в файлах с типом и текстовых
файлах

Очевидно, что одна и та же информация может быть записана как в


текстовый файл, так и в файл с типом.
Рассмотрим внутреннюю структуру представления информации в этих
файлах.

I. Представление числовой информации.

а) В текстовом файле.
Пусть в текстовом файле записана последовательность чисел

8192, 2048, …

Внутреннее представление данной последовательности в текстовом файле


представляет рисунок 5.7.
Как видно из данного рисунка, числа представлены в коде обмена
информацией ASCII (в распакованном формате в коде 8421). Под каждую
цифру десятичного кода числа отводится один байт. Числа отделяются друг от
друга хотя бы одним пробелом.
Таким образом, для хранения последовательности из двух первых чисел
8192, 2048 необходимо не менее 10 байтов.
б) В типизованном файле.
Та же последовательность чисел

8192, 2048, …

в файле типа File Of Integer имеет внутреннее представление, которое


иллюстрирует рисунок 5.8.
Как видно из данного рисунка, в файле типа File Of Integer числа
представлены в двоичном коде в формате Integer. Под каждое число отводится
два байта. Разделители между числами отсутствуют.
Таким образом, для хранения последовательности из двух первых чисел
8192, 2048 необходимо 4 байта.

99
0011 1000 0011 0001 0011 1001 0011 0010 0010 0000

Зона (старшая Цифра (младшая


Байт
тетрада тетрада байта) в
байта) коде 8421

8192 = 213 Пробел

0011 0010 0011 0000 0011 0100 0011 1000 0010 0000 …

2048 = 211 Пробел

Следующие
числа

Рисунок 5.7 – Представление числовой информации в текстовом файле

Код пробела Код пустого Код управляющего Код пустого


#32 символа #0 символа #8◘ символа #0

0010 0000 0000 0000 0000 1000 0000 0000

8192 = 213 2048 = 211

Рисунок 5.8 – Представление числовой информации


в файле типа File Of Integer

100
При попытке вывода данных байтов файла на экран выведется пробел (не
изображается), пустой символ (не изображается), символ кода ASCII с номером
8 (изображается в виде ◘), пустой символ.
Таким образом, при работе с числовой информацией, если ее не нужно
выводить на экран или печать (это осуществляют лишь текстовые файлы),
эффективнее использовать файлы с типом:
1) работа с ними осуществляется быстрее за счет следующих факторов:
а) отсутствует преобразование информации, она в файле
представлена так же, как в памяти;
б) при работе с файлами с типом возможен режим прямого доступа;
в) меньше операций физического ввода-вывода за счет меньшего
размера файла;
2) они занимают меньше места (в текстовом файле каждая цифра числа
занимает байт, разделители – не менее одного пробела между числами, маркер
конца строки – два управляющих символа #13#10; в файле типа File Of Integer
все число занимает два байта, разделители между числами не нужны).

II. Представление текстовой информации.

а) В текстовом файле.
Пусть в текстовом файле записан текст «ВАШ ОТВЕТ НЕВЕРЕН». Пусть
данный текст разбит на строки, которые изображает рисунок 5.9.

В А Ш #13 #10

Конец строки

О Т В Е Т #13 #10

Н Е В Е Р Е Н #13 #10

Рисунок 5.9 – Представление текстовой информации


в текстовом файле

101
Каждая строка представляет собой одно слово, имеет текущую длину и
заканчивается маркером конца строки. Каждый символ текста представлен в
коде ASCII и занимает один байт.
Таким образом, для представления данной информации в текстовом
файле необходим 21 байт.
б) В файле of string [7].
Тот же текст в файле типа File Of String[7] имеет внутреннее
представление, которое представляет рисунок 5.10.

#3 В А Ш #0 #0 #0 #0

#5 О Т В Е Т #0 #0

#7 Н Е В Е Р Е Н

0-ой байт – длина Остальные байты


строки в формате Byte – в формате Char
(значения 0-255 в (в коде ASCII)
двоичном коде)

Рисунок 5.10 – Представление текстовой информации


в файле типа File Of String [7]

В файле с типом строки имеют постоянную (максимальную) длину.


Пустой символ (символ #0 в кодовой таблице) дополняет строки текущей
длины до максимальной длины.
Следовательно, для представления данной информации в файле типа File
Of String [7] необходимо 24 байта.
Сравнение файлов типа Text и File Of String показывает, что:
а) меньше места в общем случае занимает текстовый файл (так как в нем
используются строки текущей длины);
б) большую скорость работы обеспечивает File Of String, так как для
типизованных файлов имеется возможность работы в режиме прямого доступа,

102
работы одновременно в режиме записи-чтения, не нужно отслеживать
управляющие символы #13#10 (маркер конца строки).
Таким образом, для хранения текстовой информации выбирать файл типа
Text или File Of String следует в каждом конкретном случае, исходя из
особенностей задачи.

5.6. Файлы без типа

Файл без типа состоит из компонент одинакового размера. Структура


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

Var
F: File;

Назначение файлов без типа – максимально повысить скорость обмена


информацией с внешними наборами данных. Скорость обмена повышается за
счет следующих факторов:
1) В файлах без типа отсутствует преобразование типа компонент;
2) Не выполняется поиск управляющих символов (типа конец строки);
3) В файлах без типа, как и в файлах с типом, возможна организация
метода прямого доступа. Поэтому в них возможно одновременное
использование операций чтения и записи независимо от того, какой
процедурой (Reset или Rewrite) они были открыты.
4) Обмен информацией с внешними наборами данных может быть
осуществлен большими блоками.
Последний фактор является основным с точки зрения повышения
скорости обмена.
Для файлов без типа определены те же процедуры и функции, что и для
файлов с типом, за исключением процедур Read и Write. Определены
процедуры Assign, Rewrite, Reset, Seek, Close, функции Eof, Filesize, Filepos.

1) Процедуры Reset и Rewrite


Процедуры Reset и Rewrite имеют следующие особенности.
При вызове процедур Reset и Rewrite может быть использовано два
параметра. Формат вызова данных процедур имеет вид:

103
Rewrite (F [,Recsize]);
Reset (F [,Recsize]);

Recsize – это необязательное выражение типа Word, определяющее размер


записи (в байтах), используемый при передаче данных.
Например,

Rewrite (F, 1);


Reset (F, 1);

В данном случае второй параметр определяет длину записи в 1 байт.


Если параметр Recsize опущен, то подразумевается длина записи, равная
128 байт. Это физически минимально возможный объем информации для
обмена.

2) Процедура Blockread
Вместо процедур Read и Write в файлах без типа используются
процедуры Blockread и Blockwrite.
Процедура Blockread имеет следующий формат вызова:

Blockread (F, Buf, Count [, Result]);

Здесь F – имя файловой переменной без типа, Buf – любая переменная (в


качестве формального параметра используется параметр-переменная без типа),
Count – выражение типа Word, определяющее количество считываемых
записей.
Процедура Blockread считывает блок информации длиной в Count или
меньше записей в область памяти, занимаемую переменной Buf (начиная с ее
первого байта). Действительное число считанных полных записей (оно не
может превышать значение Count) заносится в параметр Result (если он есть).
Если Result меньше Count, то это значит, что конец файла достигнут до
полного окончания передачи. В этом случае, если параметр Result отсутствует,
возникает сообщение об ошибке ввода-вывода (поэтому лучше параметр Result
использовать).
В результате выполнения процедуры Blockread “окно” файла (текущая
позиция файла) передвинется на число записей, равное значению Result.
Объем блока информации, считываемый процедурой Blockread в
переменную Buf, занимает

Result* Recsize

байтов. Здесь Recsize – размер записи, определенный при открытии файла.


Размер блока информации не должен превышать 64К байта (это размер

104
сегмента данных). В противном случае возникнет сообщение об ошибке ввода-
вывода.

3) Процедура Blockwrite
Процедура Blockwrite имеет следующий формат вызова:

Blockwrite (F, Buf, Count [, Result])

Назначение параметров – то же, что и в предыдущей про