Размещение динамических переменных
Выделение динамической памяти в программе на 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^;
Управление вычислениями Операторы if, then, else
Оператор 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 Y:= 60; DoSomething(Y); end; end; end else if X < -100 then begin Y:= -20; if X < -200 then begin Y:= -40; if X < -400 then begin Y:= -60; DoSomething(Y); end; end; end;
ПРИМЕЧАНИЕ. Если фрагмент кода содержит две и более последовательностей условий, проверяющих значения одной и той же переменной, то вместо условного оператора лучше применить оператор case (см. ниже). В рассмотренных примерах условием было простое выражение. Если же необходимо связать несколько выражений, то каждое из них следует заключать в скобки:
if (X = 20) and (Y = 50) then DoSomething;
Циклы
Элементы структуры цикла
Циклы и их использование – важный аспект программирования. Выполнение повторяющихся действий с различными исходными данными – типичная работа любого компьютера. В этом разделе мы рассмотрим общие понятия и типы циклов Object Pascal: for, while и repeat. Цикл – это программная структура, повторяющая выполнение группы операторов определенное число раз. В любом цикле присутствуют: · Точка начала цикла. · Тело цикла. Обычно это блок begin – end, содержащий многократно выполняемые операторы. · Точка окончания. · Условие окончания цикла. · Средства принудительного окончания цикла. Точкой начала цикла является одно из ключевых слов for, while, или repeat. Тело цикла состоит из операторов, которые выполняются на каждой итерации цикла. В теле цикла могут присутствовать любые допустимые операторы Object Pascal и в количестве от одного до нескольких. Если тело цикла образовано несколькими операторами, их необходимо ограничить словами begin и end (за исключением цикла repeat). Если тело цикла – один оператор, begin – end не нужны. Точкой окончания цикла является либо ключевое слово end (for- или while- циклы) или ключевое слово until (цикл repeat). Если тело цикла состоит из единственного оператора, то точка окончания цикла есть (;). Условие цикла – это фактор, управляющий повторением действий. Большинство циклов работет так. В точке начала цикла проверяется условие окончания. Если оно ложно, то выполняются все операторы тела цикла, иначе – осуществляется переход к первому оператору, расположенному за точкой окончания цикла. По достижении точки окончания цикла управление передается в точку его начала.
Исключением из этого правила является цикл repeat, который проверяет условие в точке окончания цикла, а не в начале. Условие цикла необходимо для определения момента выхода из него, например: «Это надо повторять до тех пор (until) когда X станет равным 10», или «Продолжать чтение файла пока (while) есть непрочитанные записи».
ПРЕДУПРЕЖДЕНИЕ. Очень легко создать «бесконечный» цикл. При входе в бесконечный цикл программа перестает реагировать на внешние воздействия – «зависает». Если это случилось, нажмите Ctrl+Alt+Del. Появится диалоговое окно Close Program (в Windows NT – Task Manager) с сообщением «Программа не отвечает». Выберите имя программы и нажмите кнопку End Task («Снять задачу»). Если вы запустили программу из среды Delphi кнопкой Run (F9) и программа зависла, попробуйте в главном меню Delphi выбрать Run | Program Reset или нажмите Ctrl+F2. Учтите, что Windows 95 реагирует на команду Program Reset не совсем адекватно и может зависнуть сама. В этом смысле Windows NT более надежна. По возможности дайте своей программе доработать до конца, особенно в среде Windows 95.
Теперь рассмотрим каждую разновидность цикла более подробно. Цикл for…
Цикл for... следует использовать когда число повторений известно заранее. Для организации такого цикла достаточно знать, сколько итераций должно быть сделано. Для этого задают начальное и конечное значения счетчика цикла. Если при переходе от одной итерации к другой счетчик должен увеличиваться, в составе цикла используется ключевое слово to. В противном случае используется ключевое слово downto.
Цикл for с увеличением счетчика синтаксически определен как
for initial_value to end_value do begin statements; end;
Начальное значение счетчика цикла устанавливает выражение initial_value. Повтор выполнений блока statements (т.е. тела цикла) заканчивается сразу как только счетчик цикла превысит end_value. По окончании каждой итерации переменная целого типа в выражении initial_value увеличивается на единицу. Ключевые слова begin и end в цикле с единственным оператором не обязательны.
Цикл for с уменьшением счетчика в общем случае имеет следующий вид
for initial_value downto end_value do begin statements; end;
Начальное состояние счетчика цикла устанавливает выражение initial_value. Повтор выполнений блока statements (т.е. тела цикла) заканчивается когда счетчик цикла станет меньше чем end_value. По окончании каждой итерации переменная целого типа в выражении initial_value уменьшается на единицу.
Ключевые слова begin и end в цикле с единственным оператором не обязательны. Для пояснения синтаксических формул обратимся к примерам. Сначала рассмотрим цикл с увеличением счетчика цикла:
Var I: Integer; Begin for I:= 0 to 4 do Begin Memo1.Lines.Add(’Это итерация ’ + IntToStr(I)); end; end;
В этом фрагменте оператор
Memo1.Lines.Add(’Это итерация ’ + IntToStr(I));
выполняется 5 раз. Назначение оператора – добавить строку к тексту, отображаемому компонентном TMemo на форме приложения. В результате выполнения данного фрагмента программы в компонент Memo1 будут добавлены строки (Lines)
Это итерация 0 Это итерация 1 Это итерация 2 Это итерация 3 Это итерация 4
Выражение initial_value (в данном случае это I:=0) задает начальное значение счетчика цикла. Счетчиком цикла является переменная целого типа I. Выраженим end_value является константа 4. Таким образом, цикл закончится когда счетчик цикла, т.е. переменная I, будет равна 4.
Теперь рассмотрим цикл с уменьшением счетчика:
Var I: Integer; Begin for I:= 4 downto 0 do Begin Memo1.Lines.Add(’Это итерация ’ + IntToStr(I)); end; end;
В результате выполнения этого фрагмента компонент Memo1 будет содержать строки
Это итерация 4 Это итерация 3 Это итерация 2 Это итерация 1 Это итерация 0
Упражнение.
1. Создайте новую программу (File | New Application). 2. Поместите на форму командную кнопку. 3. На странице Standard найдите компонент Memo и перенесите его на форму. 4. Придайте компоненту Memo1 необходимые размеры. В инспекторе объектов придайте свойству ScrollBars компонента Memo1 значение ssVertical. 5. Дважды щелкните на кнопке Button1 для создания обработчика события OnClick. Придайте обработчику события следующий вид:
procedure TForm1.Button1Click(Sender: TObject); Var I: Integer; Begin Memo1.Lines.Clear; for I:= 0 to 5 do Memo1.Lines.Add(’Это итерация ’ + IntToStr(I)); Memo1.Lines.Add(’ ’); for I:= 5 downto 0 do Memo1.Lines.Add(’Это итерация ’ + IntToStr(I)); Memo1.Lines.Add(’––-Выполнено––-’); end;
Откомпилируйте программу (F9).
Нажмите кнопку Button1.
Ожидаемый результат показан на рисунке.
В отличие от других языков программирования, Pascal допускает изменение счетчика цикла только на 1. Чтобы организовать цикл, например от 0 до 100 с шагом 10, можно поступить следующим образом:
Var I: Integer; X: Integer; Begin X:= 0; Memo1.Lines.Clear; for I:= 0 to 9 do Begin Memo1.Lines.Add(’Счетчик = ’+ IntToStr(X)); Inc(X, 10); end; end;
Этот код выводит в Memo1:
Счетчик = 0 Счетчик = 10 Счетчик = 20 Счетчик = 30 Счетчик = 40 Счетчик = 50 Счетчик = 60 Счетчик = 70 Счетчик = 80 Счетчик = 90
ПРИМЕЧАНИЕ. Обратите внимание на то, что для увеличения значения переменной X используется процедура Inc. Эта функция увеличивает значение переменной (X в этом фрагменте) на заданную величину (10). Если приращение не указано, значение переменной увеличивается на 1. Например:
Inc(X); { X увеличивается на 1. То же, что и X:= X + 1 }
У функции Inc есть «двойник» с именем Dec. Ниже дан пример с функцией Dec:
Dec(X); { X уменьшается на 1 } Dec(X, 10); { X уменьшается на 10 }
Используйте функции Inc и Dec вместо операторов вроде X:= X + 1;
Функции Pred и Succ
Функции Pred и Succ применяются к данным перечислимого типа, например – Char, byte, integer и т.д. Функция Pred возвращает значение, которое предшествует значению ее аргумента. Например, Pred(10) вернет число 9, Pred(100) вернет 99, и т.д. Следующие три цикла идентичны:
Var X: Integer; Begin X:= 10; for I:= 0 to 9 do DoSomething; for I:= 0 to X - 1 do DoSomething; for I:= 0 to Pred(X) do DoSomething; end;
Когда счетчик цикла изменяется от 0 и надо сделать 10 итераций, легко ошибиться в сторону «лишней» итерации. Использование Pred с одной стороны, может решить проблему с лишней итерацией, а с другой стороны, Pred(X) элегантней чем X – 1. Функция Succ возвращает значение, которое следует за ее аргументом. В циклах с уменьшением счетчика полезно применять Succ так:
for I:= 100 downto Succ(X) do DoSomething;
Цикл while
Условие цикла while проверяется перед очередной итерацией. Итерации повторяются пока условие истинно.
Var X: Integer; Begin X:= 0; while X < 1000 do Begin X:= DoSomeCalculation; DoSomeMore; end; {...продолжение } end; В этом примере подразумевается, что функция DoSomeCalculation, рано или поздно, вернет значение 1,000. Пока эта функция возвращает числа меньше 1,000, цикл продолжается. Как только переменная X примет значение большее или равное 1,000, условие цикла станет ложным и повторение операторов тела цикла прекратится – произойдет переход к оператору, следующему за телом цикла. Помните, что проверка окончания цикла производится в точке while. Поэтому все операторы, которые находятся ниже строки, в которой переменная условия цикла приняла новое значение, выполняются до конца. В общем случае в качестве условия цикла рекомендуется использовать логическую переменную. Ее значение надо устанавливать в теле цикла:
Var Stop: Boolean; Begin Stop:= False; while not Stop do begin DoSomeStuff; Stop:= SomeFunctionReturningABoolean; DoSomeMoreStuff; end; end;
Если в какой–то момент времени выполнения программы переменная Stop примет значение true – выполнение цикла прекратится. Применение цикла while иллюстрирует следующая программа. Создайте новый проект и поместите на форму кнопку (Button) и блокнот (Memo). Дважды щелкните на кнопке и напишите следующий код обработчика события:
procedure TForm1.Button1Click(Sender: TObject); Var I: Integer; Begin I:= 5; Memo1.Lines.Clear; while I > -1 do Begin Memo1.Lines.Add('До перехвата '+ IntToStr(I) + ' сек.'); Dec(I); end; Memo1.Lines.Add('Цель с радара исчезла'); end;
Запустите программу и в окне блокнота увидите текст:
До перехвата 5 сек. До перехвата 4 сек. До перехвата 3 сек. До перехвата 2 сек. До перехвата 1 сек. До перехвата 0 сек. Цель с радара исчезла
В этой программе объявлена переменная I, которой перед входом в цикл присвоено значение 5. Далее начинается цикл. На каждой итерации цикла в компонент Memo1 добавляется строка текста, а значение переменной I уменьшается на 1. Как только переменная I станет равной -1, итерации прекращаются и в Memo1 выводится заключительная строка. Нетрудно сообразить, что точно такого же результата можно добиться и с помощью цикла for... downto. Строго говоря, область применения цикла while (repeat) – реализация итерационных вычислений когда число шагов (итераций) заранее неизвестно. Рассмотрим подпрограмму, которая реализует итерационный метод решения уравнения Кеплера
E – e sin(E) = M
где e и M – заданные вещественные значения, а неизвестной является вещественная переменная E. Для решения этого уравнения применяется метод последовательных приближений (итераций) по схеме
Ek+1 = M + e sin(Ek), k=0,1,2,...
с начальным приближением E0 = M. Итерации выполняют до тех пор, пока не будет достигнута заданная точность, например ½Ek+1 – Ek ½£10-9.
function SolveEquation_While(const e,M: real):real; { Решение уравнения Кеплера в цикле while } Const Eps = 1e-9; Var Eold,Enew: real; Stop: boolean; Begin Eold:= M; Stop:= false; while not Stop do Begin Enew:= M + e * Sin(Eold); Stop:= Abs(Eold-Enew)<=Eps; Eold:= Enew; end; Result:= Enew; end;
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|