Отладка программ
Отладка — этап разработки компьютерной программы, на котором обнаруживают, локализуют и устраняют ошибки.
Типы ошибок
Ошибки компиляции или синтаксические ошибки встречаются, когда забывают объявить переменную, передают ошибочное количество параметров процедуры, при назначении действительного значения целочисленной переменной. Это означает, что записываются операторы, которые не согласуются с правилами языка.
Ошибки выполнения
Программа пользователя может содержать и логические ошибки. Это означает, что программа делает то, что ей указали вместо того, что хотелось бы. Может отсутствовать инициализация переменной; могут оказаться ошибочными вычисления; рисунки, изображенные на экране, выглядят неправильно; программа может просто работать не так, как было задумано. Такие ошибки находятся с большим трудом, и интегрированный отладчик поможет вам в этом случае наилучшим образом.
Существуют две взаимодополняющие технологии отладки.
Вот что пишут об этих двух подходах к отладке программы Брайан Керниган и Роб Пайк:
«Наш личный выбор — стараться не использовать отладчики, кроме как для просмотра стека вызовов или же значений пары переменных. Одна из причин этого заключается в том, что очень легко потеряться в деталях сложных структур данных и путей исполнения программы. Мы считаем пошаговый проход по программе менее продуктивным, чем усиленные размышления и код, проверяющий сам себя в критических точках.
Щёлканье по операторам занимает больше времени, чем просмотр сообщений операторов выдачи отладочной информации, расставленных в критических точках. Быстрее решить, куда поместить оператор отладочной выдачи, чем проходить шаг за шагом критические участки кода, даже предполагая, что мы знаем, где находятся такие участки. Более важно то, что отладочные операторы сохраняются в программе, а сессии отладчика переходящи.
«Отладка сложна и может занимать непредсказуемо долгое время, поэтому цель в том, чтобы миновать большую её часть. Технические приёмы, которые помогут уменьшить время отладки, включают хороший дизайн, хороший стиль, проверку граничных условий, проверку правильности исходных утверждений и разумности кода, защитное программирование, хорошо разработанные интерфейсы, ограниченное использование глобальных переменных, автоматические средства контроля и проверки. Грамм профилактики стоит тонны лечения.»
Отладка с использованием отладчика в программных средах
Некоторые ошибки времени выполнения (логические ошибки) незаметны и трудны для прослеживания. Другие ошибки могут скрываться за неуловимым взаимодействием разделов большой программы. В этих случаях необходимо интерактивное выполнение программы, во время которого производится наблюдение за значениями определенных переменных или выражений. Вам хотелось бы, чтобы Ваша программа останавливалась при достижении определенного места так, чтобы просмотреть, как она проработала этот кусок. Вам хотелось бы остановиться и изменить значения некоторых переменных во время выполнения программы, изменить определенный режим или проследить за реакцией программы. И вам хотелось бы сделать это в режиме, когда возможно быстрое редактирование, перекомпилирование и повторное выполнение программы.
Интегрированный отладчик Turbo Pascal имеет все описанные выше возможности и даже более того. Он представляет собой встроенную часть интегрированной усовершенствованной среды Turbo Pascal (IDE): для использования предлагаются две основные функции меню (Run, Debug), а также некоторые клавиши для команд отладчика.
Visual Studio
В данном разделе я попробую описать процесс отладки программы, написанной в среде «Visual Studio C# 2008 Express Edition». Все ошибки в коде программы, которые я сам же буду находить сделаны специально.
Цель: Разработать и отладить программу «АйСчитайка», которая будет производить поиск корней квадратного уравнения.
1)создаём новый проект
2)пишем пользовательский интерфейс
3)пишем сам код программы.
Спустя 10 минут я получил:
Как мы видим на скриншоте. Компилятор даже не позволил запустить написанную нами программу. И заботливо подчеркнул все найденные ошибки.
После щелчка по ошибке в «списке ошибок» курсор будет перенесен на строчку где предполагается синтаксическая ошибка.
После исправлений всех синтаксических ошибок у нас получилось запустить программу.
При попытке расчёта, с входными данными 2, 4, -6 мы получаем ответ «нет корней». Это неправильный ответ. Придётся искать логические ошибки. В этом нам и поможет «отладчик».
Рассмотри наш проект. И заметим строчки где выполняются вычисления.
Это строчки:
Выделим СЛЕДУЮЩУЮ строчку после этих строчек, и нажмем клавишу F9. Это клавиша для создания «точки остановки компиляции». Если выполнение программы дойдёт до данной точки, то компиляция остановится, и вы уведите значения всех переменных, которые были в момент остановки компиляции. Снять «точку» можно просто выполнив щелчек левой кнопкой мыши.
Начнём процесс компиляции. Когда процесс дойдёт до «точки остановки», мы увидим значения переменных.
Если самому попробовать пересчитать. Мы получим ответ не 32, а ответ 64. Значит мы ищем ошибку в строчке «double d = b * b + 4 * a * c;».
Эта сточка должна выглядить так: «double d = b * b — 4 * a * c;»
После изменений. Мы заново запускаем программу. И получаем уже корректное значение переменной d. Затем с помощью кнопки f10, мы выполняем программу дальше, до следующих точек остановки.
Но наша программа по прежнему не корректно работает. Мы аналогичным способом находим еще 2 ошибки.
После исправления всех ошибок, программа выдаёт верный ответ.
Мы «прогоняем» через программу как можно больше тестов. Чтобы рассмотреть все возможные случаи.
ОТЛАДКА ОКОНЧЕНА! ПРОГРАММА РАБОТАЕТ!
Так должен выглядеть отлаженный код:
Основные рекомендации написании программы и отладке
- Обязательно комментировать код. Возможно вам это покажется необязательным, но это всёже ОЧЕНЬ важная вещь. Возможно вы захотите улучшить свою программу через месяц, когда уже забыли как она работает. Или же вы работаете в команде, другой член команды не сможет понять ваш код, испортит его. Комментирование кода очень важно.
- Тщательнее тестировать ваш код. Необходимо делать как можно более сложные вычисления, нестандартные.
- Максимально упрощать пример. Если у вас не работает программа, которая читае данные, обрабатывает массив, записывает данные в файл, читает их снова, то разбейте программу на составляющие и выполняйте по очереди. Если у вас не работает сложная подпрограмма обрабатывающая данные из файла — напишите сначала тест в две строчки чтобы убедиться, что вы хотя бы можете считытавать введенные данные.
- Вывод отладочной информации. Проверяйте значение КАЖДОЙ переменной! Каждого значения, возвращаемого функцией! В файл записывается пустая строка? Проверяйте составляющие этой строки на каждом этапе ее создания и выводите на экран! Убедились, что на экран выводится? Тренируйтесь писать в файл, на тестовой строке! Забитой прямо в код! Уменьшайте количество неизвестных!
- Оптимизировать код. Не стоит заставлять компьютер пересчитывать 100 мл. элементов массива.
- Обращать внимания на все ошибки. Не стоит не обращать внимания на незначительную на первый взгляд ошибку. В дальнейшем это может привести к плачевным последствиям.
- Не волноваться, не торопится.
Заключение
Отладка — главное занятие программиста.
Отладка — единственный и самый мощный способ найти ошибку в программе.
ТурбоПаскаль7.0. Логические операторы и операторы цикла. Отладка программ.
Укороченная форма оператора if
В операторе if часть, начинающаяся словом else, может отсутствовать. Часть, следующая за словом then, выполняется, если логическое условие, стоящее после слова if, имеет значение true. Если это логическое выражение имеет значение false, непосредственно выполняется оператор, следующий за if.
Наша программа может быть переписана с применением укороченной формы оператора if. Алгоритм сводится к последовательности проверок попадания переменной Аде в различные диапазоны значений. Приведем измененный текст программы:
Question =’ What is your name?’;
‘Паскаль — прост, но первый компилятор Паскаля был написан на Паскале’;
Reply21 = ‘Вас ждет удивительное путешествие’;
Reply22 = ‘сквозь джунгли особенностей и возможностей языка’;
‘Паскаль — разумный компромисс между желательным и эффективным’;
Reply4 = ‘Паскаль академически элегантен’;
if (12 <= Age) and (Age < 20) then begin
WriteLn (Reply22) end;
if (20 <= Age) and (Age < 40) then WriteLn(Reply3);
Алгоритмически этот вариант решения задачи с использованием сокращенной формы оператора if существенно слабее первого варианта, в котором использовалась полная форма. При исполнении программы будут проверены все четыре условия, даже если при первой проверке найдено окончательное решение. Однако этот алгоритм не имеет вложенных друг в друга операторов и поэтому проще для понимания.
Оператор выбора case
Оператор if — один из наиболее часто используемых структурных элементов языка — обеспечивает ветвление алгоритма только на два направления. Вместе с тем ясно, что первый алгоритм решения задачи удачнее выражает ее сущность. Для реализации подобных алгоритмов необходим оператор множественного ветвления (выбора из нескольких ветвей команд). Таким оператором в Паскале является оператор выбора case.
Общий вид этого оператора:
Case и of — зарезервированные слова, которые являются отличительным признаком оператора множественного ветвления. Селектор — это переменная или выражение порядкового типа. В зависимости от значения данного выражения или переменной происходит ветвление программы. После заголовка оператора идет перечисление различных альтернатив, по которым может выполняться программа. Альтернативы отделяются друг от друга точкой с запятой. Альтернатива состоит из метки, двоеточия и исполняемого оператора данной альтернативы. Используемая в операторе case метка может состоять из констант и диапазонов. Диапазоны значений в Паскале — это два крайних значения, написанные через двоеточие. Такая запись эквивалентна перечислению всех целых чисел в данном диапазоне. В общем случае метка может состоять из различных констант и диапазонов, разделенных запятыми, например, допустима такая метка: 4, 8..10, 12, 14.. 16. В качестве селектора нельзя использовать логические результаты сравнения чисел (условия), поэтому использованные в операторах if неравенства здесь неприменимы.
Часть оператора case, состоящая из слова else и последнего альтернативного варианта, является необязательной, она выполняется в том случае, если вычисленный в программе селектор не соответствует ни одной из перечисленных выше альтернатив. Если эта часть оператора case отсутствует, а селектор не подходит ни под одну из альтернатив, то оператор case не выполнит никаких действий. Завершается оператор case обязательным словом end;
Пример. С использованием оператора case наша программа приобретает следующий вид
WriteLn(‘Howold are you?’); ReadLn(Age);
case Age of
12..19 : begin WriteLn (Reply21); WriteLn (Reply22) end;
else WriteLn(‘Bbi ошиблись при вводе’)
Переменная Age играет роль селектора. В зависимости от значения данной переменной происходит дальнейшее ветвление программы. В нашем примере в качестве альтернатив применяются диапазоны значений, поскольку нельзя использовать результаты сравнения чисел, как в предыдущей программе. Исходные неравенства заменены диапазонами. Например, проверка логического условия Age < 12 заменена проверкой принадлежности диапазону 0..11.
В заключение подчеркнем, что оператор case имеет существенные отличия от оператора if. Используемые для выбора альтернатив метки должны быть количественно определены до начала вычислений, поэтому в них могут применяться только константы, приведенные непосредственно или описанные в разделе описаний. Кроме того, оператор ограничен порядковыми типами данных, используемых для выбора альтернатив.
If Ready then Exam_ok := true;
Идеальная логика эквивалентности:
If Ready then Exam_ok := true else Exam_ok := false;
Управление вычислительным процессом, цикл for
В математических задачах часто встречается необходимость неоднократного повторения одинаковых действий. Рассмотрим, например, вычисление суммы N членов гармонического ряда
Естественный алгоритм для решения такой задачи:
(1) обозначим искомую сумму S и присвоим ей значение, равное нулю;
(2) последовательно N раз вычислим слагаемые ряда и прибавим их к переменной S.
Многократные повторения одних и тех же действий можно выполнить с помощью конструкций, которые в программировании называются циклами. Повторяемые действия называются телом цикла. В Паскале существует несколько операторов цикла. Оператор for повторяет тело цикла заданное число раз. Он имеет следующие синтаксические формы:
Операторы тела цикла выполняются только один раз, если верхнее и нижнее значения счетчика совпадают. Если в операторе for .. to последнее значение счетчика цикла меньше первого, то тело цикла не выполняется ни одного раза. В операторе for.. downto цикл не выполняется, если последнее значение счетчика больше первого.
Программа с циклом for, которая вычисляет сумму N членов гармонического ряда1:
Оператор, следующий после слова do, может быть составным (представлять собой группу операторов).
Пример. Найти нечетные и кратные трем числа в диапазоне от 30 до 60 включительно. Распечатать их в порядке убывания.
Условие нечетности целого числа — Odd(J). Условие кратности трем — равенство нулю остатка от деления i mod 3=0. Нас интересуют числа, удовлетворяющие обоим условиям, то есть числа, для которых истинно Odd(i) and (i mod 3 = 0). Убывающий порядок следования чисел обеспечивает оператор for.. downto. Таким образом, программа будет иметь вид
var i: byte;
if Odd(i) and (i mod 3=0) then WriteLn(i) end.
В результате выполнении программы получим в столбик значения 57, 51,45,39, 33.
В приведенном примере многократно повторялся один оператор, однако во многих случаях необходимо повторять группу операторов. Вспомним, что говорилось в предыдущей главе о вложенности операторов друг в друга. Чтобы использовать вместо одного оператора группу, нужно эту группу превратить в один сложный оператор. Осуществляется это с помощью так называемых “программных скобок” — операторов begin и end.
Любая группа операторов, начинающаяся со слова begin и кончающаяся словом end в любой конструкции языка Паскаль может использоваться как один сложный оператор. Сложный оператор, в свою очередь, может иметь в своем составе сложные операторы следующего уровня вложенности и так далее. Операторы begin и end подобны открывающим и закрывающим скобкам, и на их употребление накладываются те же ограничения, что и на обыкновенные скобки в арифметических выражениях: каждой “открывающей скобке” begin должна соответствовать “закрывающая скобка” end. Слово end не может предшествовать парному ему слову begin. При компиляции программы проверяется правильность расстановки скобок и дается сообщение об ошибке. Это сообщение не всегда попадает в то место, где действительно нужна скобка, поэтому не спешите вставлять операторы туда, куда предлагает компилятор, а тщательно проанализируйте Вашу программу.
Пример. Имеется логическое выражение not a and b xor с. Требуется вывести на печать значения логических переменных а, Ь, с, при которых данное выражение истинно. Мы предлагаем перебор всех возможных значений а, b, с с проверкой выполнения условия для каждого сочетания значений.
Vara, b, с : boolean;
for a := false to true do
for b := false to true do
for с := false to true do
if not a and b xor с then
В результате выполнения этой программы на экран будет выведено:
Программа состоит из одного сложного оператора for a := false to true do, в который вложены последовательно еще два оператора for и оператор if. Часть then последнего содержит сложный оператор, состоящий из трех простых операторов, охваченных программными скобками begin и end.
Циклы с логическими условиями
Оператор while.. do
Оператор цикла while (пока, в то время как) имеет вид:
Цикл While обеспечивает выполнение тела цикла, следующего за словом do до тех пор, пока условие имеет значение true (истина). В качестве тела цикла может использоваться простой или сложный оператор. Условие проверяется перед началом каждого выполнения тела цикла, поэтому, если до первого выполнения цикла условие имеет значение false (ложь), оператор не выполняется ни одного раза (рис. 5.2).
Пример. Необходимо преобразовать значение угла в градусах к стандартному диапазону ±180° путем исключения полных оборотов окружности ±360°. Эта процедура может быть выполнена с помощью оператора цикла
Оператор while в начале цикла проверяет, превышает ли угол по абсолютному значению 180 градусов. Если это не справедливо, тело цикла не выполняется. Если угол больше допустимого, начинается выполнение цикла.
var х, у: byte;
if (5 * x — 3 * у) < 1 then х := х + 1 until(5*x-3*y)=1;
writeln(‘x= ‘,x, ‘y=’,y) end.
В результате на экране получим х=2у=3.
По крайней мере один раз вводится число; если при вводе была сделана ошибка, условие lOResult = 0 имеет значение false, и ввод будет повторяться до тех пор, пока он не будет сделан правильно.
Операторы Break и Continue
В последних версиях языка Borland Pascal введены два новых оператора, Break и Continue, применяемые внутри циклов. Они расширяют возможности использования циклов и улучшают структуру программ. В процессе выполнения тела цикла до его завершения могут возникнуть дополнительные условия, требующие завершения цикла. В этом случае цикл может быть прекращен оператором Break.
Пример. Игра с ЭВМ в кости. Условия игры. Компьютер выдает себе или Вам случайные числа от 1 до 6. Задача — набрать максимальную сумму очков. Набравший больше 21 очка проигрывает, в любой момент один из игроков может отказаться от набора очков (спасовать).
write (‘Бросить кость для Вас? (у/n)’);
until (reply = ‘у’) or (reply = ‘Y’) or (reply =’n’) or (reply = ‘N’);
если сумма очков меньше 18}
read(reply) until (reply = ‘у’) or (reply = ‘Y’) or (reply = ‘n’) or (reply = ‘N’);
по взаимному согласию}
else if SumYour < SumComp then writeln(‘Я выиграл!’)
else if SumYour = SumComp then writeln (‘Ничья!’)
else writeln(‘Вы выиграли!’);
Программа разбита на две основные части: метание костей и подведение итогов.
продолжается до тех пор, пока не будет введен допустимый символ (‘у’, ‘Y’, ‘п’ или ‘N’) при ответе на вопрос о продолжении игры.
Для имитации метания костей применена функция Random с параметром — целым числом, равным 6. При таком использовании она дает случайные целые числа в диапазоне от 0 до 5 включительно.
Подведение итогов игры выполняют вложенные друг в друга операторы if. Возможные итоги игры проверяются последовательно. Если возможен вывод, выполняется часть then оператора. Если не возможен, выполняется часть else, в которой стоит оператор дальнейшего ветвления if.
Средства среды программирования для отладки программ
Среда Borland Pascal имеет несколько встроенных инструментальных средств отладки программ. С некоторыми из них мы уже познакомились. Механизм пошагового выполнения программы, вызываемый функциональной клавишей F7, и его варианты: пошаговое выполнение без входа в процедуры, вызываемое клавишей F8, и исполнение до заданной строки (клавиша F4) позволяют проверить, соответствует ли последовательность выполнения команд Вашим требованиям к алгоритму.
Из меню Debug можно вызвать три окна: Output, Watch и Call stack, которые как объекты интерфейса подобны окну с текстом программы. Они имеют порядковый номер, их размеры и положение изменяются так же, как размеры и положение любых текстовых окон. Они приводятся в списке окон меню Windows, могут активизироваться из этого меню. Любое из этих окон закрывается клавишами Alt + F3. Отличие от текстовых окон в том, что мы не можем выполнять в них свободную запись.
Окно стека Окно отладки
Просмотр и изменение переменной
Добавить контрольные точки
Контрольные точки Окно регистров Окно программы
Добавить в окно отладки
стоит запомнить комбинацию клавиш Alt + F5 для его быстрого вызова. Если требуется одновременно наблюдать на экране текст программы и результаты ее работы в текстовом режиме, можно открыть окно Output — окно, в которое будут выводиться результаты исполнения программы. Размер окна Output можно отрегулировать клавишами перемещения курсора после нажатия Control + F5 или мышью, как описано ранее.
Окно отладки программ Watch может быть открыто пунктом меню Watch или Add watch. В каждой строчке окна может быть задана некоторая переменная или выражение. В процессе выполнения программы текущее значение заданной переменной или выражения выводится в это окно. Отладка с использованием окна Watch обычно сочетается с пошаговой отладкой, когда можно проверить не только последовательность выполнения команд, но и значения величин, приводящие к такой последовательности выполнения. Для создания окна Watch нет необходимости вызывать пункт меню Watch. Можно просто нажать клавиши Control + F7 — и Вы получите окно Add Watch, в котором надо ввести имя переменной для вывода в окно отладки. В строке для набора имени будет подсвечено то имя, на котором стоял текстовый курсор в момент нажатия клавиш Control + F7. Если Вы нажмете Enter, в окно Watch добавится набранная в окне Add Watch переменная или выражение, если начнете набор другого имени, это имя исчезнет без дополнительных действий для удаления. Если сразу после открытия окна Add Watch нажать стрелку курсора вправо, то в окно ввода начинают вводиться символы из текста программы, следующие за курсором.
Когда окно Watch активно, в нижней строке появляются указания на наиболее актуальные действия, которые можно совершить в данном состоянии. Подсказка напоминает Вам, что пошаговая отладка проводится по нажатию клавиш F7 или F8, предлагает нажать Insert для добавления новой переменной в окно, Delete для удаления записи, Enter для редактирования записи, на которой стоит курсор окна отладки.
Загрузим, например, программу определения наибольшего общего делителя. Откроем окно Output для диалога в процессе выполнения программы. Откроем окно Watch и поместим в него все три переменные нашей программы: Common, First и Second. При пошаговой отладке программы по клавише F7 мы увидим, что до исполнения программы переменные не определены. По мере ввода чисел переменные First и Second получают введенные значения. В начале цикла получает значение и переменная Common, которая изменяется при каждом прохождении цикла. Вы также увидите, что строка
if (First mod Common) = 0 then begin WriteLn(‘HCVl = ‘, Common); Break end
бывает подсвечена очень редко, так как для большинства чисел предыдущий оператор continue завершает данный проход цикла, и эта строка действительно не выполняется.
Пошаговый проход программы в том случае, когда выполняется большое число циклов, — занятие весьма утомительное, а иногда и невозможное. Поэтому предусмотрен механизм введения в программу контрольных точек
Контрольная точка — это логическое условие, которое вычисляется каждый раз при выполнении заданной строки программы. Если выполняются условия прерывания, программа останавливается в контрольной точке. Далее мы можем вводить другие контрольные точки или применять пошаговую отладку с просмотром в окне Watch.
Чтобы добавить контрольную точку в программу, надо ввести имя файла, номер строки, в которой производится проверка, логическое условие, по достижению которого происходит остановка программы, и число проходов заданной строки, в которых данное логическое условие не просчитывается. Если логическое условие не задано, остановка возникает по достижению данной строки. Число проходов данной строки без анализа по умолчанию устанавливается равным 0, то есть анализ проводится, начиная с первого прохода. Предположим, в программе определения наибольшего общего делителя Common.pas мы хотим проверить отношения, возникающие при кратности переменных Second и Common. Тогда в окне Add Breakpoint в поле Condition мы вводим условие прерывания программы Second mod Common = 0. В поле FileName автоматически вносится имя файла, активного окна, а в поле Line number — номер строки, в которой находится курсор. В строку Pass count записывается число 0, так как мы хотим остановить программу уже при первом выполнении условия прерывания .
Все введенные в программу точки прерывания можно просмотреть и отредактировать в окне Breakpoints
Окно, открывающееся при выборе пункта меню Evaluate/Modify, также, как окно Watch, позволяет просмотреть значение любой переменной или выражения, но при этом можно изменить значение переменной. Окно Evaluate/Modify может быть вызвано, если выполнение программы остановлено через механизм точки прерываний или при пошаговой отладке, и должно быть закрыто для продолжения работы программы . Это окно удобно использовать как “калькулятор”. Для выполнения вспомогательных вычислений достаточно ввести соответствующее выражение в поле Expression и прочитать результат в поле Result.