
- •1.Алфавит языка паскаль
- •9.6.Функции определения порядка
- •10.Тип вещественный
- •12.Операторы
- •12.1.Простые операторы
- •13.2.Вывод данных на монитор
- •13.3.Форматирование при выводе данных
- •17.1.Объявление и вызов подпрограмм
- •19.Блоки (модули) в паскале
- •19.1.Блок Crt (Unit Crt)
- •23.1.Работа с текстовыми файлами
- •24.Динамическая память
- •640 Кбайт ┌───────────────────┐
- •26.Динамические библиотеки
- •28.Тип объект (класс)
- •30.Встроенный ассемблер
- •30.1.Объявление операндов
- •31.Основы работы в графическом режиме
- •34.Многоугольники
- •35.Криволинейные фигуры
- •39.Команды работы редактора
24.Динамическая память
Вы наверное уже знаете, что когда вы активизируете программу,
то она считывается с диска и загружается в оперативную память. Та-
ким образом, запуск программы производится из оперативной памяти. В
ней же размещаются и другие данные, а именно:
->Часть памяти с младшими адресами занимает MS DOS. Это 50-60
килобайт, в зависимости от версии Dos.
->Далее хранится образ выполняемой программы - EXE-файл.
->Часть памяти занимают константы и переменные. Этот объем па-
мяти называется сегментом данных. Его размер не может превы-
шать 64 Кбайт.
Максимально возможный размер оперативной памяти у машин IBM PC
может быть 640 кбайт (1 байт=8 бит).
640 Кбайт ┌───────────────────┐
│ Свободный список │
├ - - - - - - - - - ┤<-FreePtr ─┐
│ Свободная память │ │
│-------------------│<-HeapPtr ├─ динамическая
│ Используемая куча│ │ память.
OvrHeapEnd->│-------------------│<-HeapOrg ─┘
│ Оверлейный буфер │
│ (если необходим) │
OvrHeapOrg->│-------------------│
│ Стек │ {для хранения локальных пе-}
SSeg:Ptr ->│-------------------│ {ременных - из подпрограмм}
│ Свободный стек │
SSeg:0000 ->│-------------------│
│ Переменные │ {сегмент данных - для хране-}
DSeg:0000 ->│-------------------│ {ния глобальных переменных}
│ │
│ *.EXE файл │
│ │
│-------------------│
│ 50-60 кбайт │
│ DOS │
└───────────────────┘
Рис.5.
25.ТИП УКАЗАТЕЛЬ
При работе с ЭВМ возникает естественный вопрос, а нельзя ли
использовать свободную память для решения своих задач. Для этих це-
лей существует аппарат указателей.
1.Как объявить переменную типа указатель?
->В ТР можно использовать три типа указателей, которые объяв-
ляются следующим образом:
а)Нетипизированные указатели:
Var A,Pl,Ptr1:Pointer;
б)Типизированные указатели:
Синтаксис этого типа данных
┌─ caret - символ вставки
<идентификатор переменной>:^<идентификатор типа>;
->Если нужен указатель стандартного типа, то:
Var I1:^Integer;
R5:^Real;
St:^String; {но нельзя объявить St:^String[12];}
Chi:^Char; {String[12] - не идентификатор типа}
->Если нужен указатель пользовательского типа, то возможны
различные варианты, но всегда <идентификатор типа> дол-
жен быть объявлен заранее.
Пример 70.
Type ArReal=Array [1..10] Of Real;
ArPtr=^ArReal;
Var X1:ArPtr;
Type ArReal=Array [1..10] Of Real;
Var X1:^ArReal; - Динамический массив.
Var X1:^Array[1..10] Of Real; {так нельзя объявить массив}
Var X2:Array [1..10] Of ^Real; - Массив динамических перемен-
ных.
в)Указатель ссылочного типа - это всегда запись минимум с дву-
мя полями.
Пример 71.
Type ListPtr=^List;
List=Record
St:String[22];
R:Real;
Arb:Array[1..6] Of Byte;
Next:ListPtr;
End;
Var A1,B,CV:ListPtr;
Отметим важную особенность таких указателей-одно из полей обя-
зательно имеет тип указателя.
Примечание:
Переменная типа указатель занимает в памяти 4 байта и хранит
адрес, начиная с которого начинают размещаться данные.
г)Указатель на строку с нуль-окончанием
В блоке System предопределен тип PChar следующим образом
Type PChar=^Char;
В режиме расширенного синтаксиса элементы строки с нуль-окон-
чанием совместимы по операции присваивания с типом PChar.
Пример 72.
->Var P:PChar;
Begin
P:='Hello...';
. . .
->Const PString:Array[0..14]='Hello...'#0;
Var P:PChar;
Begin
P:=@PString;
. . .
2.Как указатели используются в программе?
Указатель -это не простая переменная. Она используется двояко,
что обусловлено особенностями указателей, которые заключаются в том,
что с одной стороны, указатель хранит адрес в динамической памяти,
начиная с которого размещаются данные, а с другой стороны, указа-
тель является переменной объявленного типа, которая хранит данные
по адресу на который указывает указатель. В этом втором случае ука-
затель используется со значком ^. Переход от указателя (адреса) к
значению, которое хранится по этому адресу, называется операцией
разыменования. Таким образом, если в примере 63 X1 - это указатель,
то он хранит адрес в куче, а X1^ - это переменная типа массив, дан-
ные которого размещаются в динамической памяти, начиная с адреса,
который хранит указатель X1.
а)Для указателей допустимы только операции присваивания и сра-
внения. Указателю можно присвоить только содержимое другого указа-
теля, т.е. адрес, а также предопределенную константу - Nil (пустой
указатель типа Pointer, который указывает на адрес 0000:0000). При
этом надо учитывать тип указателя. Несколько указателей могут ука-
зывать на один адрес.
б)С данными указанного типа, которые хранятся по адресу, на
который указывает указатель можно выполнять все действия, которые
разрешены для этого типа данных.
Пример 73.
X1^[1]:=1.23;
R5^:=15.63;
St^:='Это значение переменной';
Ch1^:=#27;
X2[3]^:=5.4;
A1^.R:=3.3;
A1^.ArB[2]:=2;
в)Указатели могут использоваться и как типизированные констан-
ты, но их значением может быть Nil или адрес другой типизированной
константы типа указатель аналогичного типа.
Const Cr:^Real=Nil;
Cs:^String=Nil;
Ls:^String=@Cs; {адресное значение}
P:List=(St:'Указатель';R:22.4;ArB:(0,1,2,3,4,5);Next:nil);
Nil - Предопределенная типизированная константа типа Pointer,
соответствующая адресу 0000:0000.
3.Функции для работы с адресами и указателями
=>MemAvail:LongInt; - Возвращает сумму всех свободных участков в
динамической памяти.
=>MaxAvail:LongInt; - Возвращает размер наибольшего непрерывного
участка в динамической памяти.
=>Addr(X):Pointer; - Возвращает адрес указанного объекта, X - любая
переменная или идентификатор процедуры или фу-
нкции (@X), т.е. это указатель на объект.
=>Seg(X):Word; - Возвращает значение сегмента памяти с объектом X.
=>Ofs(X):Word; - Возвращает значение смещения внутри сегмента с объ-
ектом X.
=>Ptr(Seg,Ofs:Word):Pointer; - Преобразует адрес, заданный в виде
сегмента и смещения в значение типа
указатель.
=>Assigned(X):Boolean; - Возвращает True, если указатель или проце-
дура указывает на Nil.
4.Работа с нетипизированными указателями
Поскольку указатель указывает на адрес, по которому будут хра-
ниться данные, то недостаточно объявить в разделе описаний указа-
тель, необходимо в разделе операторов создать указатель, а после
окончания использования - уничтожить его.
=>GetMem(Var P:Pointer;Size:Word); - Создает новую динамическую пе-
ременную P заданного размера Size и
заносит адрес в переменную указатель.
=>FreeMem(Var P:Pointer;Size:Word); - Освобождает Size байт в дина-
мической памяти, начиная с адреса, хранящегося в P.
Пример 74.
Var ScrPtr:Pointer;
Procedure GetScreen;
Begin
GetMem(ScrPtr,$1000);
Move(Mem[$B800:0],ScrPtr^,$1000);
End;
Procedure SetScreen;
Begin
Move(ScrPtr^,Mem[$B800:0],$1000);
FreeMem(ScrPtr,$1000);
ScrPtr:=Nil;
End;
Как использовать в основной программе?
GetScreen - Сохраняет копию экрана в динамической памяти.
SetScreen - Восстанавливает вид экрана.
5.Работа с типизированными указателями
Во-первых, необходимо отметить, что для типизированных указа-
телей можно использовать процедуры создания и уничтожения динамиче-
ских переменных, которые применяются для нетипизированных указате-
лей.
Пример 75.
Type Item=Real;
ArItem=Array[1..1] Of Item;
ItemPtr=^ArItem;
Var ArPtr:ItemPtr;
N,I:Word;
{$R-} отключить проверку выхода индекса за границы диапазона
Begin
Write('Количество элементов в массиве=');
ReadLn(N);
GetMem(ArPtr,N*SizeOf(Item)); {создается указатель нужного}
For I:=1 to N Do ArPtr^[I]:=0; {размера, начиная с адреса, }
... {который хранится в ArPtr}
FreeMem(ArPtr,N*SizeOf(Item));
ArPtr:=Nil;
{$R+}
End.
Это пример создания динамического массива переменной длины.
Во-вторых, для типизированных указателей имеются специальные проце-
дуры создания и уничтожения динамических переменных.
=>New(Var P:<тип>); - Создает новую динамическую переменную P, раз-
мер которой определяется указанным типом, и
заносит адрес в переменную указатель.
=>Dispose(Var P:<тип>); - Освобождает динамическую переменную, на-
чиная с адреса, указанного в P.
Пример 76.
Type St24=String[24];
Var P:^St24;
Begin
New(P);
P^:='Это значение указателя';
Dispose(P);
P:=Nil;
End.
Отметим, что в небольших программах, в которых нет повторного
обращения к динамическим переменным, их можно не уничтожать, но мы
с вами будем считать это ошибкой.
6.Работа с указателями ссылочного типа
C помощью двух-трех указателей этого типа можно хранить в ди-
намической памяти массивы размером более 64 кбайт. Указатели ссыло-
чного типа организуют структуру в виде цепочки или очереди, где
один элемент помнит адрес другого и т.д. Для управления такими стру-
ктурами предусмотрено две процедуры:
=>Mark(Var P:<тип>,включая Pointer>); - Запоминает адрес начала сво-
бодного участка в динамиче-
ской памяти.
=>Release(Var P:<тип>, включая Pointer>); - Освобождает память выше
границы P.
Создаются и уничтожаются указатели ссылочного типа с помощью
уже известных процедур New и Dispose.
Пример 77.
Var P1,P2,P3,P4,P5:^Real;
P:^Integer;
Begin ┌──────────────┐
New(P1); │ │
New(P2); ├──────────────┤
Mark(P); ├──────────────┤ <-HeapPtr
New(P3); ├───────P5^────┤ <-P5
New(P4); ├───P4^────────┤ <-P4
New(P5); ├─────────P3^──┤ <-P3 <-P - запомнили границу
... ├──────P2^─────┤ <-P2
├──P1^─────────┤ <-P1<-HeapOrg
└──────────────┘
Dispose(P4); Release(P);
Y Y
┌─────────────┐ ┌─────────────┐
│ │ │ │
├─────────────┤<-HeapPtr │ │
├───────P5^───┤<-P5 │ │
├─────────────┤ │ │
├────────P3^──┤<-P3<-P ├─────────────┤<-HeapPtr
├─────P2^─────┤<-P2 ├───────P2^───┤<-P2
├──P1^────────┤<-P1<-HeapOrg ├────P1^──────┤<-P1<-HearOrg
└─────────────┘ └─────────────┘
Рис.6.
=>Release(HeapOrg); - Освобождает всю динамическую память.
а)Построение списков с добавлением элементов в конец очереди.
Пример 78.
Type ListPtr=^List;
List=Record
D:<любой тип>;
Next:ListPtr;
End;
Var FirstPtr,LastPtr,NewPtr:ListPtr;
TopHeap:^Integer;
D:<любой тип>;
Begin
...
Mark(TopHeap); {задание границы списка}
FirstPtr:=Nil; {начальный адрес указателя}
<цикл>
Begin
New(NewPtr); {создание указателя на каждом шаге и за-}
NewPtr^.D:=D; {дание значений полей данных}
If FirstPtr=Nil Then FirstPtr:=NewPtr
Else LastPtr^.Next:=NewPtr;
LastPtr:=NewPtr;
LastPtr^.Next:=Nil;
End;
... │ │
Release(TopHeap); удаление всего списка │ 3 шаг│
... ├──────┤
End. │ │ Nil<-│ Next │
│ │ │ 2 шаг│ ├ ─ ─ ─┤
│ 1 шаг│ ├──────┤ │ D3 │ ┌LastPtr
├──────┤ Nil<-│ Next │ ┌>├──────┤<─┴NewPtr
┌│ Next │ ├ ─ ─ ─┤ └─┤ Next │
│├─ ─ ─ ┤ │ D2 │ ┌LastPtr ├ ─ ─ ─┤
││ D1 │ ┌LastPtr ┌>├──────┤<─┴NewPtr │ D2 │
│├──────┤<─┼NewPtr └─┤ Next │ ┌>├──────┤
││ │ ├FirstPtr ├ ─ ─ ─┤ └─┤ Next │
│ └TopHeap │ D1 │ ├ ─ ─ ─┤
└>Nil ├──────┤<─┬FirstPtr│ D1 │
└TopHeap ├──────┤<─┬FirstPtr
Рис.7. └TopHeap
После последнего шага получим
├──────┤
┌ Nil<-│ Next │
выполняет- ├ ─ ─ ─┤ ┌LastPtr─выполняется на каждом этапе
ся на каж- │ Dn │ │ LastPtr:=NewPtr;
дом этапе ├──────┤<┐<─┴NewPtr──выполняется на каждом этапе
LastPtr^. │ Next ├─┘<─┐ New(NewPtr);
Next:=Nil; ├ ─ ─ ─┤ │
│ Dn-1 │ │
├──────┤<┐<─┤
│ . ├─┘ │
├──────┤ │
│ . │ │
├──────┤ │
│ . │ │
├──────┤<┐ │
│ Next ├─┘<─┼──выполняется на каждом этапе
├ ─ ─ ─┤ │ LastPtr^.Next:=NewPtr;
│ D2 │ │
├──────┤<┐<─┘
│ Next ├─┘
├ ─ ─ ─┤
│ D1 │
├──────┤<─┬FirstPtr-выполняется на первом этапе
│ │ └TopHeap FirstPtr:=NewPtr;
Рис.8.
б)Построение списков с добавлением элементов в начало очереди
Пример 79.
Type ListPtr=^List;
List=Record
D:<любой тип>;
Next:ListPtr;
End;
Var LastPtr,NewPtr:ListPtr;
TopHeap:^Integer;
D:<любой тип>;
Begin
...
Mark(TopHeap); {задание границы списка}
LastPtr:=Nil; {начальный адрес указателя}
<цикл>
Begin
New(NewPtr); {создание очередного указателя и задание}
NewPtr^.D:=D; {значений полей данных}
NewPtr^.Next:=LastPtr;
LastPtr:=NewPtr;
End;
...
Release(TopHeap); {удаление всего списка}
End.
│ │
│ 3 шаг│
├──────┤ ┌LastPtr
┌──┤ Next │<─┴NewPtr
│ │ │ ├ ─ ─ ─┤
│ 2 шаг│ │ │ D3 │
├──────┤ │ ├──────┤
┌─┤ Next │ │┌─┤ Next │
│ │ │ ├ ─ ─ ─┤ ││ ├ ─ ─ ─┤
│ 1 шаг│ │ │ D2 │ ┌LastPtr ││ │ D2 │
├──────┤ │ ├──────┤<─┴NewPtr └│>├──────┤
│ Next │->Nil │ │ Next │->Nil │ │ Next │->Nil
├ ─ ─ ─┤ │ ├ ─ ─ ─┤ │ ├ ─ ─ ─┤
│ D1 │ ┌LastPtr │ │ D1 │ │ │ D1 │
├──────┤<─┼NewPtr └>├──────┤<─TopHeap └>├──────┤<─TopHeap
└TopHeap
Рис.9.
После последнего шага получаем:
│ │
├──────┤
┌──┤ Next │
│ ├ ─ ─ ─┤ ┌LastPtr-выполняется на каждом этапе
│ │ Dn │ │ LastPtr:=NewPtr;
│ ├──────┤<─┴NewPtr--выполняется на каждом этапе
│┌─┤ Next │ New(NewPtr);
┌>││ ├ ─ ─ ─┤
│ ││ │ Dn-1 │
│ └│>├──────┤
├─>│ │ . │
выполняется│ │ │
на каждом │ │ . │
этапе ─────┤ │ │
NewPtr^.New│ │ │ . │
:=LastPtr; │ │ ├──────┤
│ │┌─┤ Next │
│ ││ ├ ─ ─ ─┤
└>││ │ D2 │
││ ├──────┤
└│>│ Next │->Nil-выполняется на первом этапе
│ ├ ─ ─ ─┤ NewPtr^.Next:= LastPtr;
│ │ D1 │
└>├──────┤<─TopHeap
Рис.10.
В результате получается следующая цепочка связанных элементов.
┌─>┌──────┐ ┌─>┌──────┐ ┌─>┌──────┐
┌────────┐ │ │ D │ │ D │ │ │ D │
│ Начало ├─┘ ├ ─ ─ ─┤ . . . ├ ─ ─ ─┤ │ ├ ─ ─ ─┤
└────────┘ │ Next ├─┘ │ Next ├─┘ │ Next ├->Nil
└──────┘ └──────┘ └──────┘
Рис.11.
в)Вставка новых элементов в список
Вставку дополнительных элементов рассмотрим для списка, пост-
роенного в примере 67.
->Вставка в начало очереди:
Пример 80.
┌ - (было)- - - - -┌────>┌──────┐
┌────────┐ | │ │ D │
│ Начало ├─┴────>┌──────┐ │ ├ ─ ─ ─┤
└────────┘ │ │ D │ │ │ Next ├─ ...
│ ├ ─ ─ ─┤ │<┐ └──────┘
│ │ Next ├─────┘ │
│┌──>└──────┘ │
│├─New(NewPtr); │
│└─NewPtr^.D:=D; │
│ NewPtr^.Next:=FirstPtr;
└──FirstPtr:=NewPtr;
->Вставка в конец очереди:
Пример 81.
┌─>┌──────┐ ┌───>┌──────┐
│ D │ │ │ D │
├ ─ ─ ─┤ │ ├ ─ ─ ─┤ ┌──────────>┌──────┐
│ Next ├──┘ │ Next ├─┘│ │ D │
└──────┘ └──────┘ │ ├ ─ ─ ─┤
┌─N-1 LastPtr──┐│ │ Next ├─>Nil
└─>N-2 ┌>││ ┌─>└──────┘└───┐
│ └──────────>LastPtr │
│ │ └─────┐ │
│ │New(NewPtr);─┤ │
│ │NewPtr^.D:=D;┘ │
│ └LastPtr^.Next:=NewPtr;│
└───LastPtr:=NewPtr; │
LastPtr^.Next:=Nil;───┘
->Вставка в середину очереди:
Пример 82.
┌─>┌──────┐ ┌- - (было)- - - ┌─────>┌──────┐
│ D │ | │ │ D │
├ ─ ─ ─┤ ┌────>┌──────┐ │ ├ ─ ─ ─┤
│ Next ├─┘ │ D │ │ │ Next ├─
└──────┘ │ ├ ─ ─ ─┤ │<┐ └──────┘
A │ │ Next ├───┘ │
│┌───>└──────┘ │
│├─New(NewPtr); │
│└─NewPtr^.D:=D; │
│ NewPtr^.Next:=A^.Next;
└──A^.Next:=NewPtr;
г)Удаление элементов из очереди
->в начале очереди:
Пример 83.
┌──────────────┐
├ -│─>┌──────┐ ├─>┌──────┐ ┌─>┌──────┐
┌────────┐ │ │ │ D │ │ │ D │ │ │ D │
│ Начало ├─┘ │ ├ ─ ─ ─┤ │ ├ ─ ─ ─┤ │ ├ ─ ─ ─┤
└────────┘ │ │ Next ├─┘ │ Next ├─┘ │ Next ├─...
│ └──────┘ └──────┘ └──────┘
│ FirstPtr 2─>FirstPtr
│ NewPtr:=FirstPtr;
└─ FirstPtr:=FirstPtr^.Next;
Dispose(NewPtr);
NewPtr:=Nil;
->в конце очереди:
Пример 84.
┌─>┌──────┐ ┌->┌──────┐ ┌──────>┌──────┐
│ D │ │ │ D │ | │ D │
├ ─ ─ ─┤ │ ├ ─ ─ ─┤ | ├ ─ ─ ─┤
│ Next ├─┘ │ Next ├─┴──>Nil │ Next ├─>Nil
└──────┘ └──────┘ │ └──────┘
┌─N-2 ┌─N-1 │ LastPtr
└─>N-1 └─>LastPtr │ NewPtr:=LastPtr;
└───────────── LastPtr:=(N-2)^.Next;
Dispose(NewPtr);
NewPtr:=Nil;
->в середине очереди:
Пример 85.
┌───────────────┐
┌─>┌──────┐ ├-│- ─>┌──────┐ ├─>┌──────┐
│ D │ │ │ │ D │ | │ D │
├ ─ ─ ─┤ │ │ ├ ─ ─ ─┤ | ├ ─ ─ ─┤
│ Next ├─┘ │ │ Next ├─┘ │ Next ├─...
└──────┘ │ └──────┘ │ └──────┘
A │ B │ C
A^.Next:=B^.Next; │
Dispose(B);─┬─────────┘
B:=Nil;─────┘
7.Указатели структурированных типов данных
Пример 86.
Type ArR1=Array[1..20] Of Real;
ArR2=Array[1..10,1..10] Of ^Real;
ArRtr=^ArR1;
Rec=Record
B:Byte;
C:Char;
Ars:Array [1..10] Of String[10];
End;
ArRec=Array [1..15] Of Rec;
ArRecPtr=^ArRec;
ArPtrRec=Array [1..8] Of ^Rec;
Var A1:ArPtr;A2:ArR2;
A3:ArRecPtr;A4:ArPtrRec;
I,J:Byte;
Begin
New(A1);
A1^[14]:=5.4;
For I:=1 To 10 Do
For J:=1 To 10 Do New(A2[I,J]);
A2[1,5]^:=3.6;
A2[6,4]^:=0;
New(A3);
A3^[5].Ars[6]:='abcd';
A3^[4].B:=124;
For I:=1 To 8 Do New(A4[I]);
A4[3]^.C:=#65;
A4[7]^.Ars[1]:='abcde';
...
Dispose(A1); A1:=Nil;
For I:=1 To 10 Do
For J:=1 To 10 Do Begin
Dispose(A2[I,J]);
A2[I,J]:=Nil:
End;
Dispose(A3);
A3:=Nil;
For I:=1 To 8 Do Begin
Dispose(A4[I]);
A4[I]:=Nil;
End;
End.