Открыть Электронные книги
Категории
Открыть Аудиокниги
Категории
Открыть Журналы
Категории
Открыть Документы
Категории
по дисциплине
Основы алгоритмизации
и программирования
Часть 2
Минск 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. ПОДПРОГРАММЫ
6
Встроенные (стандартные) процедуры и функции являются частью
языка и используются в программе без предварительного описания в разделе
объявлений блока.
Процедуры и функции пользователя организуются самим
программистом и являются локальными блоками. Их предварительное
описание обязательно.
1.2. Процедуры
<Объявление_процедуры> ::=
<Заголовок_процедуры> ;
<Тело_процедуры> ;
7
<Заголовок_процедуры> ::=
Procedure <Идентификатор>
<Список_формальных_параметров>
<Список_формальных_параметров>::=
( <Формальный_параметр> )
8
<Вызов_процедуры >::=
<Идентификатор_процедуры>
( <Аргумент> )
Пример 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.
10
Синтаксическая диаграмма задания формальных параметров в заголовке
процедуры при ее объявлении имеет вид, который иллюстрируют рисунок 1.5.
<Формальный_параметр>::=
<Идентификатор>
: <Тип_параметра>
Var
Const
1.2.5. Параметры-значения
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.
12
D, поскольку данный результат должен быть передан в вызывающую
программу.
1.2.6. Параметры-переменные
Пример 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.
1.2.7. Параметры-константы
14
СПОСОБ 1.
Внутри подпрограммы объявляется локальная переменная нужного типа,
налагаемая на переданный фактический параметр. Для описания такой
переменной используется зарезервированное слово Absolute.
Синтаксическая диаграмма описания налагаемой переменной имеет вид,
который представляет рисунок 1.6.
Этот способ применения нетипизованных параметров-переменных
иллюстрирует пример 1.4.
<Имя_переменной> : <Тип_переменной>
Absolute <Имя_параметра_без_типа>
Пример 1.4.
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.
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 {Вызывающая программа}
…
Bytes (Source)
Bytes (Dеst).
20
Описание процедурного типа вводится в разделе Туре. Синтаксис
описания совпадает с заголовком подпрограммы (процедуры или функции),
однако имя подпрограммы в описании опускается.
Например:
Здесь объявлены два процедурных типа — Pr, Pr1. В типе Pr1 в качестве
параметра используется переменная Р процедурного типа Pr.
Имена параметров в описании процедурного типа играют чисто
иллюстративную роль. Никакого влияния на это описание данные имена не
оказывают.
Процедурные типы допускается использовать в любом контексте. В
частности, могут быть введены переменные этого типа.
Например, с учетом вышеприведенных описаний типа вводятся две
переменные P и P1 процедурного типа:
Var
Р: Pr;
P1: Pr1;
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.
24
В зависимости от конкретного значения процедурной переменной
Proced будет вызвана к исполнению либо процедура Log, либо процедура
Step. Результаты работы вызванной процедуры помещаются в массив Rez,
являющийся локальной переменной процедуры Vizov, и выводятся на печать.
Type
Mas = Array [1..10] Of Integer;
Diap = 1..10;
Procedure X (Y: Mas; I: Diap);
25
1.2.11. Принцип локализации имен
Пример 1.7.
Использование одинаковых имен в различных областях действия.
26
В данной программе используются идентификаторы Q, Y, C, X, Z, причем
идентификаторы C, X используются как локальные и как глобальные
переменные. Справа изображены области действия используемых имен. Здесь
через CГ, XГ обозначены глобальные переменные C, X, через CЛ, XЛ – локальные
переменные C, X.
В программе имеется два оператора вывода Writeln. Первым из них будет
выполнен оператор, находящийся в подпрограмме Al. В результате будут
выведены значения С, X, Y, равные:
1.3. Функции
27
<Заголовок_функции> ::=
Function <Идентификатор>
<Список_формальных_параметров>
: <Идентификатор_типа>
Второе отличие.
Как и процедуры, функции могут изменять значения глобальных
переменных, параметров-переменных и параметров без типа. Но кроме этого
каждая функция вычисляет значение, называемое возвращаемым значением
функции. Данное значение передается в точку вызова функции. Чтобы
установить возвращаемое значение, необходимо присвоить это значение
идентификатору функции. Поэтому в теле функции обязательно должен
присутствовать хотя бы один оператор присваивания, в левой части которого
записано имя описываемой функции без параметров:
<Идентификатор_функции> := <Выражение>
Пример 1.8.
Объявление функции. Функция Simvol определяет наличие искомого
символа в анализируемой строке.
<Вызов_функции> [(Список_фактических_параметров)].
29
допускается использование выражений (например, в правой части оператора
присваивания).
Применительно к примеру 1.8 вызов функции в вызывающей программе
может быть использован, например, так:
Пример 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, поскольку по синтаксису языка
Паскаль в списке вывода могут использоваться значения выражений.
Вместо оператора {*} в тексте программы можно использовать,
например, такой фрагмент:
⎧1 , если n = 0 ;
n! = ⎨
⎩ n ⋅ ( n − 1 )! при n > 0.
Пример 1.10.
Рекурсивная функция. Вычисление n!.
31
Явная рекурсия – это рекурсия, при которой обращение к подпрограмме
содержится в теле самой подпрограммы.
Неявная (взаимная) рекурсия – это рекурсия, при которой обращение к
подпрограмме содержится в теле другой подпрограммы, к которой
производится обращение из данной подпрограммы.
Возможность рекурсивного обращения обеспечивается специальными
средствами, которые при каждом повторном (рекурсивном) вызове подпрограммы
создают новый экземпляр ее памяти, делая временно недоступным прежний
экземпляр памяти (т.е. значения локальных переменных и фактических
параметров, существующих на момент рекурсивного вызова). Все экземпляры
памяти рекурсивной подпрограммы помешаются в стек. Всегда доступен только
текущий экземпляр памяти, созданный последним. При каждом возврате из
рекурсивной подпрограммы текущий экземпляр памяти удаляется из стека и
становится доступным предыдущий экземпляр.
В нерекурсивной подпрограмме механизм выделения памяти в стеке
отсутствует.
Рекурсивность является не свойством подпрограммы, а лишь свойством
ее описания. Ту же подпрограмму можно организовать и без рекурсии.
Например, функцию из предыдущего примера можно реализовать без рекурсии
следующим образом.
Пример 1.11.
Нерекурсивная функция. Вычисление n! по формуле:
n! = 1 ⋅ 2 ⋅ 3 ⋅ ... ⋅ ( n − 1 ) ⋅ n .
32
1.5. Директивы
<Тело_подпрограммы> ::=
Блок
Forward
Interrupt ;
External
Inline
33
должна определяться с помощью определяющего описания. Определяющее
описание - это описание, использующее тот же заголовок подпрограммы, что и
в опережающем описании, но без списка формальных параметров, и в которое
включен блок (разделылокальных описаний и операторов).
Опережающее и определяющее описания представляют собор полное
описание подпрограммы. Подпрограмма считается описанной с помощью
опережающего описания.
Определяющее описание может быть внешним описанием, но не может
быть внутренним описанием или другим опережающим описанием.
Определяющее описание не может содержать директиву Interrupt.
Между опережающим и определяющим описаниями могут описываться
другие подпрограммы, которые могут обращаться к подпрограмме с
опережающим описанием (рисунок 1.9). Таким образом может быть
реализована взаимная (неявная) рекурсия.
Использование процедур со взаимной рекурсией при опережающем
описании одной из процедур иллюстрирует программа, приведенная в примере
1.12.
Опережающее
описание
подпрограммы 1
Подпрограмма 2
Определяющее
описание
подпрограммы 1
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.
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 программы или
использующего его модуля.
37
…
{Заголовки процедур и функций с указанием параметров}
Procedure …
Function …
Implementation {Секция реализации}
<Описание локальных типов, констант, переменных, процедур и функций,
описанных в секции Interface, и внутренних процедур и функций>
Begin {Секция инициализации}
<Операторы>
End.
<Модуль>::=
<Заголовок_модуля> ;
<Интерфейсный_раздел>
<Раздел_реализации>
<Раздел_инициализации> .
38
<Заголовок_модуля>::=
Unit <Имя_модуля>
<Интерфейсный_раздел>::=
Interface
<Предложение_Uses>
<Раздел_констант>
<Раздел_типов>
<Раздел_переменных>
<Раздел_заголовков_процедур_и_функций>
39
Синтаксическая диаграмма раздела заголовков процедур и функций
имеет вид, который изображает рисунок 1.13.
В интерфейсной секции недопустимы заголовки подпрограмм с
директивами Interrupt и Forward. Само описание заголовков процедур и
функций в интерфейсной секции является аналогом опережающего описания
(аналогом директивы Forward).
Если при объявлении типов, данных или подпрограмм используются
типы, введенные в других модулях, то эти модули должны быть перечислены в
директиве Uses сразу после слова Interface. В директиве Uses данной секции
рекомендуется указывать только те модули, которые необходимы именно в
этой секции.
<Раздел_заголовков_процедур_и_функций>::=
<Заголовок_процедуры> ;
<Заголовок_функции>
<Директива_Inline> ;
40
другим модулям. Это скрытая часть модуля.
Описанные в разделе реализации типы, константы, переменные являются
глобальными по отношению к подпрограммам этого раздела, а также
операторам раздела инициализации.
Если в телах подпрограмм или при объявлении типов, переменных в
разделе реализации используются имена, объявленные в других модулях, и эти
модули не попали в предложение Uses раздела Interface, то они перечисляются
в предложении Uses после слова Implementation.
Если в модуле содержатся обращения к внешним процедурам или
функциям, описанным на языке Assembler, то после заголовка такой
подпрограммы вместо ее тела в разделе реализации записывается директива
External. Данная директива говорит о том, что тело процедуры или функции
описано в другом модуле (на языке Assembler, файл имеет расширение obj).
<Раздел_реализации>::=
Implementation
<Предложение_Uses>
<Раздел_меток>
<Раздел_констант>
<Раздел_типов>
<Раздел_переменных>
Раздел_описаний_процедур_и_функций
41
Синтаксическая диаграмма раздела инициализации имеет вид, который
содержит рисунок 1.15.
Синтаксическая диаграмма раздела операторов имеет вид, который
представляет рисунок 1.16.
Если программный модуль использует несколько модулей Unit, то их
части инициализации будут выполняться в порядке их перечисления в
предложении Uses программы до операторов тела программы. Если
операторная часть в секции инициализации отсутствует, то секция состоит из
слова End.
Все разделы модуля являются необязательными, но служебные слова,
начинающие разделы, должны присутствовать обязательно.
<Раздел_инициализации>::=
End
<Раздел_операторов>
<Раздел_операторов>::=
Пример 1.13.
Структура пустого модуля. Модуль содержит только служебные слова,
которые должны присутствовать в Unit обязательно.
42
Unit Hollow; {Пустой модуль}
Interface
Implementation
End.
Пример 1.14.
Абстрактная программа, отражающая правила оформления модуля Unit и
программного модуля. Подчеркнуты служебные слова, которые должны
присутствовать в Unit обязательно.
Destination = Disk.
{$U <Имя_файла>}
44
Uses Dos, Crt, {$U Unit1.pas} U1;
Первая особенность.
Подключение модулей происходит в порядке их перечисления в
предложении Uses: слева направо. В этом же порядке срабатывают разделы
инициализации модулей. Инициализация происходит только при работе
программы. При подключении модуля к модулю инициализация не
выполняется.
Вторая особенность.
Порядок подключения влияет на доступность библиотечных типов,
данных, процедур и функций. Например, имеются два модуля U1 и U2. В
каждом из этих модулей в интерфейсной секции описаны одноименные тип
Ned, переменная D, процедура SetNed. Но они реализованы по-разному.
Если в программе записано предложение использования
Третья особенность.
Решение проблемы закольцованности (циклических ссылок модулей друг
на друга). Например, модуль U1 использует элементы из модуля U2, а модуль
U2 использует элементы из модуля U1. Решение данной проблемы зависит от
того, в каком разделе возникла закольцованность.
Если оба модуля подключают друг друга в разделе реализации:
Unit U1;
Interface
45
Implementation
Uses U2;
…
Unit U2;
Interface
Implementation
Uses U1;
…
Пример 1.15.
Пусть Unit U1 из примера 1.14 в секции реализации подключает модуль
U2. Пусть модуль U2 имеет следующий интерфейсный раздел:
Unit U2;
Inteface
Uses U1;
Procedure Dni (Var Dd: Ned);
…
Unit U3;
Intereface
Type
46
Ned = (Pn, Vt, Sr, Ch, Pt, Sb, Vs);
Implementation
End.
{$I <Имя_файла>}
47
Примеры включения исходных файлов:
48
РАЗДЕЛ 2. ПРОСТЕЙШИЙ ВВОД-
ВЫВОД
Пример 2.1.
Пусть имеется следующий фрагмент программы:
Var X, Y, Z: Integer;
49
...
{*} Readln (X, Y);
{**} Readln (Z);
...
Пусть во входном файле Input имеются следующие значения:
248 −15 4 70 значения 1-й строки
11 значения 2-й строки
X = 248 Y = -15 Z = 11
X = 248 Y = -15 Z = 4.
Особенности ввода:
1) Тип вводимого данного из входного файла должен соответствовать
типу переменной Xi из списка фактических параметров процедур Read, Readln.
2) Если в процедуре ввода в списке переменных присутствует несколько
переменных, то во входном файле они должны отделятся друг от друга
разделителями.
При вводе числовых значений они обычно разделяются пробелами.
Несколько идущих подряд переменных типа Char во входном файле
должны быть записаны без разделителей и без окаймляющих апострофов.
Пример 2.2.
Ввод переменных типа Char.
Var
A, B, C: Char;
-------------------------
Read (A, B, C);
-------------------------
50
End
51
При использовании процедуры Writeln переход на следующую строку
осуществляется после вывода всех элементов из списка ее фактических
параметров.
Процедура Writeln без параметров используется для перехода на новую
строку.
Количество позиций поля, отводимого в строке выходного файла для всех
типов выходных данных, кроме вещественных, равно минимально
необходимому. Для вывода вещественных типов отводится фиксированное
количество позиций (например, для типа Real отводится 17 позиций), причем
вещественное число будет выведено в виде мантиссы и порядка.
Если тип переменной вывода Еi – Boolean, то на печать выводится
значение True или False.
Недостаток процедур вывода с элементами списка вывода вида Еi –
жесткая фиксация выводимых значений по позициям строки.
Для управления выводом по позициям строки используются элементы
вывода вида
Е: L1
или
E: L1: L2
R = L1 – 7.
52
Write (1.2546: 10: 2);
zm m0.m1m2…mREzpp2p1
1 . 2 5
2 позиции
10 позиций
Пример 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 поз.
60 поз.
--------------------------------------------
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. ЗАПИСИ
Komplex запись
Re Im поля
55
Anketa
<Тип_Record>::=
Record
<Список_полей> End
< Список_полей>::=
<Общая_часть> ; <Вариантная_часть> ;
56
Поле записи обозначается идентификатором. К полю обращаются по
имени. Областью действия полей записи является сама запись. Имя каждого
поля внутри записи должно быть уникальным.
<Общая_часть>::=
<Поле> : <Тип>
,
Каждому полю записи дается свое имя и задается тип значения этого
поля.
Пример 3.1.
Объявление записи, структуру которой представляет рисунок 3.1.
Type
Komplex = Record
Re: Real;
Im: Real
End;
или эквивалентно
Type
Komplex = Record
Re, Im: Real;
57
End;
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
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;
Пример 3.3.
Присвоение значений полям переменной D1 (к примеру 3.2).
D1.God := 1970;
D1.Mes := Jn;
D1.Den := 15;
<Диапазон> : ( <Список_полей> )
Диапазон ::=
<Константное_выражение>
.. <Константное_выражение>
Пример 3.4.
Запись с вариантной частью без поля признака.
Var
Z: Record
61
Case Integer Of
1: (I1: 1..10);
2: (J1: Char);
3: (K1: Boolean)
End;
У части 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: ( )
Оператор_With::=
With <Переменная> Do
<Оператор>
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;
64
адреса, до завершения оператора With не отражаются на значении
вычисленного ранее адреса.
Указание списка переменных типа запись в операторе With (см.
предыдущую синтаксическую диаграмму) является сокращенной формой
оператора With. Сокращенная форма оператора:
With Z1 Do
With Z2 Do
With Z3 Do
...
With Zn Do <Оператор>
Пример 3.8.
Применительно к примеру 3.2 для полного устранения необходимости в
составных именах полей (см. пример 3.7) может быть использована следующая
сокращенная форма оператора 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’;
3.5. Константа-запись
66
Синтаксис задания константы-записи иллюстрирует рисунок 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));
68
РАЗДЕЛ 4. МНОЖЕСТВА
Пример 4.1.
Пусть множество может содержать набор элементов 0..7, а его конкретное
значение равно множеству элементов 0, 3, 4. В памяти компьютера оно будет
представлено в виде, который иллюстрирует таблица 4.1.
0-й бит 1-й бит 2-й бит 3-й бит 4-й бит 5-й бит 6-й бит 7-й бит
1 0 0 1 1 0 0 0
69
4.2. Конструктор множества
<Конструктор_множества> ::=
[ <Выражение> ]
.. <Выражение>
Пример 4.2.
Примеры множеств.
70
Конструктор множества – это фактически множественная константа.
Порядок перечисления элементов в множестве не играет роли. Каждый
элемент учитывается только один раз.
Например, множества [1, 2, 3] и [1, 3, 2] – это одно и тоже множество.
<Тип_Set>::=
Set Of <Базовый_скалярный_тип>
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;
Пример 4.4.
Использование множественных выражений. Для переменных,
объявленных в примере 4.3, можно записать:
L1 := [True];
L := L1; {Значение L1 к этому моменту должно быть определено}
I := [1, 3, 5];
Den := [Sub, Vos];
72
Таблица 4.2 – Операции над множествами
= Равно
<> Не равно
Результат операции равен True, если левое
<=
множество является подмножеством правого Boolean
Результат операции равен True, если правое
>=
множество является подмножеством левого
Результат операции равен True, если некоторое
In скалярное значение (левый операнд) является
элементом множества (правый операнд)
Not *) Дополнение множества (одноместная операция)
+ Объединение множеств
Пример 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] – дополнение множества}
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]}
[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.8.
Ввод значения переменной типа множества больших латинских букв из
входного файла. Признак окончания ввода множества – точка.
Var
B: Char;
Mn: Set Of ‘A’..‘Z’; {Тип элементов вводимого множества}
Begin
Mn := []; {Начальное значение множества – пустое множество}
Repeat
Read (B); {Чтение очередного элемента множества}
76
Mn := Mn + [B] {Объединение множеств}
Until B = ’.’; {‘.’ – признак конца текста}
...
Пример 4.9.
Продолжение примера 4.8. Вывод значения переменной типа множества
латинских букв.
77
<Константа_множество>::=
[ ]
<Константа_элемент>
<Константа_элемент>::=
<Константа>
.. <Константа>
Пример 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. ФАЙЛЫ
F F1 F2 F3 …
длина не
файл элементы файла
зафиксирована
<Файловый_тип>::=
File Of <Тип>
Text
80
5.2. Процедура Assign
<Диск>:\<Имя_каталога>\…\<Имя_каталога>\<Имя_файла>
A:\Katalog1\Katalog2\Rez.pas
81
или организовать связь в диалоге:
Var
St: String;
...
Begin
...
Writeln (‘Введите имя файла’);
Readln (St);
Assign (F, St);
1) Con
Con – устройство консоли (при вводе – это клавиатура, при выводе –
экран дисплея).
Например, процедура
82
3) Com1, Com2
Com1, Com2 – устойства последовательного ввода-вывода, используемые
для обмена данными между компьютерами. Вместо Com1 может быть
использовано имя ‘Aux’.
4) Nul
Nul – нулевое устройство. Для него при выводе не осуществляется
никаких действий. При попытке чтения возникает ситуация конца файла.
5) Crt
Crt – устройство текстового ввода-вывода. Аналогично устройству Con,
но имеет ряд дополнительных функций управления экраном (например,
установка цветов, указание места на экране для вывода и т.п.). Crt не
поддерживается операционной системой.
6) ‘’
‘’ – использование пустой строки вместо имени Name. В этом случае
файловая переменная F связывается с Con (по аналогии с пунктом а)).
Например,
Пример 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;
1) Процедура Assign
Процедура Assign – связывает файловую переменную с внешним файлом
на диске. Описана в п.5.2.
Окно
84
3) Процедура Write (F, V1 [, V2, … , VN])
Окно
Окно
Пример 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;
...
F F1 F2 F3 …
Окно
86
перебирая компоненты с помощью процедуры Read, дойти до нужной
компоненты.
Результат выполнения процедуры Read (F,V1,V2) изображает рисунок 5.6.
F F1 F2 F3 …
V1 F1
F F1 F2 F3 …
V2 F2
6) Функция Eof(F)
Функция Eof(F) (End Of File) – служит для определения факта выхода при
чтении за пределы файла.
Функция Eof возвращает значение признака конца файла. Если достигнут
конец файла F (окно указывает на маркер конца файла – позицию, следующую
за последней компонентой файла), или если файл пустой, то значение функции
Eof равно True. В противном случае функция Eof возвращает значение False.
Если значение функции Eof равно True, то использование процедуры
Read недопустимо.
Если в заголовке функции Eof опущено имя файла, то предполагается
файл Input. Например,
L := Eof;
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
...
Пример 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;
…
9) Функция Filesize(F)
Функция Filesize(F) – возвращает текущий размер файла (число элементов
в файле). Тип результата – Longint. Для пустого файла возвращается значение 0.
Файл должен быть предварительно открыт.
Функция не может быть использована для текстовых файлов.
Пример 5.5.
Расширение файла F (добавление элементов в конец файла).
...
Seek (F, Filesize (F));
Write (F, C);
...
89
В данном примере процедура Seek помещает окно файла за последним
элементом файла (функция Filesize возвращает количество элементов, но так
как они нумеруются с нуля, то значение функции Filesize совпадает с номером
следующей компоненты после последней).
Пример 5.6.
Определение размера файла. Закрытие файла.
…
Assign (F, ‘Old’);
Reset (F);
Write (‘Размер файла Old ’, Filesize (F)); {Вывод на экран размера файла}
Close (F);
…
T = File Of Char.
90
Функция Eoln(F) принимает значение True, если окно (текущая позиция
доступа) установлено на маркер конца строки, и False – в противном случае.
Текстовые файлы – это переменные типа Text.
Тип Text относится к предопределенным структурным типам.
Переменные типа Text описываются c помощью указания имени типа:
Var
X, Y: Text;
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);
…
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.
94
типов отводится 24 позиции, причем вещественное число будет выведено в
виде мантиссы и порядка.
• Значения Ei могут сопровождаться указанием формата вывода:
E: L1[: L2] .
Writeln(F);
Writeln(A, B, C);
Writeln;
Пример 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.
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
…
98
5.5. Сравнительная характеристика
представления информации
в файлах с типом и текстовых
файлах
а) В текстовом файле.
Пусть в текстовом файле записана последовательность чисел
8192, 2048, …
8192, 2048, …
99
0011 1000 0011 0001 0011 1001 0011 0010 0010 0000
0011 0010 0011 0000 0011 0100 0011 1000 0010 0000 …
Следующие
числа
100
При попытке вывода данных байтов файла на экран выведется пробел (не
изображается), пустой символ (не изображается), символ кода ASCII с номером
8 (изображается в виде ◘), пустой символ.
Таким образом, при работе с числовой информацией, если ее не нужно
выводить на экран или печать (это осуществляют лишь текстовые файлы),
эффективнее использовать файлы с типом:
1) работа с ними осуществляется быстрее за счет следующих факторов:
а) отсутствует преобразование информации, она в файле
представлена так же, как в памяти;
б) при работе с файлами с типом возможен режим прямого доступа;
в) меньше операций физического ввода-вывода за счет меньшего
размера файла;
2) они занимают меньше места (в текстовом файле каждая цифра числа
занимает байт, разделители – не менее одного пробела между числами, маркер
конца строки – два управляющих символа #13#10; в файле типа File Of Integer
все число занимает два байта, разделители между числами не нужны).
а) В текстовом файле.
Пусть в текстовом файле записан текст «ВАШ ОТВЕТ НЕВЕРЕН». Пусть
данный текст разбит на строки, которые изображает рисунок 5.9.
В А Ш #13 #10
Конец строки
О Т В Е Т #13 #10
Н Е В Е Р Е Н #13 #10
101
Каждая строка представляет собой одно слово, имеет текущую длину и
заканчивается маркером конца строки. Каждый символ текста представлен в
коде ASCII и занимает один байт.
Таким образом, для представления данной информации в текстовом
файле необходим 21 байт.
б) В файле of string [7].
Тот же текст в файле типа File Of String[7] имеет внутреннее
представление, которое представляет рисунок 5.10.
#3 В А Ш #0 #0 #0 #0
#5 О Т В Е Т #0 #0
#7 Н Е В Е Р Е Н
102
работы одновременно в режиме записи-чтения, не нужно отслеживать
управляющие символы #13#10 (маркер конца строки).
Таким образом, для хранения текстовой информации выбирать файл типа
Text или File Of String следует в каждом конкретном случае, исходя из
особенностей задачи.
Var
F: File;
103
Rewrite (F [,Recsize]);
Reset (F [,Recsize]);
2) Процедура Blockread
Вместо процедур Read и Write в файлах без типа используются
процедуры Blockread и Blockwrite.
Процедура Blockread имеет следующий формат вызова:
Result* Recsize
104
сегмента данных). В противном случае возникнет сообщение об ошибке ввода-
вывода.
3) Процедура Blockwrite
Процедура Blockwrite имеет следующий формат вызова: