размером 12 байт (6+6), то его размер необходимо увеличить еще на 4 байта. Для этого вместо приведенного выше описания необходимо дать следующее:
TYPE
Dot = RECORD
х, у : Real;
foo : Array [1..4] of Byte
END;
Теперь при размещении переменной типа Dot память будет выделяться и, что самое главное, освобождаться блоками по 16 байт. Заметим, что в принципе можно было бы отказаться от использования процедур New и Dispose и управлять памятью с помощью одних лишь процедур GetMem и FreeMem, хотя это вызвало бы определенные трудности при работе с объектами.
11.5.6. Обработка ошибок распределения памяти
По умолчанию при возникновении ошибки монитора кучи происходит аварийный останов программы. Однако эту ситуацию можно изменить, если воспользоваться системной переменной HeapError типа Pointer, которая указывает на функцию со следующим заголовком:
FUNCTION ИмяФункции( Size : Word ) : Integer;
- 210 -
Обычно ее называют HeapFunc. Эта функция вызывается в случае, когда New или GetMem не может обработать запрос на распределение кучи. Параметр Size содержит размер блока, который не мог быть распределен, и функция обработки ошибки должна попытаться освободить блок размером не меньше Size. В качестве результата функция должна возвращать значения 0, 1 или 2. В случае 0 немедленно будет возникать фатальная ошибка и останов программы. В случае 1 вместо аварийного завершения процедуры New или GetMem будет возвращаться указатель, равный nil. Наконец, 2 означает как бы замалчивание ошибки, но вызывает повторение запроса на распределение, что, впрочем, может опять вызвать ошибку.
Необходимо, чтобы эта функция компилировалась в режиме {$F+}. Функция обработки ошибки (пусть в программе она названа UserHeapFunc) может быть подставлена в монитор кучи присваиванием ее адреса системной переменной HeapError:
HeapError := @UserHeapFunc;
Мало смысла в том, чтобы писать свои функции HeapFunc, возвращающие значение 0. Программа и так оборвется при нехватке памяти в куче. Зато очень удобно обрабатывать ошибки распределения памяти, если установить возвращаемое значение равным 1:
{$F+}
FUNCTION HeapFunc(Size : Word) : Integer;
BEGIN
HeapFunc := 1
END;
{$F-}
и подставить эту функцию через переменную HeapError. Теперь можно анализировать последствия работы любой процедуры размещения динамических переменных:
...
New(P);
if P = nil then обработка ситуации нехватки памяти;
{иначе нормальная работа с P^}
...
Если пойти еще дальше, то можно написать функцию HeapFunc такой структуры:
- 211 -
($F+}
FUNCTION HeapFunc( Size : Word ) : Integer;
BEGIN
{ ОСВОБОЖДЕНИЕ КАКИМ-ЛИБО СПОСОБОМ Size БАЙТ В КУЧЕ }
HeapFunc := 2 {и повтор неудачного распределения }
END;
{$F-}
Начальное значение переменной HeapError при старте программы равно nil. Его же надо восстанавливать, если отпала необходимость в обработке ошибок кучи.
11.6. Ссылки, работающие не с кучей
Традиционно понятие «ссылка» всегда увязывается с динамическими переменными, кучей и т.п. У Турбо Паскаля тоже есть традиции. Одна из них — расширение общепринятых стандартов. В частности, ничто, кроме традиционных учебников стандартного Паскаля, не обязывает связывать ссылки или указатели именно с кучей. Ссылки могут указывать на что угодно: даже на выполнимый код программы (крайний и бесполезный случай). Обычно в разных трюках ссылки связываются со статическими данными или с системными областями памяти ПЭВМ (областью данных БСВВ, видеопамятью и др.).
Кроме способа связывания, такие ссылки ничем не отличаются от рассмотренных ранее (рис. 11.8).
| PROGRAM NoHeap;
| { ПРИМЕР ССЫЛОК БЕЗ ИСПОЛЬЗОВАНИЯ КУЧИ }
| TYPE { базовые типы: }
| VideoArray=Array[1..4000] of RECORD {структура экрана }
| Symbol : Char;
| Attrib : Byte
| END;
| Vector=Array[1..100] of Real; { одномерный массив }
| Matrix=Array[1..10,1..10] of Real;{ матрица 10 на 10 }
| VAR
| VideoPtr : ^VideoArray; { ссылка на структуру экрана }
| Vec : Vector; { ссылка на одномерный массив }
| MatPtr : ^Matrix; { ссылка на массив — матрицу }
| P : Pointer; { просто указатель }
| i : Word; { счетчик для циклов }
Рис. 11.8
- 212 -
| BEGIN
| VideoPtr := Ptr( $B800, 0 );
{Переменная VideoPtr теперь содержит адрес начала видеопамяти в цветных и черно-белых режимах. Для режима mono надо подставить в присваивании Ptr{$B000,0). После этого можно непосредственно обращаться к видеопамяти. }
| for i:=1 to 4000 do begin
| { Заполнение видеопамяти}