В ТР имена объектов (переменных и др.) д. б. определены до момента использования их в программе. Как отмечалось ранее, ОП персонального компьютера имеет сегментную структуру. Адрес – совокупность двух 16-ти разрядных слов - сегмента и смещения. (Например: $0060:$01А0). Сегмент - участок памяти, имеющий максимальную длину 64К (65536 байт). Начальный адрес каждого сегмента кратен 16 (т.е. 0, 16, 32, и т.д.), следовательно, два сегмента отстоят друг от друга, по крайней мере, на 16 байт. Сегменты адресуют память с точностью до параграфа. Параграф – фрагмент памяти равный 16 байт. Смещение – линейная адресация в сегменте. Она также имеет 16-ти разрядные адреса и адресует память с точностью до байта. При этом глобальные переменные и типизированные константы размещаются в сегменте данных. Такие переменные называются статическими, а память, выделяемая компилятором для их хранения называется статической памятью.
Локальные переменные размещаются в памяти динамически при активизации подпрограммы. После выполнения подпрограммы память освобождается.
Память, которая выделяется под локальные переменные, называется сегментом стека. Она задается директивой {$M<размер памяти>}. Минимальный размер памяти 1К, максимальный 64К, по умолчанию 16к.
В ТР существует возможность создавать новые переменные в момент работы программы или уничтожать их в соответствие с требованиями задачи. Такие переменные называются динамическими.
Для динамических переменных отводится динамическая память, которая имеет стековую структуру и называется «кучей» (хипом – Heap-куча). Размер хипа задается директивой {$M<размер памяти>}.
Доступ к статическим переменным осуществляется через их имена.
Доступ к динамическим переменным осуществляется через указатель на место их расположения в памяти.
Многие практические задачи трудно или невозможно решить без использования динамической памяти. Например, обработки массивов больших объемов (более 65536 байт).
Ссылочные типы. Указатели в Паскале
Указатель – это переменная, которая содержит адрес другой переменной (байта памяти).
В ТР имеется два вида указателей: указатель на объект некоторого типа (типизированный) и указатель, не связанный с типом.
Описание указателей.
Для объявления типизированных указателей используется значок ^, который помещается перед соответствующим типом:
Type T = ^T1;
Var A :T;
где: T – имя типа;
T 1 - базовый тип (любой в т.ч. указатель);
^ - указатель.
Примеры:
Var
a :byte; {выделение памяти для переменной где хранится ее значение}
a ;^byte; {выделение памяти для переменной где хранится ее адрес}
Var
p1 :^integer;
p2, p3 :^real;
Для объявления переменных не связывая их, с каким либо типом данных можно использовать указатель без типа (pointer).
Var
p :pointer;
где: pointer - не типизированный указатель, который занимает в памяти 4 байт (2-байта сегмент, 2байта смещение.).
Операции с указателями
Для указателей допустимы операции сравнения и присваивания.
Присваивание. Указателю можно присвоить содержимое другого указателя того же самого типа или константу NIL – пустой, или адрес объекта с помощью функции ADDR или оператора @.
Пример:
P1 := PP;
P2 := NIL;
P3 := Addr(X);
P4 := @X;
Процедуры и функции для работы с указателями и адресами в Паскале
Функции:
ADDR(X) – результат POINTER, в котором содержится адрес аргумента. (X –имя любой переменной, процедуры или функции).
OFS(X):WORD – возвращает значение смещения адреса объекта X.
SEG(X):WORD – возвращает значение сегмента адреса объекта X.
CSEG(X):WORD – возвращает текущее значение регистра Cs.
DSEG(X):WORD – возвращает текущее значение регистра Ds.
SSEG(X):WORD – возвращает текущее значение регистра Ss.
SPRT(X):WORD - возвращает текущее значение регистра Sp.
PRT(SEG,OFS) – преобразует отдельно заданные значение сегмента и смещения
к типу указателя.
MAXAVAIL:LONGINT - возвращает размер наибольшего непрерывного участка
кучи.
MEMXAVAIL:LONGINT - возвращает размер общего свободного пространства кучи.
Процедуры:
DISPOSE(TP:POINTER) – уничтожает динамическую переменную и возвращает
в кучу фрагмент динамической памяти, который был зарезервирован
указателем.
NEW(TP:POINTER) – резервирует фрагмент кучи для размещения переменной.
GETMEM(P:POINTER; ZIZE:WORD) –выделяет из кучи блок заданного
размера и адрес его начала присваивает указателю.
FREEMEM(P:POINTER; ZIZE:WORD) – освобождает блок заданного
размера..
MARK(P:POINTER) – запоминает текущую вершину кучи (адрес начала
свободного участка).
RELEASE(P:POINTER) – освобождает участок кучи от адреса с P до конца.
Пример 19.1. Сгенерировать случайным образом массив случайных чисел. Разместить массив в динамической памяти, найти сумму элементов и удалить его с памяти.
Текст программы
Program Dinmas;
Uses CRT;
Const k1=100; k2=2*k1+1;
Type
Item = Real;
DinArr = array[1..$FFF0 div SizeOf(Item)] of Item;
DinPrt = ^DinArr;
Var
Arr : DinPrt;
I,N : Word;
S :Real;
f :real;
{$R-}
Begin
ClrScr;
Randomize;
Writeln('Введите количество элементов массива N:');
Readln(N);
{N*SizeOf(Item)-размер требуемой памяти }
GetMem(Arr,N*SizeOf(Item));
Writeln('Введите массив X(N):');
For i:=1 to N do
begin
f:=random(k2);
Arr^[i]:=k1-f; Write(Arr^[i]:4:2,' ':2)
{ Read(Arr^[i]);};
End;
Writeln;
S:=0;
For i:=1 to N do S:=S+Arr^[i];
Writeln(^G'Результат:',^J^M,^G'Сумма=',S);
FreeMem(Arr,N*SizeOf(Item));
Repeat Until KeyPressed
End.
Результаты работы программы:
Введите количество элементов массива N:
200
Введите массив X(N):
-35.00 -82.00 45.00 95.00 -26.00 -72.00 1.00 -77.00 10.00 89.00 -38.00
-48.00 8.00 26.00 13.00 33.00 6.00 82.00 -63.00 -83.00 -23.00 39.00
6.00 11.00 48.00 72.00 -100.00 -2.00 93.00 68.00 73.00 66.00 -91.00
6.00 0.00 -19.00 94.00 32.00 -63.00 59.00 87.00 29.00 -93.00 -51.00 3
4.00 10.00 58.00 0.00 27.00 41.00 6.00 34.00 55.00 -84.00 7.00 -37.00
-49.00 -6.00 71.00 11.00 -47.00 -85.00 -53.00 51.00 63.00 -80.00 32.
00 -71.00 -73.00 -27.00 -15.00 -7.00 -68.00 -89.00 32.00 -22.00 -86.00
-52.00 27.00 -16.00 92.00 19.00 -35.00 -8.00 -78.00 16.00 -94.00 -47
.00 -8.00 -98.00 -96.00 -5.00 -68.00 37.00 10.00 82.00 -32.00 15.00 2
2.00 -21.00 11.00 55.00 -15.00 -54.00 -20.00 -79.00 -15.00 -79.00 -13.
00 -62.00 -59.00 -93.00 -57.00 -42.00 57.00 41.00 -64.00 -43.00 -71.00
-66.00 49.00 25.00 66.00 89.00 38.00 98.00 -84.00 64.00 71.00 -75.00
77.00 83.00 27.00 -88.00 49.00 -100.00 59.00 29.00 -57.00 70.00 33.0
0 -87.00 -8.00 -45.00 23.00 88.00 -51.00 -66.00 5.00 95.00 -28.00 15.
00 -1.00 -26.00 -63.00 37.00 -76.00 -1.00 96.00 -43.00 -94.00 -29.00
-78.00 -99.00 4.00 -80.00 -43.00 -5.00 -6.00 -13.00 -1.00 -75.00 80.00
43.00 -58.00 -31.00 69.00 -81.00 -71.00 -62.00 -21.00 -84.00 4.00 67
.00 -82.00 -55.00 25.00 -4.00 94.00 75.00 -55.00 -15.00 -86.00 -43.00
-92.00 5.00 -81.00 -18.00 -33.00 -71.00
Результат:
Сумма=-1.8610000000E+03
Пример 19.2. Сгенерировать случайным образом матрицу случайных чисел. Разместить матрицу в динамической памяти, найти сумму элементов и удалить ее с памяти.
Текст программы:
Program Dinmas;
Uses CRT;
Type
Item = Real;
DinArr=array[1..$Ff div sizeof(item),1..$ff div sizeof(item)] of Item;
DinPrt = ^DinArr;
Var
Arr : DinPrt;
k,j,l,I,N : Word;
S :Real;
{$R-}
Begin
ClrScr;
randomize;
Writeln('Введите количество строк массива N:'); Readln(i);
Writeln('Введите количество столбцов массива N:'); readln (j);
n:=i*j;
{N*SizeOf(Item)-размер требуемой памяти }
GetMem(Arr,N*SizeOf(Item));
{ Writeln('Введите массив X(N):');}
For k:=1 to i do
for l:=1 to j do Arr^[k,l]:=random (2000);
S:=0;
For k:=1 to i do
for l:=1 to j do S:=S+Arr^[k,l];
Writeln(^G'Результат:',^J^M,^G'Сумма=',S);
FreeMem(Arr,N*SizeOf(Item));
Repeat Until KeyPressed
End.
Результаты работы программы:
Введите количество строк массива N:
1000
Введите количество столбцов массива N:
1000
Результат:
Сумма= 5.7820716634E+32
Пример 19.3. Разместить запись в динамической памяти, затем удалить ее с памяти.
Текст программы:
Program pointer1;
{Работа с динамической папятью}
Uses crt;
Type
FriendRec = Record
Name : string[30];
Age : Byte;
End;
FrPtr = ^FriendRec;
Var
p : FrPtr;
Begin
ClrScr;
GetMem(p, SizeOf(FriendRec)); {Выделение памяти в куче}
Write('Введите имя : '); Readln(p^.Name);
Write('Введите возраст : '); Readln(p^.Age);
Writeln;
Writeln('Память для записи о имени и возрасте распределена в куче.');
Writeln;
Writeln('Имя : ',p^.Name);
Writeln('Возраст : ',p^.Age);
Writeln;
FreeMem(p, SizeOf(FriendRec)); {Освобождение памяти}
Writeln('Память, занимаемая записью о имени и возрасте освобождена.');
Repeat until KeyPressed;
End.
Результаты работы программы:
Введите имя: Иванов Александр Григорьевич
Введите возраст: 33
Память для записи о имени и возрасте распределена в куче.
Имя: Иванов Александр Григорьевич
Возраст: 33
Память, занимаемая записью о имени и возрасте освобождена.