Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

new_pos_2011

.pdf
Скачиваний:
5
Добавлен:
18.03.2016
Размер:
1.46 Mб
Скачать

REPEAT и UNTIL являются в данном случае логическими скобками. Перед UNTIL можно не ставить ";". Цикл REPEAT выполняется так: сначала выполняется тело цикла, затем вычисляется логическое выражение, и если оно истинно, цикл заканчивается. Таким образом, цикл REPEAT всегда выполняется хотя бы один раз. Обратите внимание на то, что тело цикла WHILE выполняется до тех пор, пока логическое выражение истинно, а тело цикла REPEAT - пока логическое выражение ложно. Запишем наши примеры, используя цикл REPEAT:

const

n = 22;

var c : char;

var i : word ; s : single;

begin c:=pred(' ');

begin

i:=1; s:=0;

repeat c:=succ(c); write(c) until

 

repeat s:=s+sqr(i);

c=#255; writeln;

 

inc(i)

end.

 

until i>n;

 

 

writeln('сумма=',s);

 

end.

Рассмотрим управляющие процедуры, используемые при работе с цикличсекими операторами. Процедура BREAK приводит к немедленному окончанию цикла, в котором она вызвана, и переходу на первый оператор, расположенный непосредственно за телом цикла Вызов процедуры CONTINUE приводит к немедленному переходу к следующему шагу цикла.

Процедура HALT останавливает выполнение программы. Приведем пример использования операторов для блокировки несанкционированного

доступа в программу

 

const parol='tdf25';

 

var

i:integer;

 

 

s:string;

 

begin

for i:= 1 to 3

do

 

begin

 

 

write('введите пароль'); readln(s);

 

if s = parol

 

then

break

 

else

if i <> 3 then continue;

writeln('доступ к программе запрещен'); writeln( 'нажмите enter');

readln; halt

end;

writeln('пароль верен, доступ к программе разрешен'); readln;

end.

41

Рассмотрим еще несколько примеров.

Пример нахождения суммы бесконечного ряда e 1 2!1 3!1 4!1 ... .

var i : Integer;

eps, sum, znam, slag : Extended; begin

writeln(Rus('Ведите необходимую точность вычислений')); readln(eps);

Sum:=1; znam:=1; i:=1; repeat

slag:=1/znam;

Sum:=Sum+slag;

inc(i);

znam:=znam*i; until slag<eps;

writeln(Rus('Сумма ряда = '),Sum:15:12); writeln(Rus('Точное значение = '),exp(1):15:12); readln;

end.

Пример нахождения суммы бесконечного функционального ряда

sin x x

x3

 

x5

 

x7

 

x9

 

,

где

 

x

 

1 .

 

 

 

 

 

 

3!

5!

7!

9!

 

 

 

 

 

 

 

Для заданных положительных значений

и x вычислим сумму ряда с точ-

ностью . Суммирование ряда завершается, если модуль очередного члена ряда не превосходит . Предусмотрим ограничение количества слагаемых ряда.

var y, sum, x, eps, a, mnog : extended; i, m_dop: Word;

begin

writeln(Rus('Введите значение аргумента '), 'x='); readln(x); writeln(Rus('Введите значение погрешности '), 'eps='); readln(eps); writeln(Rus('Введите максимально допустимое количество членов

ряда')); readln(m_dop);

y:= sin(x);

i:= 0; {количество членов ряда} a:= x; { a - первый член ряда }

Sum:= a; { Sum - первая частичная сумма ряда }

42

while (abs(a) > eps) and (i<=m_dop) do begin

i:= i+1;

mnog:= -x*x/(2*i*(2*i+1)); a:= a*mnog;

Sum:= Sum+a; end;

if i<=m_dop then

begin writeln(Rus('Контрольное значение функции'),' sin(x)= ', y:12:8); writeln(Rus('Приближенное значение функции = '),Sum:12:8); writeln(Rus('при количестве членов ряда='), i );

end else

begin writeln(Rus('Количество членов ряда превосходит максимально допустимое'));

writeln(Rus('Требуемая точность вычислений не достигнута'));

end;

readln;

end.

Дадим краткую сравнительную характеристику циклических операторов:

Таблица 4

 

WHILE

 

REPEAT…UNTIL

 

FOR

 

Определе-

До начала цикла должны быть сде-

Начальная

установка пе-

ние пара-

ланы начальные установки пере-

ременной счетчика циклов

метров

менных,

управляющих

условием

до заголовка не требуется

цикла

цикла, для корректного входа в

 

 

 

 

 

цикл

 

 

 

 

 

 

 

 

 

Параметры

В теле цикла должны присутство-

Изменение

в

теле

цикла

цикла

вать операторы, изменяющие пе-

значений

 

переменных,

 

ременные условия так, чтобы цикл

стоящих в заголовке цик-

 

через

некоторое

число

итераций

ла, не разрешается

 

 

завершился.

 

 

 

 

 

 

Работа

Цикл

работает

 

Цикл

работает

Количество итераций цик-

цикла

пока

условие

 

пока

условие

ла неизменно и

точно

 

истинно

(пока

 

ложно

(пока

определяется

значениями

 

True)

 

 

 

False).

 

нижней и верхней границ и

 

 

 

 

 

 

 

шага цикла

 

 

 

Условие

Цикл

заверша-

 

Цикл завершает-

Нормальный

ход

работы

 

 

 

 

 

43

 

 

 

 

 

завершения

ется,

когда

ся, когда условие

цикла может быть нару-

цикла

условие стано-

становится

ис-

шен оператором goto или

 

вится

ложным

тинным

(до

процедурами

BREAK и

 

(до False).

 

True).

 

 

CONTINUE

 

Минималь-

Цикл может не

Цикл

обязатель-

Цикл может не выпол-

ное количе-

выполниться

но выполняется

ниться ни разу, если шаг

ство

ни

разу,

если

как

минимум

цикла будет изменять зна-

выполне-

исходное

зна-

один раз

 

чение счетчика от нижней

ния тела

чение

условия

 

 

 

границы

в

направлении,

цикла

при

 

входе в

 

 

 

противоположном верхней

 

цикл

равно

 

 

 

границе

 

 

 

False.

 

 

 

 

 

 

 

 

Условие

Если

в

теле

Независимо

от

Если в теле цикла требует-

использо-

цикла

требует-

количества

опе-

ся выполнить более одного

вания со-

ся

выполнить

раторов в

теле

оператора,

то необходимо

ставного

более

одного

цикла

использо-

использовать

составной

оператора

оператора,

то

вание составного

оператор

 

 

тела цикла

необходимо

оператора

не

 

 

 

 

использовать

требуется

 

 

 

 

 

составной

опе-

 

 

 

 

 

 

 

ратор

 

 

 

 

 

 

 

 

12.Метки.ОператорGOTO.ПроцедураHalt

Метка - это идентификатор, отмечающий определенный оператор исходного кода. Меткой может быть любой правильный идентификатор или целые числа от 0 до 9999, они могут записываться перед любым выполняемым оператором и отделяются от него двоеточием. Оператор может иметь любое количество меток. Все метки, использованные в программе, должны быть описаны в разделе описаний с ключевым словом LABEL. В одном операторе LABEL можно описать несколько меток, тогда они разделяются запятыми. Оператор безусловного перехода

GOTO метка;

передает управление оператору с соответствующей меткой, при этом все операторы, расположенные между оператором GOTO и оператором, которому передается управление, не выполняются. С помощью оператора GOTO нельзя передать управление внутрь цикла, внутрь условного оператора и внутрь оператора выбора.

Общепризнано, что оператор GOTO является вредным оператором, он усложняет алгоритмы, затрудняет чтение программы и является источ-

44

writeln('ошибка !')
begin x:=sqrt(x); write(x:5:2); end;

ником ошибок. Постарайтесь не применять этот оператор в своих программах.

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

var x : single;

begin write('введите число '); read(x); x:=sqrt(x); write(x:5:2);

end.

Если введено отрицательное число, то в третьем операторе программы произойдет аварийное прерывание. Стремясь избежать этого, мы могли бы записать программу в виде:

var x : single; label finish;

begin write('введите число '); read(x); if x<0 then goto finish; x:=sqrt(x); write(x:5:2);

finish: end.

Однако можно не использовать GOTO : var x : single;

begin write('введите число '); read(x); if x<0

then else

end.

Как видите, программа даже стала лучше, т.к. теперь она сообщает о неправильном вводе. Но она все-таки имеет один недостаток - условный оператор усложнил структуру программы.

13.Интервальныетипыданных.ОператорTYPE.Массивы

Интервальный тип - это некоторый подтип порядкового типа данных (вспомним, что порядковые типы - это ShortInt, Byte, Integer, Word, LongInt, Char и Boolean). Пусть, например, некоторая переменная в программе может принимать значения от -1 до 99. Мы могли бы описать ее как Long–Int или Integer (хотя это излишне!), могли бы описать ее как ShortInt, что достаточно разумно. Но можно создать для нее и специальный тип данных, объединяющий только числа от -1 до 99:

45

var x : -1..99;

Вместо имени одного из стандартных типов мы использовали в описании переменной построенный нами собственный интервальный тип. Таким образом, описанная переменная x может принимать только значения -1, 0, 1, ..., 99 , в остальном она ничем не отличается от других целых переменных. Ее можно вводить, выводить, использовать в качестве переменной цикла, подставлять в выражения и т.п. Любой интервальный тип есть подтип некоторого стандартного базового типа, в нашем случае - типа ShortInt. Но если бы мы стали использовать интервальный тип -1..200 , то он бы уже был подтипом типа Integer, а 0..200 -подтипом типа Byte. Компилятор Паскаля самостоятельно анализирует интервальные типы и подбирает для них минимальный подходящий базовый тип. Это нужно знать, чтобы определять размер и способ кодировки ваших переменных. Вы можете выполнить оператор

write('переменная x:-1..99 занимает ',sizeof(x),' байт');

и убедиться, что ее размер действительно равен 1.

В качестве базового типа можно использовать не только арифметические типы, но и типы Char и Boolean (правда, в последнем случае это довольно бессмысленно). Опишем, например, переменную, значением которой могут быть только маленькие латинские буквы:

var letter : 'a'..'z';

В общем случае интервальный тип описывается как

константное выражение 1 .. константное выражение 2

где оба выражения имеют один порядковый тип и второе из них не меньше первого. Созданным вами типам вы можете давать имена, для этого используется оператор TYPE:

TYPE имя типа=описание типа;

Операторы TYPE так же, как и все другие операторы описания, записываются в разделе описаний. В программе может быть сколько угодно операторов TYPE, и их можно чередовать с другими операторами описания, но любые идентификаторы, использованные в описании типа, должны быть описаны раньше. После того, как некоторый тип получил имя, вы в дальнейшем можете пользоваться этим именем вместо полного описания типа: const tmin=-5;

tmax=15; type t_range_type=tmin..tmax; var t:t_range_type;

type t_range_subtype=tmin+3..tmax-5; var t1:t_range_subtype;

Заметим, что хорошим тоном в программировании считается всегда давать мнемонические имена объявляемым типам.

46

Теперь, зная об интервальных типах, мы можем говорить о массивах. Массив во всех языках программирования - это множество индексированных (пронумерованных) однотипных элементов. В Паскале описание одномерного массива имеет вид:

ARRAY [тип индекса] OF тип элемента

Здесь тип индекса ShortInt, Byte, Char, Boolean или интервальный тип; тип элемента - любой тип, в том числе и массив. Вы заметили, что не все порядковые типы можно использовать как тип индекса, это не значит, что, например, тип Word чем-то хуже типа Byte. Такое ограничение обусловлено тем, что в Паскале никакой объект не может иметь размер больше (64К - 2) байта, или 65534 байта. Это ограничение действует и для интервальных типов, так вы можете описать массив var a : array[1..65534] of byte; но не массив var a : array[1..65535] of byte; и не массив var a : array[1..33000] of word;

Больше никаких ограничений на тип индекса не накладывается. Тип элементов массива может быть любым - целочисленным, вещественным, символьным, логическим, интервальным. Элементы массива могут быть массивами, тогда вы получите массив размерностью больше чем 1. Опишем несколько массивов:

var a : array[char] of 1..5;

-массив из 256 элементов, каждый из которых есть целое число от 1 до 5, индексы элементов изменяются от #0 до #255;

const max = 99; min = 10; type nums = min..max;

type arraytype = array[-10..0] of nums; var a : arraytype;

-массив из 11 элементов с индексами от -10 до 0, каждый элемент - целое положительное число из двух цифр;

type indextype = 'a'..'z';

var a: array [indextype] of boolean;

-массив из 26 элементов с индексами от 'a' до 'z', каждый элемент - логическая переменная.

В программе вы можете использовать как массивы целиком, так и отдельные элементы массивов. Элемент одномерного массива записывается в виде:

имя массива [индексное выражение]

Индексное выражение - это любое выражение соответствующего типа. Если элемент массива - не массив, то с ним можно выполнять любые операции, разрешенные для простых переменных соответствующего типа. Целому массиву можно лишь присваивать массив того же типа. Заметим, что если массивы описаны в программе таким образом:

47

var

a

: array[1..3] of single;

 

b,c,d : array[1..3] of single;

type massiv=array[1..3] of single;

var

e,f : massiv;

 

g

: massiv;

 

h,i : massiv;

то массивы b,c,d - однотипные и массивы e,f,g,h,i тоже однотипные, но массивы a и b (a и c,a и d) имеют разный тип; и массивы b (c,d,a) и e (f,g,h,i) тоже имеют разный тип! Компилятор считает, что две переменные имеют один и тот же тип, только если они описаны в одном операторе через запятую, либо имена их типов одинаковы! Запомните это очень важное правило.

Рассмотрим пример: программа вводит массив из N целых чисел (количество элементов и способ заполнения определяет пользователь, N не превосходит 100), и определяет сумму элементов массива, наибольший (наименьший) элемент, создает новые массивы, удовлетворяющие некоторому условию.

const Nmax=100;

type IndexType = 1..Nmax;

Mas = array [IndexType] of integer; var i,dim:1..100;

a,b,c:Mas;

N,j,k:Byte; S,a_max:Integer;

begin repeat

writeln('Введите размерность массива от 1 до ',Nmax); readln(dim)

until (dim>=1) and (dim<=Nmax); repeat

writeln('Заполнить массив вручную (1) или случайным образом (2)?); readln(N)

until (N<>1) or (N<>2); if N=1

then for i:= 1 to dim do

begin write(' Введите A[ ' , i , ' ] = ' ); readln(a[i])

end

else begin randomize;

for i:= 1 to dim do

a[i]:= - 30 + Random(71);

48

end;

writeln; writeln('Массив А'); for i:=1 to dim do

write(a[i]:3,' '); writeln;

s:= 0;

for i:= 1 to dim do s:= s + a[i];

writeln('Сумма элементов массива = ',S); a_max:= a[1];

for i:= 1 to 100 do if a[i] > a_max

then

begin a_max:= a[i]; j:= i

end;

writeln('Наибольший элемент массива a[',j,']= ',a_max); j:= 0; k:= 0;

for i:=1 to dim do if a[i] >= 0

then begin j:= j+1; b[j]:= a[i]

end

else begin k:= k+1; c[k]:= a[i]

end;

writeln('Cоздание новых массивов с элементами: b[j] >=0, c[k] <0'); writeln('Массив b:');

if j>0

then

for i:=1 to j do write(b[i]:3,' ')

else

 

writeln('массив не содержит элементов');

writeln; writeln(('Массив c:');

if k>0

 

then

for i:=1 to k do write(c[i]:3,' ')

else

writeln(Rus('массив не содержит элементов'));

writeln; readln end.

Теперь перейдем к рассмотрению многомерных массивов. Размерностью, или количеством измерений массива, называется количество индексов у элемента массива, но не количество элементов в массиве. Мы уже

49

знаем, что элемент массива может быть массивом, поэтому двумерный массив можно описать, например, так :

var a : array[1..10] of array[1..20] of single;

или так:

 

var a : array[1..10, 1..20] of single;

 

При обращении к ячейкам массива можно использовать в программе

запись вида a[i][j] или a[i,j].

 

Напишем программу, перемножающую две квадратные вещественные

матрицы:

const Nmax=100;

type

IndexType = 1..Nmax;

 

Mas = array [IndexType] of integer;

 

Mas_d = array [IndexType,IndexType] of integer;

var

a,c : Mas_d;

 

ss,b : Mas;

 

N,m,a1,i,j,dim,l : Integer;

begin

 

 

repeat

 

writeln('Введите размерность массива от 1 до ',Nmax-1);

 

readln(dim)

 

until (dim>=1) and (dim<Nmax);

 

repeat

 

writeln('Заполнить массив вручную (1) или случ образом (2)?);

 

readln(N)

 

until (N<>1) or (N<>2);

 

if N=1

then

for i:= 1 to dim

do

 

 

for j:=1 to dim

do

 

 

begin

write(' Введите A[ ' , i , ',',j,' ] = ' );

 

 

readln(a[i,j])

 

 

end

 

 

 

else

 

 

 

 

 

begin

randomize;

 

 

 

for i:= 1 to dim

do

 

 

for j:=1 to dim

do

 

 

 

a[i,j]:= - 30 + Random(71);

 

end;

 

 

 

writeln;

 

 

 

 

writeln('Массив А');

 

 

for i:=1 to dim do

 

 

 

begin

for j:=1 to dim

do

 

 

 

 

50

write(a[i,j]:3,' '); writeln;

end;

for i:= 1 to dim do begin SS[i]:= 0;

for j:= 1 to dim do SS[i]:= SS[i] + A[i, j] ;

writeln('Сумма элементов ',i,' строки = ',SS[i]);

end;

readln;

writeln('Обмен местами первой и последней строки'); for j:= 1 to dim do

begin a1:=a[1,j]; a[1,j]:=a[dim,j]; a[dim,j]:=a1

end;

writeln('Измененный массив А'); for i:=1 to dim do

begin for j:=1 to dim do write(a[i,j]:3,' ');

writeln;

end;

readln;

repeat writeln('Введите номер столбца (от 1 до ',dim,')'); readln(M)

until (m>=1) and (m<=dim);

writeln('Вставка данных из одномерного массива B в столбец с

 

номером ', M,' массива А'); writeln('Массив B');

for i:= 1 to dim do

begin

b[i]:= - 30 + Random(71);

 

write(b[i]:3,' ');

end;

 

for i:= 1 to dim do

begin

for j:=dim+1 downto M+1 do

 

A[i,j]:=A[i,j-1];

 

A[i,M]:=B[i]

end; writeln('Массив А'); for i:=1 to dim do

begin for j:=1 to dim+1 do write(a[i,j]:3,' ');

51

writeln;

end;

writeln('Создание массива C из четных столбцов массива A'); for j:= 1 to dim do

if (j Mod 2) = 0

then for i:= 1 to dim do C[i,j Div 2]:= A[i,j];

writeln('Массив C')); l:=dim div 2;

for i:=1 to dim do begin for j:=1 to l do

write(c[i,j]:3,' '); writeln;

end;

readln;

end.

Каким именно образом сгруппировать значения элементов, легко понять, вспомнив, что массив ARRAY[1..3,1..2] OF Single есть на самом деле компактная запись описания ARRAY[1..3] OF ARRAY[1..2] OF Single.

14.Защитаотошибокиотладкапрограмм.Стандартнаятехникаотладки

Впрограммах принято выделять три вида ошибок. Ошибки времени выполнения (Runtime errors), как следует из названия, происходят во время выполнения программы. Во время разработки программы могут воз-

никать синтаксические ошибки и логические ошибки. Под синтаксисом подразумевается структура кода - написание имен и ключевых слов, правила грамматики, пунктуация и т.д. Приведенные ниже фрагменты кода содержат синтаксические ошибки. Сможете ли вы обнаружить их?

if x>y

var perem_x,perem_y,rez:integer;

then m:=x;

begin

else m:=y;

perem_x:=1; perm_y:=2;

 

rez:= perem_x+ perem_y;

Обнаружение таких ошибок не всегда очевидно, особенно для начинающих программистов. В первом примере перед else стоит “;” и Delphi автоматически обнаружит ошибку. Появится сообщение: ';' not allowed before 'ELSE'. Во втором примере в операторе присваивания неправильно введено имя perm_y (вместо perem_y) и сообщение об ошибке будет следу-

ющим: Undeclared identifier: 'perm_y'.

52

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

var x:single; begin

x:=10.0; repeat

writeln(x); x:=x-3.0/4.0;

until x=0.0;

Дело в том, что переменная х никогда не будет равна 0, и поэтому цикл будет бесконечным. Чтобы прекратить выполнение зациклившейся программы, выберите в главном меню Delphi команду Run Program Reset или воспользуйтесь комбинацией клавиш <Ctrl+F2> при активном окне выполнения программы. А для исправления ошибки нужно заменить предложение until следующим: until x <= 0;

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

Чтобы предотвратить ошибки времени выполнения, программист должен предусмотреть проверку всех данных, вводимых пользователем. Защита от ошибок предотвращает крах работы программы в результате случайного или злонамеренного ввода неправильных данных.

Как же должен поступать программист, когда при выполнении его программы возникают ошибки? Прежде всего, нужно локализовать ошибку, то есть найти оператор, в котором она произошла. В этом вам может помочь среда Delphi, если в ней правильно установлены опции компилятора. Опции компилятора позволяют изменять режим компиляции (выберите в главном меню Delphi команду Project Options Compiler).

Пока нас будут интересовать лишь следующие опции: Range checking, I/O checking, Overflow checking, Debug information. Если они включены, то настройка среды благоприятна для отладки вашей программы. Если они выключены, то их следует включить, а также можно вставить в любом месте исходного кода, где допустима вставка комментариев. Опции записываются в программе в виде: {$ буква + / - }. Каждой опции соответствует своя буква (это заглавные буквы в названии каждой опции), символ "+"

53

означает включить, а символ "-" - выключить. В программе можно задать одну опцию, например, {$O+} или несколько опций -{$R+,I-} . Некоторые опции можно записывать только в самом начале кода программы, другие могут размещаться в любом ее месте.

Опция Range checking (R) отвечает за контроль ошибок Range check error (ошибка проверки диапазона). Они возникают, например, при попытке присвоить целочисленной переменной недопустимое значение, или при использовании недопустимого индексного выражения для элемента любого массива.

Опция Overflow checking (C) отвечает за контроль ошибок Ariphmetic overflow (целочисленное переполнение), возникающих при выполнении арифметической операции над целыми числами, когда результат операции выходит за границы соответствующего типа.

Опция I/O cheking (I) отвечает за контроль ошибок ввода-вывода. Опция Debug information (D) включает в код программы отладочную

информацию, что позволяет среде при аварийном завершении программы показать курсором оператор, в котором произошла ошибка.

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

Наиболее простой прием отладки — дамп данных. Если программа не работает как ожидается, программист может добавить строки кода, выводящие промежуточные значения выбранных переменных. Такой вывод значений переменных называется дампом данных. Отслеживая изменение переменных, программист может обнаружить неполадку в исходном коде. Вспомним последний пример логической ошибки. Фрагмент кода этого примера фактически выполняет дамп данных: значения переменной х выводятся на экран на каждой итерации цикла repeat. Это позволяет сразу увидеть, что переменная х уменьшается бесконечно.

Другой стандартный прием — ручное выполнение кода. В этом случае программист работает как компьютер. Ручное выполнение, или отслеживание кода во многих случаях оказывается незаменимым. С помощью карандаша и бумаги программист моделирует компьютерную память и, последовательно выполняя каждую строку кода, берет на себя функции микропроцессора. Конечно, человек не может сравниться с микропроцессором в быстродействии и объеме обрабатываемых данных, поэтому для использования такого приема нужно максимально упростить задачу, не потеряв при этом существенных деталей.

Какие преимущества имеет ручное выполнение по сравнению с дампом данных? Во-первых, такой способ не требует применения компьютера — программу можно отлаживать где угодно, для этого достаточно иметь рас-

54

печатку кода, карандаш и бумагу. Во-вторых, часто программист может быстрее обнаружить логическую ошибку, отслеживая код вручную, чем с помощью дампа данных. Рассмотрим технику ручного выполнения кода на примере модифицированной версии примера с логической ошибкой.

var x:single; begin

x:=5; repeat

x:=x-2; writeln(x:5:2);

until x=0;

Номер итерации

х

Выводимое значение

0

5

пусто

1

3

3

2

1

1

3

-1

-1

На третьей итерации программист видит, что значение х стало равным -1, и понимает, что программа вошла в бесконечный цикл. Это довольно простой пример. На практике чаще всего реализовать ручное вычисление сложнее и на это уходит намного больше времени.

Среда разработки Delphi содержит встроенный отладчик, значительно облегчающий программисту отслеживание кода и обнаружение ошибок. Чтобы отладчик Delphi можно было использовать, нужно убедиться, что режим интегрированной отладки включен. Для этого в главном меню выбе-

рите команду Tools Debugger Options (Инструменты Режимы отладчика) и на закладке General (Общие) убедитесь, что установлен флажок Integrating debugging (Интегрированная отладка) в левой нижней части диалогового окна.

Команды отладчика доступны из меню Run

Рис. 4 и на панели инструментов отладки Debug (рис. 5).

Рис. 5 Для активизации панели Debug щелкните правой кнопкой мыши на главном меню или на любой видимой панели инструментов (при этом всплывает контекстное меню Toolbar) и выберите Debug. На рис. 4 показаны пиктограммы, которые находятся на панели инструментов отладки по умолчанию. Кроме того, быстро получить доступ к командам отладчика можно из окна редактора кода. Щелкните правой кнопкой мыши в любом месте окна редактора кода, при этом всплывет контекстное меню, в пункте

Debug которого перечислены все доступные команды отладчика.

Отладчик Delphi предоставляет полуавтоматические способы обнаружения ошибок. С его помощью программист может наблюдать (Watch) значения определенных переменных или выражений в процессе выполнения программы, причем для этого не нужно вставлять в код операторы дампа данных. Кроме того, с помощью отладчика можно остановить выполнение программы в указанной программистом точке прерывания (breakpoint) или задать пошаговое выполнение программы. Обратите внимание: отладчик - это специальная программа (утилита) времени разра-

55

56

ботки, команды отладчика нельзя использовать в программе, выполняющейся за пределами среды разработки Delphi.

Приведем короткое описание точек прерывания (breakpoint), пошагового выполнения (step) и окон наблюдения (watch). Точки прерывания выполнения исходного кода устанавливаются программистом слева от строки кода, перед которой нужно остановить выполнение. Их можно устанавливать только на выполнимых строках кода (нельзя на пустых строках, операторах объявления и комментариях). Когда при выполнении исходного кода встречается точка прерывания, программа временно останавливается, пока программист не выберет команду Run меню Run или не щелкнет на кнопке Run панели отладки.

Когда программа остановлена, программист может просмотреть и/или изменить значения выражений в окне Evaluate/Modify (Оценить/

Изменить).

В процессе интерактивной отладки значения переменных и выражений можно просматривать также с помощью всплывающего окна указателя (tooltip). Для этого нужно всего лишь поместить указатель мыши на переменную или выражение в редакторе кода и через одну секунду (по умолчанию) Delphi выведет во всплывшем окне значение этой переменной или выражения.

Для включения режима вывода всплывающего окна указателя выберите в главном меню команду Tools Editor Options... (Инстру-

менты Параметры редактора...),

откроется окно Editor

Options. Затем во вкладке Code Insight

(Подсказка кода) уста-

новите флажок Tooltip expression evaluation (Оценивание и вывод выражений во всплывающее окно). Обратите внимание: при

этом должен быть установлен режим Integrated Debugging (Инте-

грированная отладка).

В отладчике Delphi есть две операции пошагового выполнения. Операция Trace Into (Отследить в) выполняет за один шаг один оператор исходного кода. Например, если оператором является вызов подпрограммы, то следующим будет выполнен первый оператор вызванной подпрограммы, после чего программа опять остановится. В то же время операция Step Over (Перешагнуть через) выполняет всю подпрограмму как одно целое - выполнение программы остановится на следующем операторе текущей подпрограммы независимо от того, является ли он вызовом подпрограммы.

Наблюдаемое выражение (watch expression) — это определенное программистом выражение (оно может не входить в исходный код), значение которого программист может наблюдать во время выполнения программы. Значения наблюдаемых выражений видны в окне наблюдения (Watch window), в режиме прерываний они автоматически обновляются.

57

Кроме того, в окне локальных переменных (Local variable window) ав-

томатически выводятся значения всех объявленных локальных переменных текущей подпрограммы.

Чтобы практически освоить приемы работы с отладчиком Delphi, рассмотрим следующий пример. При этом настоятельно рекомендуем проделать все действия за компьютером.

1. Числа Фибоначчи определяются как последовательность значений an 1 an an 1 , где a1 1, a2 1 . Приведенная ниже подпрограмма выво-

дит числа Фибоначчи, значение которых меньше или равно 30 000, и квадраты каждого из этих чисел:

{Это функция, вычисляющая квадрат целого числа} function Square(value: Integer): Integer;

begin

Square := value * value; end;{ Square }

{Вывод чисел Фибоначчи, значения которых меньше, или равны

30000}

var num1,num2,sum: Integer; begin

num1 := 1; num2 := 1;

writeln(Rus('Число Фибоначчи Квадрат числа')); writeln(num1 :10, Square(num1) :15);

repeat

writeln(num2 :10, Square(num2) :15); sum := num1 + num2;

num1 := num2; num2 := sum;

until (num2 > 30000); readln

end.

2. Поставьте точку прерывания на первую строку тела цикла repeat. Для этого поместите на этой строке курсор мыши, щелкните правой кнопкой и во всплывшем меню выберите команду Debug Toggle

Breakpoint (Отладка Переключение точки прерывания).

Другой способ сделать это — установить на строку указатель мыши и нажать клавишу <F5>, которая переключает точку прерывания (чтобы увидеть, как она переключается, нажмите <F5> несколько раз). Обратите внимание: редактор кода выделяет строку и располагает слева от нее красный

58

кружочек, как показано на рис. 6. Еще один способ установки точки прерывания — щелкнуть левой кнопкой мыши на серой полосе слева от строк кода (там, где устанавливается красный кружочек).

Рис.6 3. Выполните программу. Как видите, отладчик Delphi остановил выполнение программы в точке прерывания и вывел окно редактора кода (рис. 7). В окне редактора отмечена следующая строка, которая пока не выполнена, при этом на левой серой полосе появилась зеленая стрелка - указа-

тель инструкций (instruction pointer).

Рис.7

59

4. Выполняйте строки кода путем последовательных щелчков на кнопке Trace Into (Отследить в) или нажатий клавиши <F7>. Обратите внимание: указатель инструкции попадает внутрь подпрограммы Square (). Сделав еще два щелчка, вернитесь в основную программу.

5. Установите наблюдение за переменными numl и value. Для этого выберите команду Run Add Watch... (Выполнить Добавить наблюдение) или нажмите клавиши <Ctrl+F5>. Появится окно Watch Properties (Свойства наблюдения), показанное на рис. 8.

Рис.8 В области Expression (Выражение) введите num2 и нажмите клавишу <Enter>. Сделайте то же самое для переменной value. Еще один способ быстрой установки наблюдения за выражением - щелкнуть в редакторе кода правой кнопкой мыши на нужном выражении и во всплывшем меню вы-

брать команду Debug Add Watch at Cursor (Отладка

Установка наблюдения в точке указателя мыши).

6.Чтобы отследить работу программы, несколько раз нажмите клавишу <F7>. Обратите внимание: в процессе пошагового выполнения значения

вокне наблюдений изменяются. Продолжая нажимать клавишу <F7>, дождитесь момента, когда указатель инструкций попадет в точку прерывания.

7.Чтобы перешагнуть через вызов функции Square (), нажмите клавишу <F8>. Обратите внимание: указатель инструкций не вошел в функцию Square (), а переместился в следующую строку кода текущей подпрограммы.

8.Удалите все наблюдения за значениями переменных. Для этого щелкните правой кнопкой мыши на окне наблюдений и во всплывшем меню выберите команду Delete All Watches (Удалить все наблю-

дения). Закройте окно наблюдений Watch List.

9. В редакторе коде щелкните на строке с точкой прерывания. Нажав клавишу <F5>, отключите точку прерывания.

60

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