Смекни!
smekni.com

Оперативная память ПК, виды, назначение и основные характеристики (стр. 4 из 5)

Текущий адрес свободного участка динамической памяти хранится в указателе HEAPPTR Посмотрим, как можно использовать динамическую память для размещения крупных массивов данных. Пусть, например, требуется обеспечить доступ к элементам прямоугольной матрицы 100x200 типа EXTENDED Для размещения такого массива требуется память размером 200 000 байт A00x200x10). Казалось бы, эту проблему можно решить следующим образом: var i,j : Integer; PtrArr : array [1..100, 1..200] of AReal; begin for l := 1 to 100 do for j := 1 to 200 do new(PtiArr[i,]l); and. Теперь к любому элементу вновь созданного динамического массива можно обратиться по адресу, например: PtrArr[1,11Л :- 0; if PtrArr[I,j*2JA > 1 then Вспомним, однако, что длина внутреннего представления указателя состав- составляет 4 байта, поэтому для размещения массива PTRARR потребуется ЮОх х200х4 = 80 000 байт, что превышает размер сегмента данных F5 536 байт), доступный, как уже отмечалось, программе для статического размещения данных.

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

Однако в Турбо Паскале над указателями не определены никакие операции, кроме операций присваивания и отношения. Тем не менее, решить указанную задачу все-таки можно. Как мы уже знаем, любой указатель состоит из двух слов типа WORD, в которых хранятся сегмент и смещение. В Турбо Паскале определены две встроенные функции типа WORD, позволяющие получить содержимое этих слов: ? SEG(X) — возвращает сегментную часть адреса; ? OFS (X) — возвращает смещение. Аргументом х при обращении к этим функциям может служить любая пере- переменная, в том числе и та, на которую указывает указатель Например, если имеем р : begin new(p); pn := 3.14; end. то функция seg(P) вернет сегментную часть адреса, по которому располага- располагается 4-баЙтный указатель р, в то время как seg(P") — сегмент 6-байтного участка кучи, в котором хранится число 3,14.

Таким образом, возможна такая последовательность действий. Вначале процедурой getmem из кучи забираются несколько фрагментов подходящей дли- длины (напомню, что за одно обращение к процедуре можно зарезервировать не более 65 521 байт динамической памяти). Для рассматриваемого примера удобно резервировать фрагменты такой длины, чтобы в них могли, напри- например, разместиться строки прямоугольной матрицы, т. е. 200x10 = 2000 байт. Начало каждого фрагмента, т. е. фактически начало размещения в памяти каждой строки, запоминается в массиве PTRSTR, состоящем из 100 указателей. Теперь для доступа к любому элементу строки нужно вычислить смещение этого элемента от начала строки и сформировать соответствующий указатель: var Integer; array A , .100J of pointer; PtrStr pr const SizeOfReal begin for l := 1 to 100 do GetMem(PtrStr[i]rSizeOfReal*200); 6; /Обращение к элементу матрицы [i,j]} pr := ptr(seg(PtrStr[i]"), ofs(PtrStr[i.]") + [j-l) *SizeOtReal) ; if pr" > 1 then end.

Поскольку оператор вычисления адреса PR : = PTR.. . будет, судя по всему, использоваться в программе неоднократно, полезно ввести вспомогательную функцию GETR, возвращающую значение элемента матрицы, и процедуру PUTR, устанавливающую новое значение элемента (правила объявления про- процедур и функций изложены в главе 8).

Каждая из них, в свою очередь, обращается к функции ADDRR для вычисления адреса. В примере 6.1 приводится программа, создающая в памяти матрицу из NxM случайных чисел и вычисляющая их среднее значение. const SizeOfReal = 6; N = 100; М = 200; var l, ] : Integer; PtrStr: array [1..N] of pointer; s : Reel; (Длина переменной типа REAL} {Количество столбцов} {Количество строк} /64 type RealPomt = ~Rcal; {— --) Function AddrR (i,j : word} : RealPoint; {По сегменту i и смещению j выдает адрес вещественной перемерной} begin AddrR := ptr (seg (PtrStr [1] л) , ofs (PtrStr [i]'v) + (j-l) \SizeOfReal) end {AddrR I; { 1 Function GetRA,j: Integer): Real; {Выдает ¦значение вещественной переменной по сегменту i и смещению j ее адреса} begin GetR := AddrR (i.,])A end I GetR); {- ¦-- I Procepure PiitR(i,j - Integer; x- Rpal); {Помещает в переменную, адрес которой имеет сегмент i и смещение j, вещественное значение у] begin AddrR (x, j) " -.= х end {PutR); {- } begin (Main! for i :=] to N do begin GetMem(PtrStr[i] ,M^SizeOfRea!) ; for j •= 1 to M do PutR(i, j. Random) end; s := 0; fcr l := 1 to N do for j := 1 to M do s := s + GetR(i, j) ; WriteLn(S / (N * M) : 12:10) end {Mam}.

В рассмотренном примере предполагается, что каждая строка размещается в куче, начиная с границы параграфа, и смещение для каждого указателя PTRSTR равно нулю. В действительности при последовательных обращениях к процедуре GF.TMEM начало очередного фрагмента следует сразу за концом предыдущего и может не попасть на границу сегмента. В результате, при размещении фрагментов максимальной длины F5 521 байт) может возникнуть переполнение при вычислении смещения последнего байта.

5. Процедуры и функции для работы с динамической памятью.

Ниже приводится описание как уже рассмотренных процедур и функций, так и некоторых других, которые могут оказаться полезными при обращении к динамической памяти. Функция addr. Возвращает результат типа POINTER, в котором содержится адрес аргумента. Обращение: ADDR (X) Здесь х — любой объект программы (имя любой переменной, процедуры, функции). Возвращаемый адрес совместим с указателем любого типа. Отметим, что аналогичный результат возвращает операция.

Функция CSEG. Возвращает значение, хранящееся в регистре cs микропроцессора (в начале работы программы в регистре содержится сегмент начала кода программы). Обращение: CSEG Результат возвращается в слове типа WORD Процедура DISPOSE. Возвращает в кучу фрагмент динамической памяти, который ранее был зарезервирован за типизированным указателем. Обращение: DISPOSE(TP) Здесь ТР типизированный указатель. При повторном использовании про- процедуры применительно к уже освобожденному фрагменту возникает ошибка периода исполнения. При освобождении динамических объектов можно указывать вторым параметром обращения к DISPOSE имя деструктора.

Функция DSEG. Возвращает значение, хранящееся в регистре DS микропроцессора (в начале работы программы в регистре DS содержится сегмент на- начала данных программы). Обращение: DSEG Результат возвращается в слове типа WORD 16й Часть I Ядро Турбо Паскаля Процедура ffeemem. Возвращает в кучу фрагмент динамической памяти, кото- который ранее был зарезервирован за нетипизированным указателем. Обращение: FREEMEM (P, SIZE) Здесь: ? р — нетипизированный указатель; ? SIZE — длина в байтах освобождаемого фрагмента.

При повторном использовании процедуры применительно к уже освобожденному фрагменту возникает ошибка периода исполнения. Процедура getmem. Резервирует за нетипизированным указателем фрагмент динамической памяти требуемого размера. Обращение: GFTMFM (P, ST7F) За одно обращение к процедуре можно зарезервировать не более 65 521 байт динамической памяти. Если нет свободной памяти требуемого размера, воз- возникает ошибка периода исполнения.

Если память не фрагментирована, последовательные обращения к процедуре будут резервировать последователь- последовательные участки памяти, так что начало следующего будет располагаться сразу за концом предыдущего. Процедура MARK Запоминает текущее значение указателя кучи HEAPPTR Об- Обращение: MARK (PTR) Здесь PTR — указатель любого типа, в котором будет возвращено текущее значение HEAPPTR Используется совместно с процедурой RELEASE для освобождения части кучи.

Функция maxavail. Возвращает размер в байтах наибольшего непрерывного участка кучи. Обращение: MAXAVAIL Результат имеет тип LONGINT За один вызов процедуры NEW или GETMEM нельзя зарезервировать памяти больше, чем значение, возвращаемое этой функцией. Функция mema VAIL. Возвращает размер в байтах общего свободного пространства кучи. Обращение: MEMAVAIL Результат имеет тип LONGINT Процедура NEW. Резервирует фрагмент кучи для размещения переменной. Обращение: NEW (TP) Здесь тр — типизированный указатель.

За одно обращение к процедуре можно зарезервировать не более 65 521 байт динамической памяти. Если нет свободной памяти требуемого размера, возникает ошибка периода исполнения. Если память не фрагментирована, последовательные обращения к процедуре будут резервировать последователь – последовательные участки памяти, так что начало следующего будет располагаться сразу за концом предыдущего. Процедура NEW может вызываться как функция. В этом случае параметром обращения к ней является тип переменной, размещаемой в куче, а функция NEW возвращает значение типа "указатель". Например: type Pint -ЛInteger; var р: Pint; begin p = New (Pint) ; end.

При размещении в динамической памяти объекта разрешается в качестве второго параметра обращения к NEW указывать имя конструктора (см. главу 10). Функция OFS. Возвращает значение типа WORD, содержащее смещение адреса указанного объекта. Вызов: OFS (X) Здесь х — выражение любого типа или имя процедуры. Функция PTR. Возвращает значение типа POINTER по заданному сегменту SEG и смещению OFS. Вызов: PTR (SEG, OFS) Здесь: О SEG — выражение типа WORD, содержащее сегмент; ? OFS — выражение типа WORD, содержащее смещение.

Значение, возвращаемое функцией, совместимо с указателем любого типа. Процедура RELEASE. Освобождает участок кучи. Обращение: RELEASE (PTR) Здесь PTR — указатель любого типа, в котором предварительно было сохранено процедурой MARK значение указателя кучи. Освобождается участок кучи от адреса, хранящегося в PTR, до конца кучи. Одновременно уничтожается список всех свободных фрагментов, которые, возможно, были созданы процедурами DISPOSE ИЛИ FREEMEM. Функция SEG.

Возвращает значение типа WORD, содержащее сегмент адреса указанного объекта. Вызов: SEG (X) Здесь х — выражение любого типа или имя процедуры. Функция SIZEOF. Возвращает длину в байтах внутреннего представления указанного объекта. Вызов: SIZEOF (X) Здесь х — имя переменной, функции или типа. Например, везде в программе из примера 6.1 вместо константы SIZEOFREAL можно было бы использовать обращение SIZEOF (REAL) 6.7. Администратор кучи

Как уже отмечалось, администратор кучи — это служебная подпрограмма, которая обеспечивает взаимодействие пользовательской программы с кучей. Администратор кучи обрабатывает запросы процедур NEW, getmem, DISPOSE, FREEMEM и др. и изменяет значения указателей HEAPPTR и FREELIST. Указа- Указатель HEAPPTR содержит адрес нижней границы свободной части кучи, а ука- указатель FREELIST — адрес описателя первого свободного блока. В модуле SYSTEM указатель FREELIST описан как POINTER, однако фактически он указывает на следующую структуру данных: type PFreeRec = "TFieeRac; TFreeRec - record Next : pointer; Size : pointer end, Эта списочная структура предназначена для описания всех свободных блоков па- памяти, которые расположены ниже границы HEAPPTR.