Выделение и освобождение динамической памяти 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Выделение и освобождение динамической памяти



Вся динамическая память в Турбо Паскале рассматривается как сплошной массив байтов, который называется кучей (heap). Физически куча располагается в старших адресах сразу за областью памяти, которую занимает тело программы.

Начало кучи хранится в стандартной переменной heaporg, конец – в переменной heapend. Текущую границу незанятой динамической памяти указывает указатель heapptr.

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

 

Var

i, j: ^integer;

r: ^real;

 

Begin

New(i);

End.

 

После выполнения этого фрагмента указатель i приобретет значение, которое перед этим имел указатель кучи heapptr, а сам heapptr увеличит свое значение на 2, так как длина внутреннего представления типа integer, с которым связан указатель i, составляет 2 байта (на самом деле это не совсем так: память под любую переменную выделяется порциями, кратными 8 байтам). Оператор

 

New(r);

 

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

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

 

i^:= 2; {В область памяти i помещено значение 2}

r^:= 6.5; {В область памяти r помещено значение 6.5}

 

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

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

r^:= sqr(r^) + i^ - 17;

Разумеется, совершенно недопустим оператор

r:= sqr(r^) + i^ - 17;

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

r^:= sqr(r);

поскольку значением указателя r является адрес, иего (в отличие от того значения, которое размещено поэтому адресу) нельзя возводить в квадрат, ошибочным будет и такое присваивание:

r^: = i;

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

Dispose (r);

Dispose (i);

вернут в кучу 8 байт, которые ранее были выделены указателям iиr.

Процедура Dispose(ptr)не изменяет значения указателя, а лишь возвращает в кучу память, ранее связанную с этим указателем. Однако повторное применение процедуры к свободному указателю приведет к возникновению ошибки периода исполнения. Освободившийся указатель программист может пометить зарезервированным словом nil.

Другая возможность состоит в освобождении целого фрагмента кучи. С этой целью перед началом выделения динамической памяти текущее значение указателя heapptr запоминается в переменной—указателе с помощью процедуры Mark. Теперь можно в любой момент освободить фрагмент кучи, начиная от того адреса, который запомнила процедура Mark, и до конца динамической памяти. Для этого используется процедура Release. Например:

Var

p, pl, p2,

p3, p4, p5: ^integer;

Begin

New (pl);

New (p2);

Mark (p);

New (p3);

New (p4);

New (p5)

Release (p);

End.

В этом примере процедурой Mark(p)в указатель p было помещено текущее значение heapptr, однако память под переменную не резерви­ровалась. Обращение Release(p)освободило динамическую память от помеченного места до конца кучи.

Как уже отмечалось, параметром процедуры Newможет быть только типизированный указатель. Для работы с нетипизированными указателями используются процедуры:

GetMem (p, size) – резервирование памяти;

FreeMem (p, size) – освобождение памяти.

Здесь р нетипизированный указатель;

size размер в байтах требуемой или освобождаемой части кучи.

За одно обращение к куче процедурой GetMemможно зарезервировать до 65521 байта динамической памяти.

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

 

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

Пример: создать в динамической памяти массив из переменных вещественного типа размерностью 100 * 200.

Для решения данной задачи воспользуемся следующими функциями:

Seg(x) – возвращает сегментную часть адреса;

Ofs(x) – возвращает смещение.

(Тип обоих функций – word).

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

С помощью встроенной функции

 

Ptr (seg, ofs: word): pointer

 

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

Для рассматриваемого примера удобно резервировать фрагменты такой длины, чтобы в них могли, например, разместиться строки прямоугольной матрицы, т.е. 200 * 6 = 1200 байт. Начало каждого фрагмента, т.е. фактически начало размещения в памяти каждой строки, запоминается в массиве PtrStr, состоящем из 100 указателей. Теперь для доступа к любому элементу строки нужно вычислить смещение этого элемента от начала строки и сформировать соответствующий указатель:

 

 

Var

i, j: integer;

PtrStr: array [1..100] of pointer;

pr: ^real;

Const

SizeOfReal = 6;

 

Begin

for i:= 1 to 100 do

GetMem (PtrStr[i], SizeOfReal * 200);

{Обращение к элементу матрицы [i,j]}

pr:= Ptr(Seg (PtrStr[i]^), Ofs (PtrStr[i]^) + (j – 1) * SizeOfReal);

End.

 

Пример: создать в памяти матрицу размерностью N * M, заполнить ее случайными числами и вычислить среднее значение.

 

const

SizeOfReal = 6; {Длина переменной типа REAL}

N = 100; {Количество столбцов}

М = 200; {Количество строк}

 

var

i, j: integer;

PtrStr: array [1..N] of pointer;

s: real;

 

type

RealPoint = ^real;

 

 

function AddrR (i, j: word): RealPoint;

{Функция по сегменту i и смещению j выдает адрес вещественной переменной}

begin

AddrR:= Ptr (Seg (PtrStr[i]^), Ofs (PtrStr[i]^)+(j –1)* SizeOfReal);

end;

 

function GetR (i, j: integer): real;

{Функция в ыдает значение вещественной переменной по сегменту i смещению j ее адреса}

be gin

GetR:= AddrR (i, j)^;

end;

 

procedure PutR (i, j: integer; x: real);

{Процедура п омещает в переменную, адрес которой имеет сегмент i смещение j, вещественное значение х}

be gin

AddrR (i, j)^:= x;

en d;

 

{Основная программа}

begin

for i:=1 to N do

begin

GetMem (PtrStr[i], M * SizeOfReal);

for j:= 1 to M do

PutR (i, j, Random)

end;

s:= 0;

for i:= 1 to N do

for j:= 1 to M do

s:= s + GetR(i,j);

WriteLn (s / (N * M): 12:10)

end.



Поделиться:


Последнее изменение этой страницы: 2017-02-05; просмотров: 403; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.17.150.163 (0.019 с.)