Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ответы программирование.doc
Скачиваний:
68
Добавлен:
18.03.2015
Размер:
1.19 Mб
Скачать

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

• Функция Addr - возвращает результат типа Pointer, в котором содержится адрес аргумента.  • Addr(X), x - любой объект программы. Возвращаемый адрес совместим с указателем любого типа. Аналогично операции @.  • Функция CSeg - возвращает значения хранящееся в регистре CS (в начале работы программы в CS содержится сегмент начала кода программы), результат CSeg - слово типа Word.  • Процедура Dispose(x) - возвращает в кучу фрагмент динамической памяти, зарезервированный за типизированным указателем x.  • Функция DSeg - возвращает значение хранящиеся в регистре DS (в начале работы в DS - сегмент начала данных программы), результат - типа Word.  • Процедура FreeMem - возвращает в кучу фрагмент динамической памяти, который ранее был зарезервирован за нетипизированным указателем. FreeMem(P, Size), P - нетипизированный указатель. Size - длина фрагмента, подлежащего освобождению.  • Процедура GetMem(P, Size) - резервирует память (за одно обращение не более 65521 байт), если нет свободной памяти - ошибка времени исполнения.  • Процедура Mark(Ptr) запоминает текущее значение указателя кучи HeapPtr. Ptr - указатель любого типа, в нём будет возвращено HeapPtr. Используется совместно с Release для освобождения части кучи.  • Функция MaxAvail - возвращает размер (в байтах) наибольшего непрерывного свободного участка кучи. Результат типа LongInt. За один вызов New или GetMem нельзя зарезервировать значение большее, чем возвращаемое этой функцией.  • Процедура New(TP) - резервирует фрагмент кучи для размещения переменной. TP - типизированный указатель (за одно обращение не более 65521байт).  • Функция MemAvail - возвращает размер (в байтах) общего свободного пространства кучи. Результат типа Longint.  • Функция Ofs(X) - возвращает значение типа Word, содержащее смещение адреса указанного объекта. X - выражение любого типа или имя процедуры.  • Функция Ptr(Seg, Ofs) - возвращает значение типа Pointer по заданному сегменту и смещению. Значение, возвращаемое функцией, совместимо с указателем любого типа.  • Процедура Release(Ptr) - освобождает участок кучи. Рtr - указатель любого типа, в котором сохранено процедурой Mark значение указателя кучи. Освобожденный участок кучи - от адреса в Ptr до конца. Одновременно уничтожается список свободных фрагментов, созданных по Dispose и FreeMem.  • Функция Seg(X) - возвращает значение типа Word, содержащее сегмент адреса указанного объекта.  • Функция SizeOf(X) - возвращает длину (в байтах) внутреннего представления указанного объекта. X - имя переменной, функции или типа.

Проблема потерянных ссылок Работа с динамическими переменными через указатели требует большой тщательности и аккуратности при проектировании программ. В частности, следует стремиться освобождать выделенные области сразу же после того, как необходимость в них отпадает, иначе “засорение” памяти ненужными динамическими переменными может привести к быстрому ее исчерпанию.

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

Program LostReference;  Type   PPerson = ^Person;   Person = Record . . . .   End; Procedure GetPerson; Var   Р: РРerson; Begin   P:= New(PPerson); End; Begin   WriteLn(MemAvail);   GetPerson;   Writeln(MemAvail); End.

Вызов New в процедуре GetPerson приводит к отведению памяти для динамической переменной типа Person. Указатель на эту переменную присваивается переменной Р. Рассмотрим ситуацию, возникающую после выхода из процедуры GetPerson. По правилам блочности все локальные переменные подпрограммы перестают существовать после ее завершения. В нашем случае исчезает локальная переменная Р. Но, с другой стороны, область памяти, отведенная в процессе работы GetPerson, продолжает существовать, так как освободить ее можно только явно, посредством процедуры Dispose. Таким образом, после выхода из GetPerson отсутствует какой бы то ни было доступ к динамической переменной, так как единственная "ниточка", связывавшая ее с программой - указатель Р - оказался потерянным при завершении GetPerson. Вывод на печать общего объема свободной памяти до и после работы GetPerson подтверждает потерю определенной области.

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

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

Var   P: Integer;   Procedure X1;   Var     i: Integer;   Begin     i:= 12345;     P:= @i;     WriteLn(P^); { напечатает 12345 }   End;   Procedure X2;   Var     j: Integer;   Begin     j:= 7777;     WriteLn(P^); { напечатает 7777, а не 12345 }   End; Begin   X1;   X2; End;

В этом примере глобальная ссылочная переменная Р первоначально (в процедуре X1) устанавливается на локальную переменную i. После завершения процедуры X2 переменная i исчезает, указатель Р “повисает”. Вызов процедуры Х2 приводит к тому, что на место, локальной переменной i, будет помещена локальная переменная j, и указатель Р теперь ссылается на нее, что подтверждает результат второго вызова WriteLn.

Таким образом, смысл указателя Р зависит в данном случае не от семантики решаемой задачи, a от системных особенностей ее функционирования, что может привести к неожиданным эффектам. Аналогичные ситуации возможны при повторном использовании областей динамической памяти: если на освобожденную область остался указатель, то он может быть (по ошибке) использован после повторного выделения этой памяти для другой переменной, что опять- таки не будет "замечено" системой, но может сделать поведение программы непредсказуемым.

  1. Работа с файлами в языке Pascal. Файлы последовательного доступа.