При разработке программ, особенно в первое время, возможны синтаксические и логические ошибки. Чем сложнее программа, тем труднее их обнаружить. Для облегчения обнаружения и устранения ошибок в среде разработки Borland C++ Builder 6 существует встроенный отладчик программ. Он позволяет выполнять программу по шагам, устанавливать точки останова, просматривать переменные и производить другие операции.
ОТЛАДКА ПРОГРАММ Несмотря на то, что созданные нами программы очень просты, я хочу рассказать об их отладке. Дело в том, что рано или поздно в создаваемой программе могут появиться ошибки. Это могут быть синтаксические или более сложные для обнаружения логические ошибки. И тогда потребуется инструмент для их устранения. Чем быстрее мы познакомимся с таким инструментом, тем легче нам будет создавать более сложные программы. После разработки любого приложения необходимо произвести его компиляцию, компоновку и тестирование. Эти операции выполняются автоматически каждый раз при выполнении команды Run из главного меню среды разработки Borland C++ Builder 6. Однако данные процедуры можно произвести по отдельности с целью сокращения времени на отладку всего проекта в целом. В нашем очередном примере мы будем пользоваться различными командами трансляции (компиляции и компоновки) проекта. Рассмотрим, в чем состоит отличие между этими командами. Например, компиляцию отдельного модуля программы можно выполнить с помощью команды главного меню Project Compile. При этом не будет затрачено времени на компиляцию и компоновку всего проекта в целом. Команда главного меню Project Make позволяет выполнить компиляцию только тех модулей, которые были отредактированы, после чего выполняет компоновку проекта с созданием исполняемого модуля ехе. Таким образом, эта команда также экономит время на трансляцию проекта в целом. И только команда Project Build компилирует все модули независимо от того редактировались они или нет> а затем компонует проект и готовит исполняемый файл с расширением ехе. Данная команда необходима для переработки всего проекта после изменения настроек компилятора или среды разработки в целом. Команда Project Run, в отличие от Project Build, автоматически запускает исполняемый файл. На этапе компиляции происходит автоматический поиск средой разработки синтаксических ошибок, неправильного объявления или использования переменных и т. п. При обнаружении подобных ошибок в инспекторе кодов будет выведено соответствующее сообщение об ошибке или предупреждение. Предупреждение, в отличие от ошибки, не блокирует выполнение программы, но не гарантирует корректность ее работы. Если ошибок в программе нет, компилятор создает объектный (машинный) код программы в модулях (файлах) с расширением obj. При компоновке программы также выявляются некоторые виды ошибок, связанные с отсутствием объявления переменных, функций, библиотек и т. п. и выполняется объединение всех объектных модулей и библиотек при необходимости в один исполняемый файл с расширением ехе. Вместо исполняемого файла может создаваться библиотечный файл с расширением lib или dll при соответствующих установках среды разработки. Рассмотрим выполнение описанных процедур на конкретном примере. Создадим небольшое приложение на форме Forml с двумя кнопками Button 1, Button2 и одним элементом надписи Label 1. О том, как размещаются эти элементы на форме, рассказывалось в предыдущих статьях цикла. Это приложение должно сообщать о числе нажатий на первую кнопку. При нажатии на вторую кнопку приложение должно закрываться. Разместите эти элементы на форме Forml и измените размер формы в соответствии с рис. 1.
Рис.1 Теперь замените свойство Caption всех объектов приложения в инспекторе объектов на заголовки Программа 2, Кнопка 1, Выход и пустую строку соответственно. В результате у вас должно получиться окно, приведенное на рис. 2. Обратите внимание, что элемента Labell, располагающегося выше кнопок, не видно, поскольку мы заменили его свойство Caption на пустую строку, т. е. очистили это свойство. Рис.2 Щелкните дважды левой кнопкой мыши по созданной кнопке Выход и в открывшемся окне инспектора кодов впишите между фигурными скобками заготовки обработчика события команду закрытия приложения CloseQ;. Вернитесь к окну формы и щелкните дважды левой кнопкой мыши по кнопке с названием Кнопка 1. В отрывшемся окне инспектора кодов впишите между фигурными скобками строку команд:
Label1->Caption="Кнопка 1 нажата " + IntToStr(++i) +" раз";
В этой строке команд выполняется присвоение (знак равенства) свойству Caption элемента надписи Label 1 текстовой строчки, состоящей из трех слагаемых частей. Поскольку свойство Caption элемента надписи Labell предназначено для отображения текста, мы должны присваивать этому свойству только текстовые (строковые) значения. В языке C++ такие строковые значения заключаются в кавычки. Первая и последняя части присваиваемого значения таковыми и являются. Счетчиком числа нажатий на кнопку в программе будет служить переменная i, которая должна автоматически увеличиваться на единичку перед выводом. Для этой цели перед ней записаны два знака плюс. Данная операция в языке C++ называется автоинкрементом. Для превращения числовой переменной i в строковую используется встроенная функция C++ преобразования целых чисел в строки IntToStrQ. Итак, в одной строке команд мы осуществили целый ряд операций. Разве это не изящный язык программирования?! Сохраните проект под именем butct. bpr, а программный модуль — под именем Ubutct.cpp. Впрочем, имена вы можете дать другие. Не изменяйте только расширения файлов. Теперь попробуем скомпилировать, скомпоновать и выполнить данное приложение, а заодно проверить, нет ли в нем ошибок. Выполните команду Compile из группы Project главного меню или нажмите «горячую» комбинацию клавиш для компиляции программы Alt+F9. Перед вами откроется окно, приведенное на рис. 3. Рис.3 В верхней строке этого окна отображается путь размещения и имя проекта программы. В следующей строке вначале отображается процесс компиляции, сменяющийся записью о завершении данной операции Done и сообщением об обнаруженных ошибках There are errors (здесь есть ошибки). Ниже отображается номер текущей строки программы Current line и общее количество арок программы Total line. В нижней строке отображается обнаруженное на данный момент число замечаний Hints, предупреждений Warnings и ошибок Errors. Как видим, в программе есть одна ошибка. Нажмите кнопку ОК в окне, и перед вами окажется окно инспектора кода с выделенной строкой, имеющей ошибку, и сообщением о типе ошибки внизу окна (рис. 4). Рис.4 Сообщение «[C++ Error] Ubutct.cpp(25): E2451 Undefined symbol 'i'» говорит о том, что компилятор C++ обнаружил ошибку в 21-й строке модуля Ubutct.cpp. Код ошибки Е2451 означает, что обнаружен необъявленный символ. В нашем случае это переменная с именем i. Действительно, мы не объявили ее в программе, хотя и сделали это намеренно, с целью получения сообщения об ошибке в качестве примера. В языке C++, как впрочем и в других языках программирования, все переменные, используемые в программе, необходимо объявлять. Это делается для того, чтобы компилятор знал, сколько ячеек памяти необходимо резервировать в памяти компьютера для ее хранения. Поскольку различные типы переменных требуют для своего хранения различное количество ячеек памяти (байт), рационально резервировать для них лишь необходимый объем памяти. В нашем случае переменная i является целой, т. е. принимающей только целочисленные значения. Для ее объявления необходимо использовать директиву int (от integer — целый). Можно сделать такое объявление непосредственно в тексте программы обработчика событий, но тогда эта переменная будет недоступна в Других функциях обработчика. Поэтому мы объявим ее в блоке public (общедоступный) файла описания заголовков Ubutct.h. Для этого необходимо щелкнуть левой кнопкой мыши по закладке Ubutct.h инспектора кодов и вписать строку объявления переменной с комментариями сразу же после строчки public. Ниже приведена эта запись полностью.
public: // User declarations int i; // Переменная - счетчик Теперь снова выполним компиляцию и обнаружим, что ошибок нет. Но не спешите радоваться. Дело в том, что мы не присвоили переменной i начального значения. Компилятор не обращает на это внимания и не выдает никаких сообщений по этому поводу. Однако в программе это может привести к недоразумениям. Поэтому выполним присвоение начального значения переменой i, сделав запись i=0; в тексте программы обработчика создания формы (ведь при запуске программы Кнопка 1 не была нажата ни разу). Для этого щелкните дважды левой кнопкой мыши по форме приложения и в открывшемся окне инспектора кода впишите между фигурными скобками текста программы строку
i=0;
Выполним компоновку проекта с помощью команды Make из группы Project главного меню или просто нажав «горячую» комбинацию клавиш для компоновки программы Ctrl+F9. В результате мы должны получить безошибочную компоновку приложения. Теперь приложение можно сохранить и запустить на выполнение с помощью команды Run или воспользоваться «горячей» кнопкой запуска программы F9. На экране появится окно нашей программы. Щелкая левой кнопкой мыши но кнопке с названием Кнопка 1 вы увидите, что каждый раз в окне будет появляться сообщение о числе нажатий этой кнопки. В будущем каждый раз перед запуском приложения после редактирования не забывайте сохранять проект. Это позволит избежать потери информации при зависании программы, вызванном наличием ошибок в ней. Потренируйтесь самостоятельно, специально внося ошибки в программу и изучая возникающие при этом сообщения об ошибках при компиляции и компоновке проекта.
СТРУКТУРА ФАЙЛОВ Теперь рассмотрим более подробно структуру основных файлов программы для того, чтобы не теряться при возникновении сложных ошибок и иметь представление о том, как работает программа устранения ошибок. Вернемся к нашему примеру, приведенному выше, и рассмотрим состав файлов проекта. Для лучшего понимания текста программ тем, кто не знаком с языками программирования, я рекомендую почитать, например, книгу [1]. В языке C++ главной функцией проекта является функция main. В программах, написанных для операционной системы Windows, главной функцией является WinMain. Данная функция содержится в программном модуле butct.cpp нашего примера. Ниже приведено его содержимое.
//---------------------------------------------------------------------------
#include #pragma hdrstop //--------------------------------------------------------------------------- USEFORM("Ubutct.cpp", Form1); //--------------------------------------------------------------------------- WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { try { Application->Initialize(); Application->CreateForm(__classid(TForm1), &Form1); Application->Run(); } catch (Exception &exception) { Application->ShowException(&exception); } catch (...) { try { throw Exception(""); } catch (Exception &exception) { Application->ShowException(&exception); } } return 0; } //---------------------------------------------------------------------------
В этих нескольких строчках содержатся директивы и команды, с помощью которых формируется программа. Строчки, начинающиеся с символов //, являются строками комментариев для разделения или снабжения текстовым описанием частей программы. В языке С допускается также заключать комментарии между символами I* и */. В этом случае комментарии могут состоять из нескольких строк, заключенных между этими символами. Строка #include является строкой препроцессора, т. е. программы, которая будет подключать к нашим модулям дополнительные файлы библиотек и описаний. В данном случае строка #include говорит препроцессору о том, что ему необходимо подключить к нашей программе заголовочный файл vclh, в котором содержатся описания визуальных компонентов Borland C++ Builder. Директива #pragma hdrstop завершает список файлов заголовка для препроцессора. Строка макрокоманды USEFORMC^'Ubutct.cpp", Forml); подключает к проекту модуль Ubutctcpp и форму Forml. Далее следует заголовок программы главной функции проекта WINAPI WinMain(HINSTANCE, H1NSTANCE, LPSTR, int). Перед названием главной функции находится дескриптор W1NAPI, который позволяет использовать множество полезных готовых функций Windows в нашем приложении. В скобках заключены параметры главной функции, необходимые для опознания нашего приложения среди множества других работающих приложений, передачи параметров в программу в командной строке и для определения конфигурации окна нашего приложения. После заголовка главной функции следует блок кода, в котором может произойти исключение — аварийная ситуация (останов, ошибка программы и пр.). Такой блок должен начинаться с ключевого слова try. В этом блоке заключены операторы инициализации компонентов приложения Application->Initialize();, создания формы приложения Application >CreateForm(__classid(TForm1), &Form1); и выполнения приложения Application->Run();. Блок catch (Exception &exception) предназначен для включения операторов, вызываемых при возникновении аварийной ситуации. В нашем примере программы таким оператором является стандартный обработчик исключений Application- >ShowException (&exception);. В следующих примерах мы рассмотрим, как его использовать. Аналогичная конструкция блоков повторяется для копии приложения, запущенного вторично. Последним оператором тела функции WinMain является return 0; — оператор, завершающий приложение с кодом 0. Данный файл можно редактировать, изучив предварительно команды и функции Windows и получив некоторый опыт разработки программ. Поскольку этот файл создается средой разработки автоматически, мы в нашем курсе обучения не будем его изменять намеренно, чтобы не отягощать себя лишними проблемами. Тем не менее, при возникновении ошибок компилятора или компоновщика, не лишним будет посмотреть содержимое данного файла и отредактировать его в случае необходимости. Например, при создании новой формы и последующем ее удалении в этом файле могут сохраниться записи о несуществующей форме, приводящие к ошибкам во время трансляции. В таком случае необходимо удалить лишние записи из этого файла для устранения подобных ошибок. Далее рассмотрим структуру файла butct.cpp. Содержимое этого файла целиком приведено ниже.
//---------------------------------------------------------------------------
#include #pragma hdrstop //--------------------------------------------------------------------------- USEFORM("Ubutct.cpp", Form1); //--------------------------------------------------------------------------- WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { try { Application->Initialize(); Application->CreateForm(__classid(TForm1), &Form1); Application->Run(); } catch (Exception &exception) { Application->ShowException(&exception); } catch (...) { try { throw Exception(""); } catch (Exception &exception) { Application->ShowException(&exception); } } return 0; } //---------------------------------------------------------------------------
Большинство строк этого файла нам уже известно из предыдущего описания и самого примера программы. Рассмотрим лишь неизвестные строки. Строка #pragma package(smart_init) определяет последовательность инициализации составных частей программы. Строка #pragma resource «*.dfm» сообщает препроцессору о том, что для формы необходимо использовать файл *.dfrn с именем данного файла. В строке TForml *Forml; объявляется класс TForml для формы Forml. Далее следуют подпрограммы создания формы приложения и обработчики событий, рассмотренные выше. Обратите внимание на строку #include «Ubutct.h», которая подключает к модулю заголовочный файл Ubutct.h, представленный ниже.
//---------------------------------------------------------------------------
#ifndef UbutctH #define UbutctH //--------------------------------------------------------------------------- #include #include #include #include //--------------------------------------------------------------------------- class TForm1 : public TForm { __published: // IDE-managed Components TButton *Button1; TButton *Button2; TLabel *Label1; void __fastcall Button2Click(TObject *Sender); void __fastcall Button1Click(TObject *Sender); void __fastcall FormCreate(TObject *Sender); private: // User declarations public: // User declarations int i; // Переменная - счетчик __fastcall TForm1(TComponent* Owner); }; //--------------------------------------------------------------------------- extern PACKAGE TForm1 *Form1; //--------------------------------------------------------------------------- #endif
Ранее мы уже редактировали его в примере, вставляя строку инициализации переменной i. Рассмотрим этот файл подробнее. Строчка #ifiidef UbutctH является директивой условной компиляции. Эта директива проверяет, не был ли ранее определен идентификатор UbutctH директивой #define. Если не был, то предваряемый директивой фрагмент кода компилируется. В противном случае компиляция фрагмента не производится. Затем в строке #de-fine UbutctH определен идентифика тор UbutctH. Далее следуют четыре строки #include включения файлов, описывающие компоненты, переменные, константы и функции, которые будут использованы в данном модуле. Сюда можно добавлять дополнительные директивы, не создаваемые средой разработки автоматически. После этих строк следует описание класса формы с тремя разделами. Раздел__published: содержит объявления размещенных на форме компонентов и обработчиков событий, которые среда разработки записывает в файл автоматически в процессе проектирования формы. Раздел private: предназначен для объявления закрытых переменных, т. е. доступных только для данного модуля. Раздел public: предназначен для объявления открытых переменных, т. е. доступных не только для данного модуля, но и для других классов и модулей. Именно в этот раздел мы включили (записали) переменную — счетчик L Строка extern PACKAGE TForml *Forml; объявляет класс TForml как доступный из других модулей. Завершает файл строка #endif окончания условной директивы tifndef. Следующие два файла butct.bpr и Ubutct.dfm представляют собой текстовые файлы установок проекта и формы данного проекта соответственно. Они создаются и редактируются автоматически в процессе разработки и изменения проекта в среде разработки. Файл butct.bpr содержит директивы и макрокоманды настроек среды разработки и его не следует корректировать вручную для избежания внесения ошибок, хотя просмотреть содержимое можно с помощью любого текстового редактора. Файл Ubutct. dfrn представляет собой текстовые записи содержания и свойств формы. Его можно просмотреть и даже отре дактировать в самой среде разработки. Для этого необходимо щелкнуть левой кнопкой на форме для ее выделения, а затем щелчком правой кнопки мыши открыть контекстное меню (рис. 5) и выбрать в нем команду View as Text.
Рис.5
Перед вами должно открыться окно формы (рис. 6), в котором представлено содержимое файла формы в текстовом виде.
Рис.6
Здесь можно просмотреть и отредактировать любую запись. Естественно, это нужно выполнять, зная, что вы делаете, чтобы не внести новые ошибки. Например, в строках Left = 192, Тор = 107, Width = 220 и Height = 140 можно изменить левый и верхний отступ, а также ширину и высоту формы соот ветственно. В строчках Caption - 'Программа 2' и Color = clBtnFace можно изменить соответственно заголовок формы и цвет ее фона и т. д. Вернуться к исходному виду формы можно с помощью комбинации «горячих» клавиш Alt+F12 или через контекстное меню по команде View as Form. Файлы butct.res и Ubutct.ddp являются двоичными, хранящими ресурсы и диаграммы проекта соответственно, и не подлежат ручному редактированию. Исполняемый файл проекта имеет имя butct.exe. Наконец, файл с расширениями tds является отладочным файлом проекта, который можно всегда создать в среде разработки повторно, имея исходные файлы проекта. Помимо описанных выше файлов среда разработки позволяет создавать дополнительные файлы, которые являются вспомогательными и не подлежат ручной корректировке. Этот материал может показаться на первый взгляд очень трудным. Но со временем описанные директивы станут привычными и не будут казаться непонятными. В следующей части статьи мы рассмотрим инструменты отладчика, которые помогут нам при отладке программ.
ИНСТРУМЕНТЫ ОТЛАДЧИКА Основными инструментами отладки любой программы являются команды выполнения программы по шагам и установки точек останова. Естественно, что такие команды имеются и в среде разработки Borland C++ Builder 6. Команды пошагового выполнения программы вызываются из главного меню в группе Run или с помощью «горячих» клавиш. Список этих команд с соотвегствием «горячим» клавишам и описанием назначения приведен в табл. 1.
Команды установки точек останова и некоторые другие вызываются из контекстного меню инспектора кода через раскрывающийся список команды Debug (рис. 7), а также с помощью «горячих» клавиш.
Рис.7 Попробуйте применить эти команды на практике, используя нашу программу. Поместите курсор в инспекторе объектов на строку Labell>Caption=«KHonKa 1 нажата» + IntToStr(++i) +«раз»; и нажмите клавишу F5. При этом строка окрасится в красный цвет, что свидетельствует о том» что на ней установили точку останова. Теперь при выполнении программы произойдет автоматический останов на этой строке. Проверьте это, выполнив команду Run с помощью клавиши F9. Программа запустится, и на экране появится ее форма. Но стоит щелкнуть левой кнопкой мыши по кнопке с названием Кнопка 1, программа остановится и откроется окно (рис. 8) инспектора кода с зеленой стрелкой напротив нашей строки. Рис.8 Теперь, нажимая клавишу F8 на клавиатуре, можно продолжить выполнение программы по шагам. Для прерывания программы необходимо нажать комбинацию клавиш Ctrl+F2. Можно продолжить выполнение программы с помощью клавиши F9, а затем щелкнуть по кнопке Выход в окне программы для ее завершения. Попробуйте на практике выполнить и другие команды отладчика для того, что бы понять их назначение. Помимо команд отладки среда разработки позволяет просмотреть переменные, используемые в программе, состояние регистров процессора, процессы, происходящие в программе и т. п. Для этих целей используются окна отладчика.
ОКНА ОТЛАДЧИКА Доступ к окнам отладчика производится через пункт главного меню View—»Debug Windows. Откроется окно (рис. 9), в котором расположены команды открытия окон отладчика. Рис.9 Команда Break Points открывает окно (рис. 10), в котором отображаются и редактируются точки останова. Из этого окна через контекстное меню, вызываемое щелчком правой кнопки мыши, можно осуществлять добавление, удаление и редактирование точек останова (рис. 11).
Рис.10 Рис.11 Для вызова данной команды отладчика можно использовать «горячие» клавиши Ctrl+AIt+B. Остальные команды можно вызывать аналогично. Комбинация «горячих» клавиш для каждой из команд изображена правее самой команды (рис. 9). Команда Call Stack вызывает окно просмогра стека программы (рис. 12). Данное окно позволяет увидеть вызовы функций в программе в момент ее отладки. Рис.12 Команда Watches открывает окно переменных (рис 13), в котором можно просматривать переменные, используемые в программе во время ее работы Рис 13 Из этого окна можно вызвать контекстное меню (рис 14) щелчком правой кнопки мыши При этом появляется возможность редактировать, добавлять, активизировать, делать невидимыми и удалять переменные из окна отображения переменных
Рис.14 Окно редактирования свойств переменных Watch Properties (рис 15) вызывается из контекстного меню командой Edit Watch или с помощью клавиш Ctrl+E Оно позволяет выбрать (Expression) отображаемые переменные, разбить переменные на группы (Group name), задать число элементов массива (Repeat count) для отображения, ограничить разрядность (Digits) отображаемых значений, разрешить (Enabled) отображение переменной, разрешить (Allow Side Effects) отображение выражений и выбрать один из девяти форматов отображения переменных Команда Local Variables показывает текущие локальные переменные в отладочном режиме работы (рис.16) Команда Threads отображает окно процессов, происходящих в режиме отладки программы (рис 17) В частности, в нем можно увидеть имя исполняемой про!раммы, состояние процесса отладки и адрес точки останова Команда Modules вызывает окно (рис 18), в котором можно просмотреть все программные модули, их адреса и точки входа. Рис.15 Рис.16 Рис.17 Рис.18 Рис.19 Команда Event Log отображает окно регистрации событий программы (рис.19) Команда Code Guard Log вызывает окно сообщений (рис 20), сгенерированных специальной утилитой отладки CodeGuard С помощью команды CPU вызывается окно (рис.21), в котором отображается работа программы в машинных кодах и содержимое регистров процессора. Если программа не запущена, данная и следующая команда недоступны. Рис.20 Рис.21 Рис.22 Наконец, команда FPU вызывает окно (рис. 22) для просмотра состояния математического сопроцессора компьютера. Рекомендую читателям для тренировки поочередно открыть эти окна во время пошагового выполнения программы с помощью «горячих» клавиш. Продолжение следует...
|