Автор работы: Пользователь скрыл имя, 16 Сентября 2009 в 13:41, Не определен
Учебник по программированию
После выполнения оператора присваивания
p2:=p1;
оба
указателя будут содержать
Переменные
p1^, p2^ являются динамическими, так как
память для них выделяется в процессе
выполнения программы с помощью
процедуры New.
Динамические
переменные могут
входить в состав
выражений, например:
p1^:=p1^+8; Write('p1^=',p1^:3);
Пример.
В результате выполнения программы:
Program DemoPointer;
var p1,p2,p3:^Integer;
begin
p1:=NIL; p2:=NIL; p3:=NIL;
New(p1); New(p2); New(p3);
p1^:=2; p2^:=4;
p3^:=p1^+Sqr(p2^);
writeln('p1^=',p1^:3,' p2^=',p2^:3,' p3^=',p3^:3);
p1:=p2;
writeln('p1^=',p1^:3,' p2^=',p2^:3)
end.
на экран дисплея будут выведены результаты:
p1^= 2 p2^= 4 p3^= 18
p1^= 4 p2^= 4
Вся динамическая память – пространство ячеек, называемое кучей.
Физически куча располагается в старших адресах, сразу за программой.
Указатель
на начало кучи храниться в предопределенной
переменной HeapOrg, конец - FreePtr, текущую
границу незанятой динамической памяти
указывает указатель HeapPtr.
Для выделения памяти под любую переменную
используется процедура New. Единственным
параметром является типизированный указатель:
(на доске)
//
Var
I,J: ^Integer;
R: ^Real;
Begin
New(I); {под I выделяется область памяти,}
{адрес первого байта этой области помещается в I}
End.
//
После выполнения этого
После
того как указатель приобрёл некоторое
значение, то есть стал указывать на
конкретный байт памяти, по этому адресу
можно разместить значения соответствующего
типа:
(на доске)
I^:= 2;
R^:= 2*Pi;
Допустима запись:
R^:= Sqr (R^) + I^ - 17;
Но недопустима запись: R:= Sqr (R^) + I^ - 17;
так как указателю
R нельзя присвоить значение вещественного
выражения.
Возврат
динамической памяти обратно в кучу
осуществляется оператором Dispose:
Dispose(R);
Dispose(I); - вернут в кучу, ранее забранные
8 байт.
Dispose(Ptr) не изменяет значения указателя
Ptr, а лишь возвращает в кучу память, связанную
с этим указателем. Однако повторное применение
процедуры к “свободному” указателю
приведет к возникновению ошибки времени
исполнения.
Чтобы
указать, что указатель
свободен, нужно использовать
зарезервированное
слово Nil.
К указателям можно применять операции
отношения, в том числе и сравнения с
Nil:
(на доске)
//
Const
P:^Real = Nil;
. . . . . . . .
Begin
If P = Nil then
New (P);
. . . . . . . .
Dispose(P);
P:= Nil;
End.
//
Использование указателей, которым не присвоено значение процедурой New или каким-либо другим способом, никак не контролируется системой и может привести к непредсказуемым результатам.
Многократное чередование New и Dispose приводит к “ячеистой” структуре кучи. Дело в том, что все операции с кучей выполняется под управлением особой программы, которая ведёт учёт всех свободных фрагментов в куче.
При выполнении оператора New( ) эта программа отыскивает минимальный свободный фрагмент, в котором может разместиться требуемая переменная.
Адрес
начала найденного фрагмента возвращается
в указателе, а сам фрагмент или
его часть нужной длины, помечаются
как занятая часть кучи.
Можно освободить целый фрагмент кучи следующим образом:
(на доске)
//
//
В
этом примере процедурой Mark(P) в указатель
P было помещено текущее значение HeapPtr,
однако память под переменную не резервировалась.
Вызов Release уничтожает список свободных
фрагментов в куче, созданных Dispose,
поэтому совместное применение этих процедур
не рекомендуется.
Для работы с
P - нетипизированный указатель, Size
- размер.
За одно обращение к куче процедурой GetMem можно зарезервировать до 65521 байт. Освобождать нужно ровно столько памяти, сколько было зарезервировано, и именно с того адреса, с которого память была зарезервирована, иначе программа не будет работать и завершаться корректно !!!
Динамическая
память - 200..300 Кбайт. Нужно разместить
массив 100 * 200 типа Extended. Требуется
100 * 200 * 10 = 200000 байт. Пробуем:
Var
i,j: Integer;
PtrArr: Array[1..100, 1..200] of ^Extended.
Begin
. . . . . .
For i:= 1 to 100 do
For j:= 1 to 200 do
New (PtrArr [i,j]);
. . . . . .
End.
// есть в рм.// Теперь к любому
элементу можно обратиться: PtrArr[i,j]^:=...;
Но длина внутреннего представления указателей
4 байта, поэтому потребуется ещё 100*200*4
= 80000 байт, что превышает размер сегмента
(65536 байт), доступный для статического
размещения данных.
Можно было бы работать с адресной арифметикой
(арифметикой над указателями), то есть
не создавать массив указателей, а вычислять
адрес элемента непосредственно перед
обращением к нему.
Однако
в Турбо-Паскале над
Но
задачу решить можно:
Seg(x) - возвращает сегментную часть
адреса.
Ofs(x) - возвращает смещение.
x - любая переменная, в том числе и
та, на которую указывает указатель.
Далее с помощью Ptr(Seg, Ofs: Word): Pointer можно
создать значение указателя, совместимое
с указателем любого типа.
Таким образом, сначала с помощью GetMem забираем из кучи несколько фрагментов подходящей длины (не более 65521). Удобно по строкам 200 * 100 = 20000 байт.
Начало каждого фрагмента запоминается в массиве PtrStr из 100 указателей. Теперь для доступа к любому элементу строки нужно вычислить смещение этого элемента от начала строки и сформировать указатель.
Var
i,j: Integer;
PtrStr: Array [1..100] of Pointer;
Pr: ^Extended;
Const
SizeOfExt = 10;
Begin
For i:= 1 to 100 do
GetMem (PtrStr[i], SizeOfExt*200);
. . . . . . . . . . . . . . . .
Pr:= Ptr(Seg(PtrStr[i]^), Ofs(PtrStr[i]^) + (j - 1) * SizeOfExt); {Обращение к элементу матрицы [i,j]}
End;
далее работаем с Pr^:=. . . и т.д.
Полезно ввести две
Program Demo;
Const
SizeOfExt = 10;
N = 100;
M = 200;
Type
ExtPoint = ^Extended;
Var
i,j: Integer;
PtrStr: Array[1..N] of Pointer;
S: Extended;
Function AddrE(i,j: Word): ExtPoint;
Begin
AddrE:= Ptr(Seg(PtrStr[i]^), Ofs(PtrStr[i]^) + (j - 1) * SizeOfExt);
End;
Function GetExt(i,j: Integer): Extended;
Begin
GetExt:= AddrE(i,j)^;
End;
Procedure PutExt(i,j: Integer; X: Extended);
Begin
AddrE(i,j)^:= X;
End;
Begin {main}
Randomize;
For i:=1 to N do
Begin
GetMem (PtrStr[i], M*SizeOfExt);
For i:= 1 to m do
PutExt(i, j, Random(255));
End;
S:= 0;
For i:= 1 to N do
For j:= 1 to M do
S:= S + GetExt(i,j);
WriteLn(S/(N*M));
End.
/
/
Мы предполагали, что каждая строка размещается в куче с начала границы параграфа и смещение каждого указателя PtrStr ровно 0. В действительности при последовательных обращениях к GetMem начало очередного фрагмента следует за концом предыдущего и может не попасть на границу сегмента. В результате при размещении фрагментов максимальной длины (65521 байт) может возникнуть переполнение при вычислении смещения последнего байта.
Структурированные типы данных, такие, как массивы, множества, записи, представляют собой статические структуры, так как их размеры неизменны в течение всего времени выполнения программы.