Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Глава 10. Прочие функциональные особенности   115




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

Это не представляет проблемы, если в .h-файл входят только прототипы функций и определения        ine . Считается                                  дурного тона (и часто приводит к серь- езным ошибкам) объявлять в подключаемых файлах глобальные переменные или соз- давать в них функции.

Повторных объявлений можно избежать, воспользовавшись директивой  #ifdef. Она требует включения всех команд вплоть  до                                                       если аргумент директивы был  определен  ранее      действует наоборот— если аргумент не был

определен).

 

//Проверяем, а ли                           MyModui

Если нет, значит,               файл во время

//компхляпии вызывается впервые

#ifndef

 

//Определим       с тем, чтобы при следующем этого файла из той    программы

//                файла не было

 

можно      все, что вы   разместить

включаемом файле

 

в конце файла

 

Эта проверка будет проводиться во время компиляции, но не во время вы- полнения программы.

 

 

С++

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

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

Обратите внимание, что  имена  стандартных             h-файлов заключены в угловые скобки, тогда как при объявлении локальных .h-файлов используются обычные ка- вычки. Единственное отличие между этими типами объявлений состоит в том, что начинает искать файлы, заключенные в обычные кавычки, в текущем каталоге (каталоге проекта), а поиск файлов, заключенных в угловые скобки,  происходит в предопределенных каталогах, содержащих подключаемые файлы. С помощью на- строек проекта  программист  может                                                              где именно должен проводиться

поиск включаемых файлов.

 

 

116                                        Часть II. Становимся программистами


Глава

Отладка программ на C++

Определение типа ошибки Использование отладочной печати Использование отладчика

Первая программа BUDGET

 

е часто                  (особенно с "чайниками"), что программа идеально работает с первого раза. Крайне редко удается написать нетривиальную программу и не

допустить ни одной ошибки.

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

 

tfauta

Можно выделить два типа ошибок: те, которые компилятор может найти, и те, ко- торые не может, Первый тип называют ошибками компиляции                                                                                                          error). Их довольно легко найти, поскольку компилятор сам указывает место в программе, где встретилась ошибка. Правда, иногда описание ошибки бывает не совсем точным (компьютер так легко сбить с толку!), однако, зная капризы своего компилятора, трудно разобраться в его жалобах.

Ошибки, которые компилятор не может найти, проявляются при запуске програм- мы и называются ошибками времени исполнения (run-time error). Их найти намного труднее, поскольку, кроме сообщения об ошибке, нет и намека на то, какая именно ошибка возникла и где (сообщения, которые генерируются при возникновении оши- бок выполнения, вполне достойны                                                                                   ошибочных).

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

 

использование

Добавление команд вывода в ключевых точках помогает понять, что происходит в программе, и называется методом отладочной печати (иногда именуемым WRITE). Метод WRITE появился во времена, когда программы писались на языке FORTRAN, в котором вывод осуществляется с помощью команды WRITE.

Глава 11. Отладка программ на C++                                                            117


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

нов, а вторая приводит к неправильному результату.

//         — эта программа усредняла бы

//          ряд чисел, если бы не содержала

//           одну невыполнимую ошибку

 

 

argc, char* pszArgs[])

{

cout << "Эта программа содержит

// аккумулирует ряд чисел, пока

//         не введет отрицательное число,

// после чего выводит

 

for (int    = 0; ; ) i

// ждет ввода следующего числа

int nValue;

cout <<       следующее cin >> nValue;

// если введенное число меньше нуля...

{nValue <

(

//  вывести среднее cout << "\пСреднее "

<<

<< "\п";

break;

 

 

// если число    нуля,

// сложить его с аккумулятором

+= nValue;

}

return 0;

}

После ввода этой программы создайте выполнимый файл (клавиша <F9>). Запустите эту программу и введите числа 1, 2 и 3, а затем -1. Вы ожидаете увидеть,

что их среднее равно двум? Вместо этого очевидного результата будет выдано доволь- но непривлекательное сообщение об ошибке, показанное на рис. 11.1.

Выявление "жучка" № 1

Сообщение об ошибке, приведенное на рис.                      выглядит весьма внушительно. На самом деле большая часть информации в этом сообщении бесполезна для нас (как и для многих других). Но во второй строке есть намек на источник ошибки: Divisio n by zero at .. .                        на ноль в . . .). Как можно понять, во время выполнения программы произошла попытка деления какой-то переменной на ноль (крайне информативно, не правда ли?). Определить источник ошибки не так уж

 

118                                                     Часть Становимся программистами


следующее Доведите следующее

следующее


[Введите


 

to signa l

"
by  Zero  at                          x87


 


 

ids: ies:


 

sel=017f sel=01bf

stack:

frame


 

stack:


 

 

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

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

О таких программах иногда говорят, что они                       в космос". Ну, а по- скольку к ним прикрепили отдельный ярлык, значит, они встречаются доволь- но часто.

Следующим шагом будет запуск программы из среды разработчика, поскольку иногда среды разработчика, например Visual C++ или GNU C++, могут помочь в по- иске ошибки.

Далее приведен пример с использованием GNU C++ (работа в Visual C++ очень похожа на описанную ниже).

Откройте программу в редакторе           соберите и запустите ее, нажав клавиши

<Ctrl+F9>. Введите 1, 2 и 3, а затем -1, и программа снова аварийно завершится.

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

После запуска программы и возникновения ошибки rhid e выдает окно, содержа- щее сообщение "Program exiting code                                                                       ("Программа завершилась с кодом вы- хода как показано на рис.  Я многого не знаю, но мне точно известно, что код нормального завершения нашей программы — 0. Невооруженным глазом видно, что этот код завершения не равен нулю, а значит, что-то пошло не так. Хотя я и не знаю, что именно означает код выхода

Щелкните на кнопке и rhid e кроме окна редактирования программы откроет еще два окна.

Глава11.ОтладкапрограммнаC++                                                 119


Search Run               Options

- эта              усредняла

//                        ряд чисел. ег.пи бы не

//                         одну

ttincluifc

 


int   argc,

cout « "Эта

// аккумулирует

// пользователь

// после чего int


 

 

Program exit code: 255


1:1

 

 

F3                  ZOOM F6 Next                             Menu             Quit

 

Рис-         Код выхода Oxff означает, что программа завершилась

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

 

В третьем окне находится сообщение об ошибке, которое было создано во время запуска программы, но мы обратимся ко второму окну, показанному на рис. 11.3.

 

'    -

Kil e                                        Debug                  Options

... —

//     - эта программа усредняла бы

//        ряд чисел, если не содержала

//         одну невыполнимую ошибку

 

int   argc,

{

cout « "Эта программа содержит

//       ряд чисел, пока

// пользователь не введет отрицательное число, чего выводит среднее

int

 

i n function

in function

 

JUMP to source              Next                                                  Quit

Редактор   способен вычислить, в какой части программы возниклаошибка

Сообщение        frame                ("Отслеживание кадров вызовов") звучит так же, как сообщение о шпионском радиоперехвате. Система выполняет просмотр адресов функций, вызванных программой, начиная с самой первой. В данном случае

 

120                                                          ЧастьII.Становимсяпрограммист


ошибка находится в  main {},      которая была  вызвана  функцией       с

crtl_startu p (крайне содержательно, не правда ли?). Итак, ошибка возникла в строке 28 исходного файла Это уже теплее...

Обратимся к строке 28 исходного файла:

<<       is : " // строка 2 6

<<              // строка 27

<< "\п";        // строка 28

Думаю, никто из читателей не видит в строке 28 операции деления. Я тоже не вижу. Дело в том, что при  компиляции C++ все выражения до точки с запятой в од- ну строку, т.е. строки 26, 27 и 28 являются частями одной строки. Таким образом, все команды в строках 26—28 будут рассматриваться компилятором как одна 28-я строка.

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

=

 

Выявление "жучка" № 2

Теперь, когда найдена и исправлена ошибка № I, можно запустить программу, введя числа, заставившие ее в прошлый раз аварийно завершиться. На этот раз сооб- щение об ошибке не появится и программа вернет нулевой код выхода, но будет ра- ботать не так, как ожидалось. Вместо так горячо ожидаемой двойки будет выведено какое-то нелепое число.

Эта                      содержит

Введите                                          1

Введите                                          2

Введите  следующее  число: 3

Введите

Среднее равно: 22 952 3

 

Как       находит ошибку в исходном коде

Сообщение об ошибке, полученное во время запуска программы из MS DOS или Windows, было не очень информативным (особенно по сравнению с Visual C++ или которые смогли указать, где именно возникла ошибка). Как же среды разработки находят источник ошибки?

Компилятор C++ поддерживает два режима построения программ. По умолчанию C++ строит программу в так называемом отладочном режиме. При этом

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

При возникновении ошибки деления на ноль C++ по номеру строки машинного кода отслеживает, в каком месте исходной программы находилась ошибка.

Такая отладочная информация занимает довольно много места. Поэтому при под- готовке окончательной версии программы следует указать компилятору, что гене- рация исполняемого кода должна проводиться без отладочной информации.

 

Глава 11. Отладка программ на C++                                                                         121


Очевидно, какая-то из переменных —                или nSum (а возможно, и обе)

жит неверное значение. Для того чтобы исправить ошибку, необходимо узнать, какая именно из этих переменных содержит  неверную                                                                            Не помешало бы так- же знать, что содержится в nValue, поскольку она используется для под- счета суммы в

Для этого воспользуемся методом отладочной печати. Чтобы узнать значения nValue.       и         перепишите тело цикла for так, как показано в следующем листинге:

=

{

// ждет ввода следующего числа int nValue;

cout <<       следующее cin >>

// если введенное число меньше нуля... if (nValue < 0)

{

// ... тогда вывести среднее cout << "

«

«

break;

 

// вывести отладочную информацию cout << "nSura = " << nSum << "\n"; cout « " << nNums << "\n";

cout <<      "<< nValue << "\n"; cout << "\n";

 

// если число больше нуля,

// сложить его с аккумулятором

+= nValue;

}

Обратите внимание на то, что информация о состоянии отслеживаемых перемен-

ных nValue, nSum и nNums выводится в каждом цикле.

Ответ программы на ввод уже привычных 1, 2, 3 и -1 приведен ниже. При первом же проходе nSum принимает какое-то несуразное значение, хотя оно должно равнять- ся нулю (поскольку к этой переменной пока что ничего не прибавлялось).

Эта программа содержит

 

Введите следующее

= -858993460

0

 

Введите следующее nSum = -858993459

1

nValue= 2

 

Введите следующее nSum = -858993457

nNums= 2

nValue= 3

 


Поделиться:



Последнее изменение этой страницы: 2019-04-19; Просмотров: 203; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.062 с.)
Главная | Случайная страница | Обратная связь