Главная Случайная страница Контакты | Мы поможем в написании вашей работы! | ||
|
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,' ');
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 then m:=x; else m:=y; | var perem_x,perem_y,rez:integer; begin 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'.
Логические ошбки - это ошибки, связанные с логикой программы. При выполнении программы компьютер делает только то, что ему предписано сделать. Но иногда действия, заложенные в исходном коде, существенно отличаются от того, что программист предполагает. Посмотрите на следующий фрагмент программы и попытайтесь найти логическую ошибку:
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. Если они включены, то настройка среды благоприятна для отладки вашей программы. Если они выключены, то их следует включить, а также можно вставить в любом месте исходного кода, где допустима вставка комментариев. Опции записываются в программе в виде: {$ буква + / - }. Каждой опции соответствует своя буква (это заглавные буквы в названии каждой опции), символ "+" означает включить, а символ "-" - выключить. В программе можно задать одну опцию, например, {$O+} или несколько опций - {$R+,I-}. Некоторые опции можно записывать только в самом начале кода программы, другие могут размещаться в любом ее месте.
Опция Range checking (R) отвечает за контроль ошибок Range check error ( ошибка проверки диапазона). Они возникают, например, при попытке присвоить целочисленной переменной недопустимое значение, или при использовании недопустимого индексного выражения для элемента любого массива.
Опция Overflow checking (C) отвечает за контроль ошибок Ariphmetic overflow ( целочисленное переполнение), возникающих при выполнении арифметической операции над целыми числами, когда результат операции выходит за границы соответствующего типа.
Опция I/O cheking (I) отвечает за контроль ошибок ввода-вывода.
Опция Debug information (D) включает в код программы отладочную информацию, что позволяет среде при аварийном завершении программы показать курсором оператор, в котором произошла ошибка.
Стандартными приемами отладки являются дамп данных (выгрузка, вывод данных на экран), ручное выполнение кода и использование встроенного отладчика Delphi.
Наиболее простой прием отладки — дамп данных. Если программа не работает как ожидается, программист может добавить строки кода, выводящие промежуточные значения выбранных переменных. Такой вывод значений переменных называется дампом данных. Отслеживая изменение переменных, программист может обнаружить неполадку в исходном коде. Вспомним последний пример логической ошибки. Фрагмент кода этого примера фактически выполняет дамп данных: значения переменной х выводятся на экран на каждой итерации цикла repeat. Это позволяет сразу увидеть, что переменная х уменьшается бесконечно.
Другой стандартный прием — ручное выполнение кода. В этом случае программист работает как компьютер. Ручное выполнение, или отслеживание кода во многих случаях оказывается незаменимым. С помощью карандаша и бумаги программист моделирует компьютерную память и, последовательно выполняя каждую строку кода, берет на себя функции микропроцессора. Конечно, человек не может сравниться с микропроцессором в быстродействии и объеме обрабатываемых данных, поэтому для использования такого приема нужно максимально упростить задачу, не потеряв при этом существенных деталей.
Какие преимущества имеет ручное выполнение по сравнению с дампом данных? Во-первых, такой способ не требует применения компьютера — программу можно отлаживать где угодно, для этого достаточно иметь распечатку кода, карандаш и бумагу. Во-вторых, часто программист может быстрее обнаружить логическую ошибку, отслеживая код вручную, чем с помощью дампа данных. Рассмотрим технику ручного выполнения кода на примере модифицированной версии примера с логической ошибкой.
var x:single;
begin
x:=5;
repeat
x:=x-2;
writeln(x:5:2);
until x=0;
Номер итерации | х | Выводимое значение |
пусто | ||
-1 | -1 |
На третьей итерации программист видит, что значение х стало равным -1, и понимает, что программа вошла в бесконечный цикл. Это довольно простой пример. На практике чаще всего реализовать ручное вычисление сложнее и на это уходит намного больше времени.
Среда разработки Delphi содержит встроенный отладчик, значительно облегчающий программисту отслеживание кода и обнаружение ошибок. Чтобы отладчик Delphi можно было использовать, нужно убедиться, что режим интегрированной отладки включен. Для этого в главном меню выберите команду Tools Debugger Options (Инструменты Режимы отладчика) и на закладке General (Общие) убедитесь, что установлен флажок Integrating debugging (Интегрированная отладка) в левой нижней части диалогового окна.
Команды отладчика доступны из меню Run
Рис. 4
и на панели инструментов отладки Debug (рис. 5).
Рис. 5
Для активизации панели Debug щелкните правой кнопкой мыши на главном меню или на любой видимой панели инструментов (при этом всплывает контекстное меню Toolbar) и выберите Debug. На рис. 4 показаны пиктограммы, которые находятся на панели инструментов отладки по умолчанию. Кроме того, быстро получить доступ к командам отладчика можно из окна редактора кода. Щелкните правой кнопкой мыши в любом месте окна редактора кода, при этом всплывет контекстное меню, в пункте Debug которого перечислены все доступные команды отладчика.
Отладчик Delphi предоставляет полуавтоматические способы обнаружения ошибок. С его помощью программист может наблюдать (Watch) значения определенных переменных или выражений в процессе выполнения программы, причем для этого не нужно вставлять в код операторы дампа данных. Кроме того, с помощью отладчика можно остановить выполнение программы в указанной программистом точке прерывания (breakpoint) или задать пошаговое выполнение программы. Обратите внимание: отладчик - это специальная программа (утилита) времени разработки, команды отладчика нельзя использовать в программе, выполняющейся за пределами среды разработки 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), в режиме прерываний они автоматически обновляются. Кроме того, в окне локальных переменных (Local variable window) автоматически выводятся значения всех объявленных локальных переменных текущей подпрограммы.
Чтобы практически освоить приемы работы с отладчиком Delphi, рассмотрим следующий пример. При этом настоятельно рекомендуем проделать все действия за компьютером.
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> несколько раз). Обратите внимание: редактор кода выделяет строку и располагает слева от нее красный кружочек, как показано на рис. 6. Еще один способ установки точки прерывания — щелкнуть левой кнопкой мыши на серой полосе слева от строк кода (там, где устанавливается красный кружочек).
Рис.6
3. Выполните программу. Как видите, отладчик Delphi остановил выполнение программы в точке прерывания и вывел окно редактора кода (рис. 7). В окне редактора отмечена следующая строка, которая пока не выполнена, при этом на левой серой полосе появилась зеленая стрелка - указатель инструкций (instruction pointer).
Рис.7
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>, отключите точку прерывания.
10. Нажав клавишу <F9>, возобновите выполнение программы. Затем закройте окно с результатами выполнения программы.
15. Процедуры и функции. Сфера действия описаний
В языке Object Pascal (как вы уже поняли из предыдущего материала) существуют понятия процедуры и функции. Процедуры и функции можно определить как замкнутые программные единицы, реализующие некоторый алгоритм. Разработка функций и процедур необходима при многократном использовании в разных местах программы или в нескольких программах блока операторов, выполняющих однотипные действия, например, расчет значений сложной функции при различных значениях аргумента.
Процедуры и функции, определяемые программистом, приводятся в разделе описания основной программы. Процедуры и функции имеют заголовок, раздел описания и раздел операторов. Раздел операторов начинается с BEGIN и заканчивается END; (но не END.). Единственным новым оператором для нас будет оператор заголовка, с которого начинается всякая процедура и функция. Заголовок процедуры состоит из служебного слова Procedure, имени процедуры и списка параметров
PROCEDURE имя ( список параметров );
Здесь имя - имя процедуры (любой идентификатор), список параметров может отсутствовать, но если он есть, записывается в круглых скобках после имени процедуры и имеет вид:
[ VAR ] имя,... имя: тип;...........................; [ VAR ] имя,... имя: тип
Здесь имя - имена параметров, каждый параметр может использоваться внутри процедуры как обычная переменная соответствующего типа. Тип - имя типа, но не описание пользовательского типа; скажем, описание параметра в виде x:1..5 неверно, но, если выше описан соответствующий тип: TYPE MyType=1..5, то параметр можно описать в виде x:MyType. Ключевое слово VAR перед описанием параметров означает в данном случае, что все параметры до "; " или до " ) " - параметры-переменные; если же VAR отсутствует, то параметры являются параметрами-значениями. Смысл этих понятий мы рассмотрим чуть позже.
Процедуры вызываются в других процедурах и функциях с помощью уже известного вам оператора вызова:
имя ( список аргументов );
Список аргументов задается в том и только в том случае, когда в заголовке процедуры задан список параметров. Аргументы в списке разделяются запятыми и могут быть любыми выражениями, если соответствующий параметр есть параметр-значение, или только именами переменных, если соответствующий параметр есть параметр-переменная. Количество аргументов всегда должно совпадать с количеством параметров, и тип аргумента должен быть таким же, как тип параметра. При вызове процедуры значение соответствующих аргументов передается параметрам, и таким образом процедура получает информацию из вызывающей процедуры или функции. Запишем программу, использующую процедуру, которая будет аккуратно выводить значение переменной:
procedure outvar(x:single; name:char);
begin
writeln(Rus('переменная '),name,Rus(' равна '),x:10:4);
end; {outvar}
var a,b,c,d: single;
begin
writeln(Rus('Введите переменные a,b,c,d '));
readln(a,b,c,d);
outvar(a,'a'); outvar(b,'b');
outvar(c,'c'); outvar(d,'d');
readln
end.
Наша процедура OutVar получает из главной процедуры вещественное число х и символ Name, но ничего не передает обратно. Теперь попробуем написать процедуру, которая по заданным значениям x и y вычисляет cos(x)+cos(y) и cos(x)-cos(y):
procedure t(x,y:single; cplus,cminus:single);
begin cplus:=cos(x)+cos(y);
cminus:=cos(x)-cos(y);
end; {t}
var p,m:single;
begin
t(1.235,0.645,p,m);
writeln(p:7:3,m:7:3);
readln
end.
Запустим эту программу и - вместо правильного результата 1.129,-0.470 -получим в лучшем случае нули. Дело в том, что через параметры-значения (а Cplus и Cminus описаны в нашей процедуре как параметры-значения!) невозможно передать информацию из процедуры, но лишь в процедуру. Чтобы правильно решить нашу задачу, следует Cplus и Cminus описать в заголовке как параметры-переменные:
procedure t(x,y:single;var cplus,cminus:single);
begin cplus:=cos(x)+cos(y);
cminus:=cos(x)-cos(y);
end; {t}
Таким образом, входные параметры процедуры могут быть и параметрами - значениями и параметрами-переменными, а выходные параметры - только параметрами-переменными. Для того, чтобы глубже понять это правило, выясним, что же происходит с параметрами и аргументами при вызове процедуры. В момент вызова для каждого параметра-значения в специальной области памяти, называемой стеком (за контроль переполнения стека отвечает описанная выше опция компилятора Stack cheking), создается его копия - переменная соответствующего типа, которой присваивается значение аргумента. В дальнейшем процедура работает с этой копией, и при выходе из процедуры копия уничтожается. Таким образом, никакие изменения параметра-значения не могут быть известны за пределами процедуры. По-другому обрабатываются параметры-переменные: в процедуру передается не значение аргумента, а его адрес, и она работает с аргументом (теперь понятно, почему аргумент, соответствующий параметру-переменной, должен быть только именем переменной: он должен иметь адрес). Так что все изменения параметра на самом деле происходят с аргументом и известны в вызывающей процедуре.
Функция, в отличие от процедуры, всегда вычисляет некоторое значение скалярного типа, которое внутри функции должно быть присвоено имени функции. Заголовок функции имеет вид:
FUNCTION имя ( список параметров ): тип;
В остальном функции аналогичны процедурам. Обращение к функции осуществляется с помощью указателя функции: имя ( список параметров ) Указатель функции может использоваться как и любое другое выражение того же типа, но это не оператор. Запишем пример функции:
function Na3(x:LongInt):Boolean;
begin
Na3:=x MOD 3=0;
end; {Na3}
var L:LongInt;
begin write(Rus('Введите целое число '));
read(L);
write(Rus('Число '),L);
if not Na3(L)
then write(Rus(' не'));
writeln(Rus(' делится на 3!'));
readln; readln;
end.
В любой процедуре и функции можно использовать стандартную процедуру Exit без параметров для немедленного выхода в вызывающую процедуру.
Все процедуры и функции Паскаля являются рекурсивными, то есть могут вызывать сами себя, никаких усилий со стороны программиста для этого не требуется. В качестве примера запишем функцию, вычисляющую факториал числа:
function factorial(n:byte):single;
begin if n<=1 then factorial:=1
else factorial:=n*factorial(n-1);
end;
Теперь запишем этот алгоритм по-другому:
function factorial(n:byte):single;
var i:byte; f:single;
begin f:=1; for i:=2 to n do f:=f*i; factorial:=f;
end;
Но обратите внимание: в этих примерах предполагается, что число, для которого будет найдено значение факториала, не превосходит 255, а результат не будет превышать 3.4Е+38. Если мы попытаемся найти значение факториала для 34, то после выполнения соответствующей программы получим 2.952328Е+0038. Но вот попытка поиска значения факториала для 35 не завершится успехом. А если определить тип результата функции factorial как integer, то значения аргумента, для которых можно получить значение функции, соответствующее действительности, еще меньше. Подумайте, в чем здесь ошибка и как ее можно исправить.
Итак, мы знаем, что программа может содержать много процедур и функций, и в каждой из них могут быть описаны типы, константы и переменные. Но не все из них могут быть использованы в любом месте программы, каждое описание имеет строго определенные область видимости и время жизни. Область видимости – это фрагмент кода программы, в которых переменные, типы, константы могут быть использованы в выражениях. Время жизни – это интервал времени, в течение которого идентификатор существует в памяти компьютера.
Пусть подпрограмма А находится внутри подпрограммы В - условимся называть подпрограмму А внутренней по отношению к В, а подпрограмму В – внешней (объемлющей) по отношению к А. Если же ни подпрограмма А не находится внутри В, ни В не находится внутри А, то эти подпрограммы - независимые по отношению друг к другу. Идентификатор может быть использован только в своей области видимости. Область видимости описания любого идентификатора (например, переменной) включает ту подпрограмму, где он описан (начиная с места описания) и все внутренние подпрограммы, если там данный идентификатор не описан. В таблице 5 приведены правила, определяющие область видимости идентификаторов.
Таблица 5 | |
Место объявления идентификатора | Область видимости |
Объявление в подпрограмме | От точки объявления до конца текущей подпрограммы, включая все внутренние подпрограммы |
Объявление в программе | От точки объявления до конца текущей программы, включая все внутренние подпрограммы |
Область видимости переменной может быть локальной или глобальной. Переменная с локальной областью видимости называется локальной переменной, а с глобальной областью видимости – глобальной переменной. Локальную переменную можно использовать только в подпрограмме, в которой она объявлена, поэтому память для нее выделяется только при входе в подпрограмму и освобождается при выходе из нее. Интервал времени между этими событиями составляет время жизни локальной переменной. В то же время глобальные переменные доступны во всех частях программы, следовательно, они создаются в начале работы программы и удаляются из памяти при ее завершении.
Дата публикования: 2014-11-04; Прочитано: 316 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!