Отметим, что блок Finally – End выполняется всегда вне зависимости от того, была или не была сгенерирована ИС.
Пример.
i:= 0;
n:= 8;
Try
GetMem (p, 8000); {выделение памяти}
i:= n div i; {Деление на нуль. Оператор генерирует ИС}
n:= i + 9;
Finally
FreeMem (p, 8000); {освобождение памяти}
End;
14. Указатели
В языке есть средство, разрешающее запрашивать память динамически, т. е. по необходимости. Это позволяет уменьшить объем кода программы и экономно расходовать оперативную память. Такое средство представляет собой специальный тип, называемый указателем. Имеется два типа ука-зателей: указатель на объект некоторого типа и указатель без типа.
Тип Pointer образует указатель без типа. Указатель на тип имеет синтаксис:
^ Имя типа
Примеры объявления указателей:
Type
tDinArr = Array[1 .. 1000, 100] of String[255]; {обычный тип}
tDinArrPtr = ^tDinArr; {указатель на тип tDinArr}
tRecPtr = ^tRec; {указатель на тип записи, который описан ниже}
tTRec = Record {обычный тип-запись}
A: Integer;
B: Real;
C: String[255];
End;
Var
DinArr: tDinArr; {обычная запись}
DinArrPtr: tDinArrPtr; {указатель на тип}
RecPtr: tRecPtr; {указатель на тип-запись}
Pn1, Pn2: Pointer; {указатели без типа}
Модули System и SysUtils содержат большое количество типов для работы с указателями. Эти типы могут быть использованы для повышения эффективности пользовательских программ, в которых используются указатели. К их числу относятся: PAnsiString, PString, PByteArray, PCurrency, PExtended и ряд других указателей. Впрочем, эти типы могут быть легко заменены стандартными типами. Например PString эквивалентен ^String и т.д.
14.1. Операции с указателями
Для указателей допустимы операции присваивания и сравнения. Указателю можно присваивать:
содержимое указателя такого же типа;
константу Nil (пустой указатель);
адрес объекта с помощью функции Addr;
адрес с помощью оператора @;
адрес, построенный функцией Ptr.
Пример:
TrPt:= Nil;
Klo1Ptr:= Klo2Ptr;
P1:=@Pp; {эквивалентно P1:= Addr(Pp);}
P2:= Ptr($B701);
14.2. Стандартные процедуры и функции для работы с указателями
Procedure GetMem(Var: P: Pointer; Size: Word);
Выделяет блок памяти размера Size и присваивает адрес начала блока указателю P.
Procedure FreeMem(Var: P: Pointer; Size: Word);
Освобождает блок памяти размера Size, адресованный указателем P.
Ниже приведен подробный пример, демонстрирующий экономный процесс копирования текстового файла 't1.txt' в файл 't2.txt' с использованием указателя Buffer.
Var
F1, F2: file; {объявление файловых переменных}
Buffer: PChar; {объявление указателя на строку }
begin
AssignFile(F1, 't1.txt'); {связывание F1 с файлом 't1.txt'}
Reset(F1, 1); {файл открыт для ввода/вывода}
AssignFile(F2, 't2.txt'); {связывание F2 с файлом 'text.txt'}
Rewrite(F2, 1); {файл открыт для вывода}
try
Size := FileSize(F1); {вычисления размера файла}
GetMem(Buffer, Size); {выделение памяти под чтение файла}
try
BlockRead(F1, Buffer^, Size); {считывание всего файла 't1.txt'}
BlockWrite(F2, Buffer^, Size); {запись в файл 't2.txt'}
finally
FreeMem(Buffer); {освобождение памяти}
end;
finally
CloseFile(F1); {закрытие файла F1}
CloseFile(F2); {закрытие файла F2}
end;
end;
В этом примере объявлен указатель на строку Buffer с завершающим нулем, которая будет использована для копирования файла 't1.txt' в файл 't2.txt'. Для этого оператором GetMem для переменной Buffer^ будет динамически выделен блок памяти размером, равным размеру файла. Далее оператором BlockRead файл 't1.txt', связанный файловой переменной F1, будет считан в Buffer^ и затем оператором BlockWrite переменная Buffer^ будет записана в файл 't2.txt', связанный с файловой переменной F2. Для предотвращения исключительных ситуаций пример содержит два вложенных блока try – finally – end. Внутренний блок обслуживает возможный сбой в ситуации, когда по какой-либо причине файл не удалось прочитать или записать операторами BlockRead или BlockWrite. Такой способ гарантирует освобождение памяти оператором FreeMem как в случае успешного копирования, так и в случае возможного сбоя. Внешний блок обслуживает ситуацию, когда у системы возможно нет того объема памяти, который запрашивает оператор GetMem. В любых вариантах – при успешном или безуспешном копировании файла – следующие за последним finally операторы CloseFile закроют открытые операторами Reset и Rewrite файлы F1 и F2 и позволяет программе продолжить работу.
Procedure New(Var: P: Pointer);
Создает новую динамическую переменную того типа, на который ссылается указатель. Эквивалентна оператору GetMem(P, SizeOf(P^));
Procedure Dispose(Var: P: Pointer);
Уничтожает динамическую переменную, на которую указывает P. Эквивалентна оператору FreeMem(P, SizeOf(P^));
Procedure ReallocMem(var P: Pointer; Size: Integer);
Процедура работает следующим образом:
если P= Nil и Size = 0, то оператор не выполнит никаких действий;
если P= Nil и Size > 0, то оператор сработает аналогично GetMem;
если P <> Nil и Size = 0, то оператор сработает аналогично FreeMem.
Function Addr(X): Pointer;
Адрес указанного имени.
14.3. Прочие процедуры и функции для работы с указателями
В модулях System и SysUtils объявлены процедуры и функции, которые могут найти применение в пользовательских программах. Ниже дано описание некоторых функций и процедур.
Function GetHeapStatus: THeapStatus;
Расположена в модуле System. Дает сведение о состоянии распределен-ной и доступной программе памяти. Тип функции имеет вид
THeapStatus = record
TotalAddrSpace: Cardinal;
TotalUncommitted: Cardinal;
TotalCommitted: Cardinal;
TotalAllocated: Cardinal;
TotalFree: Cardinal;
FreeSmall: Cardinal;
FreeBig: Cardinal;
Unused: Cardinal;
Overhead: Cardinal;
HeapErrorCode: Cardinal;
end;
Function AllocMem(Size: Cardinal): Pointer;
Выделяет блок памяти и устанавливает каждый байт "в нуль". Освобо-ждение памяти можно выполнить с помощью процедуры FreeMem.
Procedure GetMemoryManager(var MemMgr: TMemoryManager);
Дает текущее состояние менеджера памяти – специальной записи с типом:
TMemoryManager = record
GetMem: function(Size: Integer): Pointer;
FreeMem: function(P: Pointer): Integer;
ReallocMem: function(P: Pointer; Size: Integer): Pointer;
end;
Procedure SetMemoryManager(var MemMgr: TMemoryManager);
Устанавливает менеджер памяти – выполняет операции выделения и освобождения памяти в соответствии с предварительно установленными в менеджере памяти значениями.
14.4. Глобальные переменные AllocMemCount и AllocMemSize
В модуле System объявлены две глобальные переменные, значения которых могут быть использованы при контроле за динамически создаваемыми и разрушаемыми пользовательскими переменными.
AllocMemCount – количество блоков выделенной памяти.
AllocMemSize – размер блоков выделенной памяти.
15. Подпрограммы
Подпрограмма – это законченная алгоритмическая единица, которая предназначена для выполнения некоторого ограниченного по отношению к использующей ее программе круга операций.
В языке Object Pascal есть два вида подпрограмм – процедуры и функции. Структура всякой подпрограммы во многом напоминает структуру исходного модуля. Каждая такая подпрограмма перед ее использованием должна быть описана. Описанием подпрограммы называется ее исходный код, а обращением к подпрограмме является оператор или его часть, которые содержат код вызова такой подпрограммы. Таким образом, описание – это технология, а обращение – это действия по предписанной технологии.
Всякая подпрограмма может иметь локальные и глобальные по отношению к ней параметры. Локальным является параметр, действие которого ограничено только подпрограммой, в которой он описан. Всякий другой параметр будет глобальным. Все глобальные параметры всегда описаны за пределами подпрограммы, в которой они используются.
15.1. Процедуры
Всякая процедура имеет заголовок и тело. Тело процедуры состоит из операторов, предназначенных для описания имен и действий над данными. Синтаксис процедуры имеет вид
Procedure procedureName(parameterList); directives;
localDeclarations;
begin
statements;
end;
Здесь
Name – имя процедуры,
parameterList – список формальных параметров,
directives – директивы,
localDeclarations – внутренние описания,
statements – операторы тела процедуры.
procedureName – имя процедуры. Именем процедуры может быть любое имя, не совпадающее ни с каким другим описанным в том же, что и процедура, блоке, именем.
parameterList – список формальных параметров может быть либо пуст (в этом случае скобки можно не использовать), либо должен содержать последовательность входных и/или выходных величин. Отдельная величина в описании заголовка может быть:
объявленной с помощью слова var переменной с типом или без типа;
константой;
выходной величиной (т. н. out-параметром).
Пример описания процедуры.
procedure ByRef(var X: Integer; L, K: Integer);
begin
X := X * 2 * L; {правильно}
K := 2 + L; {ошибка}
end;
Процедура c именем ByRef содержит три параметра – переменную X и две константы L и K. Тело процедуры состоит из операторов, заключенных в операторных скобках begin – end. Переменные L, K являются только входными и не могут быть изменены в теле процедуры. По этой причине оператор K:= 2 + L не даст результата и значение К останется таким же, каким оно было до обращения к процедуре. Напротив, переменная X, объявленная с помощью слова var, будет вычислена и на выходе будет иметь то значение, которое будет вычислено внутри процедуры ByRef.
Out-параметр являет прямую противоположность константе, он должен быть только выходной, определяемой внутри процедуры, величиной. Например, в следующем коде
procedure GetInfo(out Info: SomeRecordType);
var MyRecord: SomeRecordType;
...
Proc1(MyRecord);
…
переменная MyRecord не может передавать данные в процедуру Proc1. Напротив, только Proc1 может сформировать данные и передать их в MyRecord.
Тип параметра может быть любым. Однако нельзя объявлять новый тип прямо в заголовке подпрограммы. Такой тип должен быть объявлен раньше и только после этого может быть использован в заголовке подпрограммы.
localDeclarations – внутренние описания могут содержать описание локальных имен типов или переменных. В предыдущем примере переменная MyRecord типа SomeRecordType, объявленная внутри процедуры GetInfo, является образцом такого описания.