Написание, компиляция и отладка простейшей программы
Для новичка VisualC++ может показаться, на первый взгляд, средой пугающе сложной и непонятной. Впрочем, подобные ощущения возникают при знакомстве с любой современной средой программирования. Начальное окно, открывающееся при запуске программы, выглядит вполне обычно, но по мере того как вы станете выбирать различные пункты меню и подменю, а затем просматривать многочисленные диалоговые окна, обилие настраиваемых параметров и опций будет приводить вас все в большее смятение.
Действительно, создание многофункционального приложения Windows, поддерживающего работу в Internet, является задачей совсем не простой, тем более если начинать приходится с нуля. Но хотим вас успокоить: современные средства программирования позволяют автоматически генерировать программный код для выполнения многих стандартных задач. И мы, авторы, поставили своей целью научить вас как основным принципам программирования на C/C++, так и использованию базовых возможностей одной из мощнейших современных сред программирования, каковой является VisualC++. Надеемся, вы сможете создавать вполне профессиональные приложения, соответствующие требованиям сегодняшнего дня.
Создание вашей первой программы
Первое, что вы должны сделать, приступая к работе над новой программой, —
это создать новый проект. Для этого в меню File задайте команду New...,
а в открывшемся диалоговом окне Newвыберите вкладку Projects(рис. 3.1).
Рис 3.1. Выбор типа проекта
Теперь необходимо ввести имя файла проекта. Отнеситесь к данному моменту серьезно, так как это имя впоследствии будет использовано при построении исполняемого файла приложения.
Следующий шаг состоит в указании типа проекта. В нашем случае это будет простое консольное приложение — Win32 ConsoleApplication. В текстовом поле Location укажите папку, которую система автоматически создаст для файлов нового проекта.
Далее необходимо указать платформу, для которой создается проект. Для 32-разрядной версии компилятора VisualC++ в поле Platforms по умолчанию задана опция Win32.
После того как вы нажмете кнопку ОК, отобразится окно мастера с набором опций (рис. 3.2).
Поскольку мы будем создавать проект с нуля, выберите опцию An empty project ищелкнитенакнопке Finish. Далее можете просто щелкнуть на кнопке, расположенной первой слева на стандартной панели инструментов. Когда перед вами откроется новое окно редактирования, введите такой код:
/* ПРИМЕЧАНИЕ: данная программа содержит ошибки, */
/* введенные с целью обучить вас использованию*/
/* средств отладки */
#include <stdio.h>
/* Следующая константа определяет размер массива */
#define SIZE 5
/* Прототип функции */
void print_them(int offset, char continue, -int iarray[SIZE]);
void main( void ) {
intoffset; /* индекс элемента массива */
int iarray[SIZE]; /* массив чисел */
charcontinue= 0; /* содержит ответ пользователя */
/* Первый раз функция выводит значения неинициализированных переменных */ print_them(offset,continue, iarray);
/* Приглашение для пользователя
*/
Printf(\n\nWelcome to a trace demonstration!");
printf("\nWould you like to continue (Y/N)");
scanf("%c",continue);
/* Пользователь вводит новые значения в массив*/
if (continue== 'Y' )
for (offset=0; offset < SIZE, offset++) { printf ("\nPlease enter an integer: "); scanf("%d",siarray [of f set] ) ;
/* Второй раз функция отображает значения, введенные пользователем */ print_them{offset, continue, iarray) ;
/* Функция, выводящая значения элементов массива */
void print_them(int offset, char continue, int iarray[SIZE])
{
printf("\n\n%d",offset);
printf("\n\n%d",continue);
for(offset=0; offset < SIZE, offset++)
printf("\n%d",iarray[offset]); }
Если вы хорошо знакомы с языком С, то наверняка заметили в программе ошибки. Не исправляйте их. Они были допущены специально, с целью обучить вас использованию различных средств отладки.
Желательно сохранить файл до того, как вы приступите к его компиляции и компоновке, а тем более до того, как попытаетесь запустить программу на выполнение. Опытные программисты могут рассказать много печальных историй о тех, кто пытался запустить на выполнение несохраненные файлы, после чего, вследствие непредвиденных фатальных ошибок, работу над приложением приходилось начинать сначала.
Чтобы сохранить введенный только что код, вы можете либо щелкнуть на третьей кнопке слева на стандартной панели инструментов, либо выбрать в меню File команду Save, либо нажать [Ctrl+S]. Когда вы в первый раз выбираете команду Save, открывается диалоговое окно Save. Сохраните файл под именем ERROR. С.
На рис. 3.3 окно редактирования показано до сохранения файла. После сохранения в строке заголовка окна отобразится новое имя файла.
Как правило, проекты для Windows 3.x, Windows95/98 и Windows, NT включают большое число файлов. Но сейчас мы начали работу над простейшей программой, состоящей из единственного файла. Этого вполне достаточно, чтобы разобраться с основными этапами создания полноценного приложения на C/C++.
Добавление файлов в проект
После того как проект создан, в него можно сразу же добавлять новые файлы. Перейдите на вкладку FileView и щелкните правой кнопкой мыши на элементе ERRORfiles. Контекстное меню с выделенной командой AddFilestoProject..., которое при этом откроется, показано на рис. 3.4.
При выборе данной команды появляется окно InsertFilesintoProject, где вы можете отметить файлы, подлежащие включению в проект. Одно замечание по поводу типа файлов: файлы заголовков (с расширением Н) не включаются в список файлов проекта, а добавляются в проект непосредственно во время построения программы с помощью директив препроцессора #include.
В нашем случае нужно найти созданный ранее файл ERROR.C и выполнить на нем двойной щелчок, в результате чего файл автоматически будет добавлен в проект.
После создания исходного файла можно приступить к созданию файла исполняемого. Согласно терминологии разработчиков VisualC++, этот процесс называется построением программы. Обратимся к показанному на рис. 3.6 меню Buildс выделенной командой RebuildAll.
Единственное различие между командами Build и RebuildAll, как вы помните, состоит в том, что команда RebuildAllне проверяет дату создания используемых в проекте файлов, т.е. компилирует и компонует все файлы проекта, независимо от того, когда они были созданы. Чтобы избежать недоразумений, связанных с тем, что системное время компьютера может быть легко изменено, при работе с небольшими приложениями рекомендуется использовать команду RebuildAll.
Если в программе были допущены синтаксические ошибки, при выполнении команд
Buildи RebuildAll сообщения о них будут отображаться
на вкладке Build окна Output(рис. 3.7).
Рис. 3.7. Вывод сообщений об ошибках в окне VisualC++
Каждое сообщение начинается с указания имени файла. Поскольку приложения Windows обычно содержат много файлов, это очень ценное свойство.
Сразу за именем файла, в круглых скобках, указан номер строки, в которой была обнаружена ошибка. В нашем случае первая ошибка найдена в восьмой строке. После номера строки, за двоеточием, идут слово error или warning и номер ошибки.
Разница между сообщениями error и warning состоит в том, что программы с предупреждениями могут быть выполнены, а приложения с ошибками обязательно требуют исправлений. В конце каждой строки сообщения дается краткое описание обнаруженной ошибки.
Предупреждающие сообщения
Предупреждающие сообщения могут появляться в тех случаях, когда компилятор автоматически выполняет некоторые стандартные преобразования и сообщает об этом программисту. Например, если переменной типа int(целое число) будет присвоено дробное значение, то автоматически произойдет округление. Это не означает, что в программе допущена ошибка, но поскольку преобразование типов данных происходит незаметно для программиста, компилятор считает своим долгом сообщить об этом.
Приведем еще один пример. Большинство функций, объявленных в файле МАТН.Н, принимают аргументы и возвращают значения типа double(действительное число двойной точности). Если программа передаст одной из таких функций аргумент типа float(действительное число одинарной точности), компилятор, прежде чем направить данные в стек аргументов функции, выведет предупреждающее сообщение о том, что тип данных float был преобразован в double.
Вы можете предотвратить появление предупреждающих сообщений, если будете явно преобразовывать типы данных переменных в соответствии с правилами, принятыми в языке С. Так, в рассматриваемом случае явное приведение аргументов к типу данных double перед выполнением функции предотвратит появление предупреждающего сообщения.
Первое сообщение об ошибке
Сообщение об ошибке, представленное на рис. 3.7, является вполне типичным. Наиболее часто с ним приходится сталкиваться тем, кто только осваивает новый язык программирования. В данном случае программист попытался задать переменной имя, зарезервированное для ключевого слова. Это хороший принцип — присваивать переменным имена, созвучные с их назначением, однако выбранное нами имя вступило в конфликт с ключевым словом continue, существующим в C/C++.
Использование команд Find и Replace
Довольно часто в процессе программирования возникают ситуации, когда вам нужно найти и заменить какое-то слово в тексте программы. Вы, конечно же, можете сделать это с помощью диалогового окна, открываемого командой Replace... из меню Edit, но имеется и более быстрый способ. Рассмотрите внимательно панель инструментов, показанную на рис. 3.8, и найдите в поле списка Find слово continue.
Чтобы воспользоваться этим средством поиска, щелкните мышью в поле и введите
слово, которое хотите найти, после чего нажмите [Enter]. На рис. 3.8 показан
результат такого поиска. В тексте программы выделено слово continue, обнаруженное
первым.
Рис. 3.8. Использование средств быстрого поиска
Данный метод достаточно удобен для поиска нужного слова.
Но наша задача этим не ограничивается, поскольку имя переменной continueнам
необходимо заменить во всей программе другим именем. В таком случае целесообразнее
воспользоваться командой Replace... из меню Edit(рис. 3.9).
Рис. 3.9. Окно поиска
Наша цель состоит в том, чтобы заменить имя переменной continue словом, также указывало бы на назначение этой переменной, но отличалось бы от ервированных имен. С этой целью введем в поле Replacewith слово continu. Но осталась маленькая проблема. В программе имеется строка "\nWould you like to continue (Y/N)". Если вы выполните автоматическую замену во всем файле, щелкнув на кнопке ReplaceAll, то сообщение, выдаваемое программой, будет содержать, грамматическую ошибку. Поэтому замену следует проводить последовательно, переходя от слова к слову, а в указанном месте щелкнуть на кнопке FindNext.
Быстрое обнаружение ошибочных строк
Теперь необходимо выполнить повторную компиляцию программы, после которой
окно сообщений будет выглядеть так, как показано на рис. 3.10.
Рис. 3.10 Обновленное окно сообщений
Существует достаточно быстрый способ перехода от окна сообщений к окну редактирования, и мы вам о нем расскажем. Поместите курсор на интересующей вас строке сообщения, например на первом предупреждении:
warning. C4013: 'Printf undefined;..,
А теперь просто нажмите [Enter]. Курсор в окне редактирования будет автоматически помещен в строку программы, вызвавшую появление сообщения об ошибке, а слева от строки появится стрелка (рис. 3.11).
Как вы уже знаете, языки C/C++ чувствительны к регистру символов. Поэтому
компилятор совершенно точно установил причину ошибки. Первая буква функции
printf() в нашей программе ошибочно была введена в верхнем регистре. Компилятор,
конечно же, не смог найти в библиотеке функцию Printf().Эту ошибку нетрудно
исправить — достаточно заменить букву Р буквой р. Затем обязательно сохраните
файл.
Рис. 3.11. Помечанная стрелкой строка программного кода, содержащая ошибку
После того как вы внесли исправления, программа готова к новой попытке построить исполняемый файл. Перейдите в меню Project и вновь выберите команду RebuildAll. На рис. 3.12 показано обновленное окно сообщений.
Теперь обнаруживаем, что та же строка, которая содержала некорректное имя функции (Printf()),заключает в себе еще одну ошибку. В C/C++ все строковые значения должны браться в двойные кавычки. Значит, в нашем случае открывающие кавычки в функции printf () следует поставить сразу после открывающей круглой скобки, т.е. строка должна начинаться следующим образом: printf(".
Сохраните файл и попытайтесь еще раз построить программу.
Как выглядит окно сообщений после очередного обновления, показано на рис.
3.13.
Рис. 3.13. Окно сообщений во время четвертой попытки построить исполняемый
файл
На этот раз выдается такое сообщение об ошибке:
syntax error: missing ';'before ')'
В C/C++, в отличие от Pascal, символ точки с запятой используется для обозначения окончания выражения, а не в качестве разделителя. Таким образом, в конце второй проверки в цикле for необходимо ввести точку с запятой. После исправления сохраните файл и вновь выполните команду RebuildAll.
Все в порядке? Судя по окну сообщений, у компилятора нет больше претензий к вашей программе, и команда RebuildAll успешно сгенерировала исполняемый файл ERROR.EXE.
В окне сообщений должны отсутствовать сообщения об ошибках, но присутствовать одно предупреждение, которое можно проигнорировать. Если это не так, значит, вы допустили ошибки при вводе программы. Проверьте программный код и исправьте его.
Чтобы запустить программу, просто выберите в меню Project команду Execute. Если в ответ на запрос программы Would you like to continue(Y/N) вы нажмете клавишу [Y], а затем [Enter], на экране отобразится следующее:
-858993460
0
-858993460
-858993460
-858993460
-858993460
-858993460
Welcome to a trace demonstration! Would you like to continue (Y/N)у
На рис. 3.14 показано, что произойдет далее.
Рис. 3.14. Сообщение об ошибке выполнения программы
Использование встроенного отладчика
Созданная нами программа в начале своей работы отображает на экране исходное содержимое массива данных, после чего спрашивает, хотите ли вы продолжить работу. Ответ Y (yes— да) сигнализирует о том, что вы хотите заполнить массив собственными данными и отобразить их на экране.
Из рис. 3.14 можно сделать вывод о том, что хотя программный код набран совершенно правильно, т.е. в нем нет синтаксических ошибок, программа работает не так, как нам бы хотелось. Ошибки такого рода называются логическими. К счастью, встроенный в VisualC++ отладчик содержит ряд средств, которые послужат для вас спасательным кругом в подобной ситуации. Во-первых, вы можете выполнять программу пошагово, строка за строкой. Во-вторых, вам предоставляется возможность анализировать значения переменных в любой момент выполнения программы.
Разница между командами StepInto и StepOver
Когда вы начинаете процесс отладки, появляется панель инструментов Debug. Из множества представленных на ней кнопок наиболее часто задействуются StepInto (четвертая справа в верхнем ряду) и StepOver(третья справа). В обоих случаях программа будет запущена на выполнение в пошаговом режиме, а в тексте программы выделяется та строка, которая сейчас будет выполнена.
Различия между командами StepInto и StepOver проявляются только тогда, когда в программе встречается вызов функции. Если выбрать команду StepInto, то отладчик войдет в функцию и начнет выполнять шаг за шагом все ее операторы. При выборе команды StepOver отладчик выполнит функцию как единое целое и перейдет к строке, следующей за вызовом функции. Эту команду удобно применять в тех случаях, когда в программе делается обращение к стандартной функции или созданной вами подпрограмме, которая уже была протестирована.
Давайте выполним пошаговую отладку нашей программы.
Как видно из рис. 3.15, в окне редактирования появилась стрелка (ее называют
индикатором трассировки), указывающая на строку программы, которая
будет выполнена на следующем шаге. В данный момент она указывает на функцию
print_them().
Рис. 3.15. Окно редактирования после того, как трижды была выполнена команда
StepInto или StepOver
Имеет смысл выполнить эту функцию как одно целое. Для этого выберем команду
StepOver. Функция будет выполнена, и индикатор трассировки укажет на первый
вызов функции printf(). Теперь три раза нажмите клавишу [F10], пока стрелка
не остановится напротив функции scanf().
В этот момент вам нужно перейти в окно программы и в ответ на приглашение
Would you like to continue(Y/N) ввести Y и нажать [Enter] (рис. 3.16).
Сразу после этого на экране появится сообщение об ошибке (рис. 3.17).
Это сообщение было сгенерировано программой после попытки выполнит функцию
scanf(). Давайте попытаемся разобраться, в чем, собственно, состоит проблема.
Ошибка связана с некорректным использованием функции scanf(). Функция scanf() ожидает указания адреса ячейки памяти для заполнения. Рассмотрим такое выражение:
scanf("%C", continu);
Как видите, здесь указывается не адрес переменной, а сама переменная.
Чтобы указать адрес, нужно поместить оператор взятия адреса (&) перед
continu. Внесите исправления в выражение, чтобы оно выглядело следующим образом:
scanf("%C", &continu);
Сохраните файл и вновь выберите команду RebuildAll.
Дополнительные средства отладки
Вы, очевидно, слышали о точках останова, которые применяются в программе при необходимости прервать ее выполнение в определенных местах. Смысл использования точек останова состоит в том, что отладчик не тратит времени на пошаговое выполнение программы вплоть до указанной точки, по достижении которой переходит в пошаговый режим.
Точки останова проще всего расставлять с помощью кнопки Breakpoint (первая справа) панели инструментов Build. Для этого достаточно установить курсор на нужной строке программы и щелкнуть на указанной кнопке. Если же выделенная строка уже содержит точку останова, то после щелчка на кнопке Breakpoint она, точка останова, будет удалена. При выборе команды Go программа будет выполняться от текущего местоположения курсора до ближайшей точки останова.
Обратимся к нашей программе. Мы знаем, что все строки программы до вызова функции scanf() отлично работают. Чтобы не тратить время на пошаговое выполнение всех строк, которые уже были проверены ранее, поставим точку останова на 20-й строке, содержащей вызов функции scanf().
Имеется и другой способ задания точек останова — с помощью диалогового окна
Breakpoints(рис. 3.18), вызываемого командой Breakpoints...
из меню Edit. По умолчанию при щелчке на кнопке со стрелкой
открывается контекстное меню, в котором первым пунктом указывается команда
создания точки останова на той строке, где в данный момент в окне редактирования
находится курсор. В нашем случае это строка 20.
Рис. 3.18. Задание точки останова
Работа с точками останова
Предположим, что вы поставили точку останова в строке программы, содержащей вызов функции scanf{). Теперь выберите команду Go— либо из меню, либо нажав клавишу [F5]. Обратите внимание, что выполнение программы прерывается не на первой строке программы, а на строке, содержащей точку останова.
Далее можно продолжить выполнение программы в пошаговом режиме либо проанализировать
текущие значения переменных. Нас интересует, будет ли функция scanf() работать
корректно после того, как в программный код были внесены изменения. Выберите
команду StepOver, перейдите к окну программы, введите
букву Y в верхнем регистре и нажмите клавишу [Enter]. (Мы применили команду
StepOverдля того, чтобы избежать пошагового анализа отладчиком всех
операторов функции scanf(). При выборе команды StepIn появляется предложение
указать местонахождение файла SCANF.C)
Все отлично! Отладчик не выдал окна с сообщением об ошибке. Но означает ли
это, что все проблемы разрешены? Чтобы ответить на этот вопрос, достаточно
будет проанализировать текущее значение переменной continu.
Команда QuickWatch... открывает диалоговое окно QuickWatch(рис. 3.19), которое
позволяет по ходу выполнения программы анализировать значения переменных.
Простейший способ определить значение переменной с помощью данного окна состоит
в том, что курсор помещается на имени переменной в окне редактирования, а
затем нажимается комбинация клавиш [Shift+F9]. Проделайте указанную операцию
с переменной continu.
Рис. 3.19. Диалоговое окно QuickWatch
Теперь, когда мы определили, что переменная continu имеет правильное значение, можно продолжить выполнение программы до конца, выбрав в меню Debug команду Go(рис. 3.20).