Основы Object Pascal (551739), страница 7
Текст из файла (страница 7)
Новый термин: Указатель есть переменная, которая содержит адрес памяти, по которому записано значение другой переменной.
Рассмотрим пример. Допустим, что в некоторую подпрограмму надо передать адрес записи. Определить адрес экземпляра записи можно с помощью оператора @ следующим образом:
var
MLRecord : TMailingListRecord;
APtr : Pointer;
begin
{ Заполним запись данными. }
APtr := @MLRecord;
SomeFunction(APtr);
end;
Переменная APtr (ее тип есть Pointer) используется для запоминания адреса записи MLRecord. В данном случае мы имеем дело с так называемым нетипизированным указателем или указателем без типа. Нетипизированный указатель просто есть адрес переменной. Существуют и типизированные указатели. Допустим что нам необходимо определить специальный тип указателей на записи TMailingListRecord. Объявление типизированного указателя имеет вид:
type
PMailingListRecord = ^TMailingListRecord;
TMailingListRecord = record
FirstName : string;
LastName : string;
Address : string;
City : string;
State : string;
Zip : Integer;
end;
Здесь тип PMailingListRecord определяется как указатель записи типа TMailingListRecord. Необходимость применения в программах типизированных указателй возникает довольно часто. В следующем разделе мы рассмотрим примеры их использования.
ПРИМЕЧАНИЕ: В приведенном выше фрагменте кода поля записи объявлены как длинные строки. Если переменные типа TMailingListRecord надо будет хранить в файле, такое объявление полей не подходит. Это связано с тем, что все записи файла должны иметь одинаковый размер (число байт). Длинные строки имеют переменную длину, поэтому нет гарантии того, что все записи будут одинаковы по длине. Если записи будут храниться в файле, для объявления строковых полей следует использовать короткие строки с явной спецификацией длины.
Статические и динамические переменные
Во всех примерах, которые рассматривались выше, использовались статические переменные. Они размещаются в локальной памяти программы, которая называется стеком.
Новый термин: Локальные переменные размещаются в блоке памяти, который называется стеком программы. Локальные переменные называют статическими.
В любой момент когда программе требуется разместить переменную в памяти, используется стек. Как только необходимость в переменной отпадает, память освобождается. Обычно так происходит при входе в подпрограмму или в любой другой локальный блок кода. При выходе из подпрограммы память освобождается. Все это делается автоматически.
Размещение статических переменных в стеке имеет как преимущества, так и недостатки. С одной стороны, выделение памяти происходит максимально быстро. С другой стороны, стек имеет фиксированный размер; изменить его объем во время выполнения программы нельзя. Если в какой–то момент программа исчерпает резервы стека, ее поведение станет непредсказуемым. Она может зависнуть или закончиться аварийно. На 32-битных компьютерах такие ситуации возникают реже чем на 16-битных, но все же проблеме переполнения стека следует уделить внимание.
Если в программе используются переменные предопределенных типов или небольшие массивы, то можно обойтись статическими переменными. Но если необходима обработка записей большого размера, то лучше воспользоваться динамическими переменными. Динамические переменные размещаются в области памяти, которая называется кучей. Объем кучи определяется свободным объемом оперативной памяти и объемом свободного места на жестком диске. Стандартная настройка Windows поддерживает размер кучи 100MB. Таким образом, программе может быть доступен практически неограниченный объем памяти. (Сведение различных уровней памяти – оперативной (RAM) и дисковой к одному уровню называется виртуальной памятью). Однако, при этом имеет место незначительная потеря производительности, поскольку реализация виртуальной памяти предусматривает обмен данными с диском. Кроме того, работа с динамическими переменными требует большего внимания со стороны программиста.
Новый термин: Динамической называется переменная или объект, которая размещается в куче.
Новый термин: Куча прикладной программы Windows есть вся виртуальная память компьютера.
Размещение динамических переменных
Выделение динамической памяти в программе на Object Pascal осуществляется с помощью специальных подпрограмм. К ним относятся функции AllocMem, GetMem и New. Функция AllocMem реализует наилучший метод динамического размещения с очисткой выделенной памяти. Обратимся к примеру с записью типа TMailingListRecord. В том примере переменная MLRecord является статической и, следовательно, размещается в стеке:
var
MLRecord : TMailingListRecord;
begin
{ Заполнить MLRecord данными. }
MLRecord.FirstName := 'Per';
MLRecord.LastName := 'Larsen';
{ и т.д. }
end;
Теперь воспользуемся динамической переменной:
var
APtr : PMailingListRecord;
begin
APtr := AllocMem(SizeOf(TMailingListRecord));
APtr.FirstName := 'Per';
APtr.LastName := 'Larsen';
{ . . . . . . . . . . . }
FreeMem(APtr);
end;
Здесь мы объявили переменную APtr типа PMailingListRecord, которая является типизированным указателем на запись типа TMailingListRecord. Таким образом, в стеке программы появится переменная APtr, значением которой будет адрес участка динамической памяти, выделяемой в результате обращения к функции AllocMem. Параметром в обращении к этой функции является количество байт, определяющее размер участка динамической памяти. Здесь размер памяти вычисляется с помощью функции SizeOf. В результате обращения к AllocMem в куче создается экземпляр динамической переменной типа TMailingListRecord. Далее мы можем обращаться к полям записи как обычно.
После того как необходимость использования динамической переменной отпадает, связанную с ней память необходимо «вернуть в кучу» (освободить). Для этого используется процедура FreeMem.
Итак, мы рассмотрели правила, связанные с понятием «динамическая переменная». Использовать в программах такие динамические переменные или нет – решает программист. Однако, что касается объектов – представителей классов объектно–ориентированного программирования, то они могут быть только динамическими.
ПРИМЕЧАНИЕ: Пустое значение указателя обозначается ключевым словом nil. Например, проверить связан ли указатель с динамической памятью или нет можно так:
if SomePointer = nil then SomePointer := AllocMem(Size);
Разыменование указателя
Иногда возникает необходимость разыменования указателя.
Новый термин: «Разыменовать» указатель означает «получить доступ» к тому объекту, на который он указывает.
Обратимся к примеру с записями типа TMailingListRecord:
var
APtr : PMailingListRecord;
Rec : TMailingListRecord;
begin
APtr := AllocMem(SizeOf(TMailingListRecord));
Допустим, что нам необходимо скопировать данные динамической переменной APtr в статическую переменную Rec. Переменная APtr есть указатель на TMailingListRecord, в то время как Rec является переменной типа TMailingListRecord. Поэтому следующее выражение вида Rec := APtr; некорректно, ибо APtr содержит адрес памяти, а не значения полей записи TMailingListRecord. Чтобы решить рассматриваемую задачу, указатель надо разыменовать. Для этого используется специальный символ разыменования указателя (^):
Rec := APtr^;
Глава 2.
УПРАВЛЕНИЕ ВЫЧИСЛЕНИЯМИ,
ЗАПИСИ,
ПОДПРОГРАММЫ
Управление вычислениями
Операторы if – then – else
Выполнение нескольких инструкций
Вложенные условия
Циклы
Цикл for
Функции Pred и Succ
Цикл while
Цикл repeat
Управление циклами
Оператор goto
Оператор case
Область видимости идентификаторов
Записи
Пример записи
Оператор with
Массивы записей
Включаемый файл
Подпрограммы
Процедуры и функции
Объявление и определение подпрограммы
Параметры подпрограмм
Локальные подпрограммы
Перегрузка подпрограмм
Параметры по умолчанию
Файл PASCAL2.DOC . Версия 17/03/2000
Управление вычислениями
Операторы if, then, else
Во всех языках программирования есть управляющие операторы, например – if. Оператор if используется для проверки некоторого условия с последующим выполнением (или невыполнением) некоторых операторов. Рассмотрим пример:
var
X : Integer;
begin
X := StrToInt(Edit1.Text);
if X > 10 then
Label1.Caption := 'Вы ввели число больше чем 10.';
end;
В этом фрагменте кода от компонента Edit1 (такой компонент должен быть на форме) мы получаем значение целого числа, которое вводит пользователь. Если введенное число окажется больше 10, значением выражения x > 10 будет True и на экране появится сообщение, а в противном случае – нет. Когда условие истинно, выполняется оператор, который следует сразу после выражения if...then. Иными словами, условная часть оператора следует сразу после then.
Новый термин: Оператор if используется для проверки истинности некоторого условия и выполняет один или несколько операторов если это условие истинно.
Выполнение нескольких инструкций. Допустим, что при истинном условии необходимо выполнить несколько операторов. В данном случае группу операторов следует объединить в один блок – ограничить ключевыми словами begin – end:
if X > 10 then
begin
Label1.Caption := 'Вы ввели число больше 10.';
DoSomethingWithNumber(X);
end;
Если условное выражение примет значение False, блок кода, связанный с выражением if игнорируется, а выполнение программы продолжается с оператора, следующего за этим блоком.
ПРИМЕЧАНИЕ. При проверки истинности переменной логического типа достаточно употребить ее имя в соответствующем контексте. Пусть
var
FileGood : Boolean;
Тогда вместо if FileGood = True then ReadData;
можно написать if FileGood then ReadData;
Следующий пример показывает как применить операцию отрицания
var
FileGood : Boolean;
begin
FileGood := OpenSomeFile;
if not FileGood then ReportError;
end;
Когда при истинности условия требуется выполнить одно действие, а при его ложности – другое, в состав условного оператора вводят else:
if X = 20 then
DoSomething(X)
else
DoADifferentThing(X);
В этом примере будет выполнено одно из двух действий – либо процедура DoSomething, либо процедура DoADifferentThing.
Новый термин: Оператор else используется совместно с if и обозначает секцию кода, которую следует выполнять когда условие ложно.
Обратите внимание на то, что перед else нет «точки с запятой». Это связано с тем, что вся последовательность if...then...else образует один оператор.
Приведем еще несколько примеров синтаксиса if...then...else
if X = 20 then
DoSomething(X)
else
DoADifferentThing(X);
if X = 20 then
begin
DoSomething(X);
end else
begin
DoADifferentThing(X);
end;
if X = 20 then
begin
DoSomething(X);
X := 200;
Y := 30;
end else
begin
DoADifferentThing(X);
X := 100;
Y := 15;
end;
Вложенные условия. Операторы if могут быть вложены друг в друга произвольное число раз:
if X > 10 then
if X < 20 then
Label1.Caption := 'Значение X заключено между 10 и 20';
Имейте в виду, что это упрощенный пример. В сложной последовательности вложений легко запутаться, особенно когда много begin – end операторов выделяют блоки кода:
if X > 100 then begin
Y := 20;
if X > 200 then begin
Y := 40;
if X > 400 then begin