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

11.5.6. Обработка ошибок распределения памяти

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

FUNCTION ИмяФункции( Size : Word ) : Integer;

{209}

Обычно ее называют 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 такой структуры: {210}

($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 {211}

BEGIN

VideoPtr := Ptr( $B800, 0 );

{Переменная VideoPtr теперь содержит адрес начала видеопамяти в цветных и черно-белых режимах. Для режима mono надо подставить в присваивании Ptr{$B000,0). После этого можно непосредственно обращаться к видеопамяти. }

for i:=1 to 4000 do begin { Заполнение видеопамяти }

VideoPtr^[i].Symbol:= '+'; { символом '+' в ярко- }

VideoPtr^[i].Attrib:=15+128 { белом цвете с мерцанием}

end {for};

ReadLn; { пауза до нажатия клавиши ввода }

{ Заполнение статического массива Vec: }

for i:=1 to 100 do Vec[i] := i*3.14;

MatPtr := Addr(Vec); {передача его адреса ссылке MatPtr}

{Теперь к 100 элементам одномерного массива Vec можно обращаться и как к элементам матрицы 10x10, используя разыменование ссылочной переменной MatrPtr^. Здесь мы распечатаем диагональные элементы матрицы: }

for i:=1 to 10 do WriteLn(' ':i, MatPtr^[i,i]:-9:2 );

ReadLn; { пауза до нажатия клавиши ввода }

{ и т.п. }

END.

Рис. 11.8 (окончание)

Советуем внимательно рассмотреть пример на рис. 11.8. В нем нет ни процедур New/Dispose, ни GetMem/FreeMem! Динамические переменные здесь не создаются и не освобождаются. Взамен этого в ссылки «ручным способом» записываются адреса тех блоков памяти, с которыми мы хотели бы работать. А далее мы обращаемся с ними, как будто они есть структурированные динамические переменные.

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]