
- •Глава 4
- •Глава 5
- •Часть 1: использование турбо си.............................225
- •Часть 2. Команды меню.......................................280
- •Часть III. Дополнительные сведения о конфигурационных
- •Часть IV. Дополнительные возможности и команды
- •Глава 6
- •Глава 1: Использование библиотечных функций Турбо Си. Приво-
- •Глава 2: Библиотека Турбо Си. Описание всех библиотечных фу-
- •Глава 9, "Замечания для программистов, работающих на Турбо
- •Глава 4
- •Глава 5
- •Часть 1: использование турбо си.
- •Часть 2. Команды меню
- •Часть III. Дополнительные сведения о конфигурационных
- •Часть IV. Дополнительные возможности и команды
- •Глава 6
Глава 4
-------
ОТЛАДКА ПРОГРАММ
-----------------------------------------------------------------
Когда, после исправления всех ошибок компиляции и компонов-
ки, вы впервые запустите свою программу - вероятность ее правиль-
ной работы будет очень мала. Новая программа почти всегда содер-
жит многочисленные ошибки - логические и концептуальные - которые
вы должны найти и исправить. Процесс поиска и исправления ошибок
называется отладкой.
Очень сложно найти ошибки, наблюдая за "поведением" програм-
мы, поэтому для их обнаружения в своих программах большинство
программистов используют отладчик. Отладчик - это программное
средство, позволяющее управлять программой во время ее выполне-
ния. С помощью отладчика вы можете остановить выполнение програм-
мы в любой точке, выполнить отдельный оператор и просмотреть зна-
чения данных, на которые этот оператор воздействовал.
В этой главе ...
-----------------------------------------------------------------
Интегрированная среда Турбо Си включает отладчик, который
называется интегрированным отладчиком. В этой главе мы объясним,
как пользоваться отладчиком Турбо Си.
Глава начинается с серии примеров, демонстрирующих примене-
ние отладчика. В первом примере показаны простейшие возможности
распознавания "легких" ошибок. И следующие примеры иллюстрируют
более сложные возможности отладчика.
Затем будет рассмотрено меню команд отладчика с соответству-
ющими "горячими" клавишами или комбинациями "горячих" клавиш, а
также будет дано описание каждой команды.
И, наконец, мы дадим несколько советов, облегчающих отладку.
Многие из этих советов касаются не только самого процесса отлад-
ки, но и того, как вообще лучше писать программы, поэтому боль-
шинство из этих идей вы можете применять не только в Турбо Си, но
и в любых других языках программирования.
- 127,128 -
Как работает интегрированный отладчик
-----------------------------------------------------------------
Интегрированный отладчик Турбо Си - это символьный отладчик,
работающий в терминах языка программирования. Это значит, что вы
можете "общаться" с отладчиком с помощью того же языка, на кото-
ром написана ваша программа. Например, вы можете просмотреть ве-
личину элемента массива, указав его отладчику с помощью следующе-
го выражения:
rptr->image[nptr+0x80]
Отлаживать свою программу вы сможете просто запустив ее с
помощью элемента меню Run/Run ("горячая" клавиша Ctrl-F9). При-
чем, для подключения отладчика к вашей программе, необходимо ее
откомпилировать с "установленным" (On) вариантом меню Source
Debugging. (Установить этот вариант меню можно выбрав
Debug/SOURCE Debugging).
Перед выполнением программы вы можете установить в ней точки
останова, указав их в одной или нескольких строках исходного
текста вашей программы. Когда отлаживаемая программа встретит
точку останова, она остановится перед первым оператором строки, в
которой эта точка задана, и возвратит вам управление отладчиком.
В момент остановки программы вы можете выполнять самые раз-
личные действия. Например, вы можете:
# вывести значение переменной или выражения;
# задать в специальном окне список выражений и наблюдать за
изменениями их значений;
# изменить значение переменной;
# удалить существующую точку останова или задать новую;
# выполнить одну строку программы;
# исправить файл, перекомпилировать и перекомпоновать прог-
рамму или использовать любые другие возможности меню системы Тур-
бо Си;
# продолжить выполнение программы до тех пор, пока не встре-
- 129,130 -
тится другая точка останова.
Рисунок 4.1 иллюстрирует типичный алгоритм сеанса отладки.
(Заметьте, что на нем не показаны возможности отладчика на каждом
отдельном шаге.)
┌─────────────────────┐
│Написание или модифи-│
│ кация программы │
└──────────┬──────────┘
┌─────────────────────────────┤
│ ┌───────────┴─────────────────┐
│ │Создание (компиляция и компо-│
│ │ новка) программы │
│ └───────────┬─────────────────┘
│ │
│
│
┌──┴────────┐ Да Ошибки
│Исправление├─────── компиляции и/или
│ ошибок │ редактирования ?
└──┬────────┘
│ │
│ │Нет
│ │
│ ┌──────────┴─────────────┐
│ │Задание или изменение │
│ │ точек останова │
│ └──────────┬─────────────┘
│ ┌──────────┴────────┐
│ │ Запуск программы │
│ └──────────┬────────┘
│ │
│ │
┌────┴──────┐
│Исправление│ Ошибки Нет ┌─────────┐
│ ошибок │ выполнения ?──────────┤ Останов │
└────┬──────┘ │ │
│ │ └─────────┘
│ │ Да
│ │
│ ┌──────────┴────────┐
└──────────────────┤Отладка программы │
└───────────────────┘
- 131,132 -
Рисунок 4.1: Типичный алгоритм процесса отладки
Пример 1: Отладка простой программы.
Для получения первого опыта отладки с помощью Турбо Си, вы
можете воспользоваться программой, приведенной ниже. Мы ее назва-
ли WORDCNT. Она выводит содержимое текстового файла и подсчитыва-
ет длины его слов, то есть сообщает о том, сколько в этом файле
есть слов, состоящих из одной, двух и т.д. букв. К несчастью,
WORDCNT содержит несколько ошибок, и вы должны их найти, исполь-
зуя для этой цели отладчик.
Программа WORDCNT находится в файле WORDCNT.C на одном из
дистрибутивных дисков. Для того, чтобы сохранить ее "нетронутой",
скопируйте ее в ваш каталог Турбо Си.
Если вы работаете в каталоге, не содержащем Турбо Си, то
сделайте в нем рабочие копии файлов проекта WORDCNT.C и
WORDCNT.PRJ. Все три файла должны быть на дистрибутивных дисках и
в вашем каталоге Турбо Си.
/*****
* Read a text file; count the numberof words of length 1, 2, 3,
* etc. (Прим. Диалог с пользователем здесь адаптирован, на
* дистрибутивных дисках - нет )
* Note: This programm is for use with the debugging tutorial
* in the debbuging chapter of the User's Guide. It
* intetionally contains bugs.
*****/
#include <stdio.h>
#include <ctype.h>
#define MAXWORDLEN 16
#define NULL ((char)0)
#define SPACE ((char)0x20)
/*****
* Find the next word in the line buffer.
* IN: wordptr points to the first character of a word or a
* preceding space.
- 133,134 -
* RETURN: A pointer to the first character of the word. If there
* are no more words, a pointer to the terminating NUL.
*****/
char *nextword(char *wordptr)
{
/* Advance to the first non-space. */
while ( *wordptr==SPACE )
wordptr++;
return (wordptr);
}
/*****
* Find the length of a word. A word is defined as sequence of
* characters terminated by a space or a NUL.
* IN: wordptr points to a word.
* RETURN: The length of the word.
*****/
int wordlen(char *wordptr)
{
char *wordlimit;
wordlimit = wordptr;
while ( *wordlimit & *wordlimit!=SPACE )
*wordlimit++;
return(wordlimit-wordptr);
}
/*****
* The main function.
*****/
void main(void)
{
FILE *infile; /* Input file. */
char linebfr[1024], /* Input line buffer, very
long for safety */
*wordptr; /* Pointer to next word in
linebfr */
int i; /* Scratch variable. */
static int wordlencnt[MAXWORDLEN],
/* Word lengths are counted in elements 1
to MAXWORDLEN. Element 0 isn't used.
- 135,136 -
The array is static so that the elements
need not be set to zero at run time. */
overlencnt; /* Overlength words are counted
here. */
printf("Эта программа предлагается в качестве примера для\n");
printf("проведения практического занятия по отладке\n");
printf("Если вы не хотите ее выполнить под управлением\n");
printf("интегрированной среды, нажмите Ctrl-Break.\n");
printf("Смотрите главу Отладка программ Руководства");
printf("пользователя для уточнения деталей.\n\n");
printf("Введите имя файла: ");
gets(linebfr);
if ( !strlen(linebfr) ) {
printf("Вы должны указать имя файла !\n");
exit();
}
infile = fopen( linebfr,"r" );
if ( !infile ) {
printf("Файл нельзя открыть !\n");
exit();
}
/* Each loop processes one line. NOTE: If a line is longer
than the input buffer, the program may produce invalid
results. The very large buffer makes this unlikely. */
while ( fgets( linebfr, sizeof(linebfr), infile ) ) {
printf("%s\n",linebfr);
/* Check for buffer overflow & remove the trailing newline.
*/
i=strlen(linebfr);
if ( linebfr[i-1] != '\n' ) {
printf("Длина строки превышает максимальную");
printf("\n\t%70s\n",linebfr); }
else
linebfr[i-1] = NULL;
/* lineptr points to the 1st word in linebfr (past leading
spaces). */
wordptr = nextword(linebfr);
- 137,138 -
/* Each loop processes one word. The loop ends when
[nextword] returns NULL, indicating there are more
words. */
while (*wordptr) {
/* Find the length of this word, increment the proper
element of the length count array, and point to the
space following the word .*/
i=wordlen(wordptr);
if ( i > MAXWORDLEN )
overlencnt++;
else
;
wordlencnt[i]++;
wordptr += i;
/* Find the next word (if any). */
wordptr = nextword(wordptr);
}
}
/* Print the word length counts. Each loop prints one. */
printf("Длина Количество\n");
for ( i=1, i=MAXWORDLEN, i++)
printf(" %5d %5d\n", i, wordlencnt[i] );
printf("Остальные %5d\n", overlencnt );
/* Close the file and quit. */
fclose(infile);
}
Убедитесь в том, что опции Debug/Source Debugging и
O/C/C/OBJ Debug Information были включены (On). В ответ на приг-
лашение DOS запустите Турбо Си с помощью следующей команды
TC WORDCNT
в ответ на приглашение DOS. Создайте программу (выбрав
Compiler/Build All). Турбо Си выполнит компиляцию и компоновку
программы WORDCNT, тем самым подготовив ее для выполнения, а за-
тем остановится. Теперь выберите элемент меню Run/Trace Into (или
нажмите F7).
В этот момент отладчик разместит в окне редактирования вашу
программу так, чтобы было видно начало функции main. Строка, со-
- 139,140 -
держащая объявление функции main (void main(void)), будет выделе-
на (цветом), свидетельствуя о том, что при запуске WORDCNT она
будет выполнена первой. Эта выделенная строка называется "марке-
ром выполнения", фиксирующим текущую позицию программы: строку,
содержащую оператор, который будет выполнен следующим.
Для того, чтобы выполнить WORDCNT, выберите Run/Run. WORDCNT
предложит вам ввести имя входного файла. Наберите WORDCNT.DAT и
нажмите Ввод. WORDCNT считает и выведет первую строку файла дан-
ных, а затем ваш компьютер "зависнет", т.к. в программе есть
ошибки. Нажав Ctrl-Break и затем Esc, вы разблокируете компьютер
и сможете продолжить работу. Для завершения сеанса отладки необ-
ходимо выбрать Run/Program Reset (или нажать "горячую" клавишу
Ctrl-F2), а для возобновления отладки - Run/Trace Into (или на-
жать F7).
Установка и использование точки останова
-----------------------------------------------------------------
Первая часть функции main, согласно комментариям в исходном
тексте программы, выдает приглашение для ввода имени входного
файла и открывает этот файл. Тот факт, что WORDCNT считывает и
выводит первую строку входного файла свидетельствует о правильной
работе этой части программы, по крайней мере, она не причастна к
наблюдаемой ошибке. Поэтому вы можете выполнить первую часть main
и остановиться, когда достигнете подозрительных, с вашей точки
зрения, строк программы. Для этого, вы должны установить точку
останова в той строке, где хотите остановиться.
Используя клавиши PgDn и "стрелка вниз", переместите курсор
в строку, начинающуюся с
while(fgets(...
(эта строка находится сразу же за комментарием, начинающимся со
слов "Each loop proceess...")
Обратите внимание, что маркер выполнения не движется. Это
- 141,142 -
происходит из-за того, что никакие операторы программы не выпол-
няются; вы лишь перемещаете курсор редактора.
Для того, чтобы установить точку останова, необходимо помес-
тить курсор в нужную строку и выбрать Break/Watch/
TOGLE/Breakpoint (или нажать комбинацию "горячих" клавиш
Ctrl-F8). Отладчик выделит указанную строку (цветом), что будет
свидетельствовать об установке в этой строке точки останова. За-
метьте, что строка, в которой находится точка останова и маркер
выполнения, выделяются по-разному (разными цветами).
Выберите Run/Run (или нажмите Ctrl-F9) и WORDCNT продолжит
свою работу (в данном случае - с самого начала). После выдачи со-
общения о необходимости ввода имени входного файла, WORDCNT оста-
новится и будет ожидать ввода с клавиатуры. Отладчик выводит со-
общение WORDCNT на экран выполнения (Execution), на который
сообщения WORDCNT выдаются так, как если бы ее работа протекала
без отладчика. Наберите WORDCNT.DAT и нажмите Ввод. WORDCNT про-
должит выполнение до тех пор, пока не встретится точка останова;
затем она остановится и на экране вновь появится окно редактиро-
вания (Edit). Курсор и маркер выполнения будут указывать на опе-
ратор while.
Заметим, что установленная вами на операторе while точка ос-
танова не исчезла, вы просто не видите ее, потому что ее заслонил
маркер выполнения, но она вновь появится после того, как маркер
выполнения переместится дальше. Каждый раз, "доходя" до этой точ-
ки останова, WORDCNT будет останавливаться. И это будет продол-
жаться до тех пор, пока вы ее не "выключите".
- 143,144 -
Использование Ctrl-Break
-----------------------------------------------------------------
Помимо всех заданных вами точек останова вы можете восполь-
зоваться во время выполнения программы "моментальной" точкой ос-
танова, нажав Ctrl-Break. Используя это средство, вы можете оста-
навливать программу в любой момент времени. Когда вы нажимаете
Ctrl-Break, вы выходите из программы и возвращаетесь в редактор
Турбо Си, причем маркер выполнения находится на строке, которая
должна быть выполнена в следующий раз, а сама программа готова
продолжить свою работу.
Объясним, как в действительности происходит останов програм-
мы при нажатии Ctrl-Break.
В процессе своей работы отладчик взаимодействует с DOS, BIOS
и другими частями операционной системы; поэтому ему известно, что
работает в данный момент времени (подпрограмма DOS, функция BIOS
или же именно фрагмент вашей программы). После того, как вы наж-
мете Ctrl-Break, отладчик дождется, чтобы заработала непосредс-
твенно ваша программа. Затем, на уровне инструкций микропроцессо-
ра, он сделает несколько шагов, чтобы дойти до кода,
соответствующему началу исходной строки на Си. И только после
всего этого он остановится и переведет маркер выполнения на дан-
ную строку.
Если же вы повторно нажмете Ctrl-Break прежде, чем отладчик
найдет и укажет строку программы, то он немедленно прервет работу
программы и не будет пытаться найти строку исходного текста. В
этом случае, программа останавливается без завершения своего вы-
вода и вызова какой-либо функции выхода. (Что аналогично заверше-
нию работы программы, выполняемому с помощью функции _exit). Поэ-
тому, дважды нажимать Ctrl-Break вам следует лишь тогда, когда
программа "зациклилась" и одно нажатие ее не останавливает.
- 145,146 -
Пошаговое выполнение "через функции"
-----------------------------------------------------------------
Теперь, когда вы достигли той части WORDCNT, в которой могут
быть ошибки, необходимо быть очень внимательным. Данный фрагмент
main будет обрабатывать по одной строке, сравнивая получаемые ре-
зультаты с желаемым эффектом.
Для того, чтобы выполнить одну строку main выберите Run/Step
Over. Отладчик отработал оператор while и считает первую строку
входного файла. Затем он переместит маркер выполнения на следую-
щую строку, содержащую исполняемые операторы. Повторно воспользо-
вавшись Run/Step Over можно выполнить следующий оператор
(printf).
Привело ли к желаемому эффекту выполнение оператора while?
Для того, чтобы определить это, выберите вариант User Screen из
меню Run, или нажмите Alt-F5. В результате этой команды вы увиде-
те экран выполнения. Т.к. на этом экране видна первая считанная
из файла строка, вы можете сделать вывод, что и while и printf
работают правильно. Для возврата в окно редактирования нажмите
любую клавишу.
"Горячей" клавишей для команды Run/Step Over является F8.
Поэтому, для выполнения очередного оператора нажмите F8, что при-
ведет к вычислению длины введенной строки, значение которой прис-
воится переменной i.
Внимание! "трассирование в" или "шаг через" библиотечную
функцию longjmp не приведет к остановке на следующей строке прог-
раммы (т.к. эта функция никогда не возвращается). По этой причине
ваша программа (в этом случае) будет выполняться до следующей
точки останова или до самого конца.
- 147,148 -
Вычисление выражений
-----------------------------------------------------------------
Приводит ли выполнение оператора присваивания к желаемому
результату? Узнать это можно оценив значение переменной i. Выбе-
рите Debug/Evaluate. Отладчик откроет окно содержащее три поля.
Мы будем называть эти поля в соответствии с присвоенными им функ-
циями:
1. Поле Evaluate (вычислений): сюда вы вводите те выражения,
которые вы хотите вычислить и, возможно, изменить. (Замечание:
Если длина вашего выражения превышает размер поля вычислений, то
для его просмотра вы можете воспользоваться клавишами управления
курсором).
2. Поле RESULT (результата): в это поле отладчик выводит
вычисленное значение.
3. Поле NEW VALUE (нового значения): в этом поле вы можете
присвоить своему выражению новое значение (при необходимости).
Отметим, что в поле вычислений инидицируется слово. Это сло-
во, перенесенное в данное поле из текущей позиции курсора в окне
редактирования, является значением поля вычислений, принятым по
умолчанию. Мы сразу же можем воспользоваться им. Однако в нашем
случае, введите выражение i и нажмите Ввод. В поле результата от-
ладчик поместит значение переменной i. Оно равно 43, что является
правильным. Для выхода из системы меню нажмите F10.
Следующая группа операторов программы проверяет, заканчива-
ется ли символом новой строки строка, считанная функцией fgets.
Если нет, то можно сделать вывод, что считанная строка является
слишком длинной для имеющегося буфера, и, поэтому, программа вы-
ведет соответствующее предупреждение. В противном же случае - все
в порядке. Программа удалит из строки найденный символ окончания,
для того, чтобы он не был включен в размер последнего слова.
Перед выполнением оператора if было бы очень полезно взгля-
нуть на введенную строку, для того чтобы узнать, что нужно ждать
от выполнения этого оператора. Установите курсор в окне редакти-
рования над словом linebfr и снова выберите элемент меню
Debug/Evaluate (или же "горячую" клавишу Ctrl-F4). Отладчик выве-
дет в появившееся окно (в поле вычислений) слово "linebfr". Наж-
мите Ввод.
- 149,150 -
Отладчик выведет:
To be or not to be: that is the question.\n
Точно также, как и в исходных текстах Си-программы, обозна-
чение "\n" соответствует символу конца строки.
Теперь, нажав F8, обработайте оператор if. В случае, если
этот оператор сработает правильно, маркер выполнения должен пе-
редвинуться на предложение "else". Выполните оператор присваива-
ния (под else), а затем вновь просмотрите linebfr. (Для этого вы
можете использовать Ctrl-F4 или элемент меню Debug/Evaluate).
Символ новой строки (\n) должен быть удален. Ну и слава богу,
пойдем дальше.
Функции nextword и wordlen
-----------------------------------------------------------------
Следующий оператор вызывает функцию nextword. Эта функция
определяет положение очередного (в данном случае - первого) слова
в строке. Обработайте этот оператор, используя F8, а затем пос-
мотрите wordptr для того, чтобы узнать что же вернула nextword.
Вы можете убедиться, что wordptr указывает на T в строке To be or
not to be: that is the question. Если это так, то nextword рабо-
тает правильно, по крайней мере в этом простом случае.
Следующим фрагментом программы является цикл while. Каждая
итерация этого цикла должна обрабатывать одно слово из linebfr,
знаращивая при этом wordptr так, чтобы он указывал на новое сло-
во. После того, как таким образом в этом цикле будет обработано
последнее слово, wordptr должен указывать на ограничивающий стро-
ку null символ, а сам цикл должен завершиться.
Выполните оператор while. Маркер выполнения переместится на
первый оператор в теле цикла, который вызывает функцию wordlen.
Эта функция определяет длину слова, на которое указывает wordptr.
Выполните оператор и оцените значение переменной i. Значение i
- 151,152 -
равно 0, что является неправильным; длина первого слова обрабаты-
ваемой строки должна быть равна 2. Мы нашли ошибку!
Остановимся и подумаем
-----------------------------------------------------------------
Прежде чем вы исправите найденную ошибку, давайте обсудим ее
влияние на отлаживаемую программу. Некорректная длина слова (0)
"имеет" два значение. Во-первых, из-за нее неправильно увеличится
значение нулевого элемента массива wordlencnt. А во-вторых, нуле-
вая длина приведет к тому, что после обработки оператора wordptr
+= i, значение указателя wordptr не изменится. Из-за этого, вто-
рая итерация цикла loop начнется с тем же самым значением
wordptr, что и в первой. Поэтому, точно также как и при первом
обращении к wordlen (когда она вернула 0), повторный вызов этой
функции также возвратит все тот же 0. Следовательно, значение
wordptr останется неизменным и в третьей итерации цикла loop, и в
четвертой, и в последующих. Таким образом, найденная ошибка явля-
ется причиной "зависания" вашего компьютера.
Какой вывод можно сделать из проведенного исследования? Вы
можете подумать, что найденная ошибка объясняет только часть неп-
равильной работы WORDCNT или же, что неправильная работа програм-
мы объясняется не только найденной ошибкой. Для того, чтобы уви-
деть, что же происходит дальше, в обоих случаях вы можете
- 153,154 -
захотеть вновь запустить программу. Сделав это, вы захотите сос-
редоточиться на обнаружении новых ошибок, причем, понятно, вы за-
хотите, чтобы уже просмотренный фрагмент программы работал пра-
вильно.
Что же делать?
-----------------------------------------------------------------
Вы выяснили, что неправильная работа WORDCNT объясняется
ошибкой в функции wordlen. Кроме того вы точно определили, в чем
выражается неправильная работа этой функции. Чуть позже мы вер-
немся к этим рассуждениям, а сейчас давайте познакомимся с коман-
дами отладчика. В процессе вашей первой попытки отладки программы
WORDCNT вы делали следующее:
# убеждались в том, что опции Debug/Source Debugging и O/C/C
/OBJ Debug включены (On).
# для подготовки WORDCNT к отладке выбирали Compiler/Build
All.
# пользуясь командами редактора, перемещали курсор на нужные
фрагменты WORDCNT; выбирали Break/Watch/Toggle Breakpoint
для установки точек останова в позиции курсора; для запус-
ка WORDCNT до заданной точки останова пользовались Run/Run
(или же "горячей" клавишей Ctrl-F9).
- 155,156 -
# пользовались Run/USER SCREEN или Alt-F5 для просмотра вы-
ходных данных вашей программы на пользовательском экране.
# для обработки операторов функции main (по одной строке
программы за шаг) выбирали Run/Step Over (или же нажимали
"горячую" клавишу F8).
# выбрав Debug/Evaluate (или нажав Ctrl-F4), просматривали
значения нескольких переменных.
# проанализировав найденную ошибку, сделали вывод, что она
объясняет некорректную работу WORDCNT и, поэтому, требует
немедленного исправления.
Содержимое окна вычислений, установленное по умолчанию
-----------------------------------------------------------------
Напомним, что выбор Debug/Evaluate переносит в поле вычисле-
ний слово, на которое указывает курсор в окне редактирования.
Благодаря этому, вы можете облегчить себе работу, если перед об-
ращением к Debug/Evaluate установите курсор над переменной , зна-
чение которой вы хотите оценить. Даже если все выражение, которое
вы хотите вычислить, является весьма сложным, то вы можете значи-
тельно ускорить его ввод, если будете редактировать принятое по
умолчанию в окно вычислений выражение, а не вводя его на пустом
месте. Более того, нажимая клавишу со стрелкой вправо вы можете
"перенести" из окна редактирования в окно вычислений еще несколь-
ко требуемых вам символов. Каждый раз, когда вы будете нажимать
эту клавишу, будет выполняться перенос одного символа.
Например, пусть вы хотите вычислить значение выражения
linebfr[i-1], которое находится в следующей строке исходного фай-
ла:
if (linebfr[i-1] != '\n' )
- 157,158 -
Для этого переместите курсор к linebfr и выберите
Debug/Evaluate. В поле вычислений (Evaluate) будет скопировано
слово linebfr. Нажав пять раз клавишу со стрелкой вправо, добавь-
те к этому слову [i-1], затем нажмите Ввод.
Изменение значения оцениваемого выражения
-----------------------------------------------------------------
Элемент меню Debug/Evaluate способен изменять значения неко-
торых типов выражений, а именно, тех выражений, которые соответс-
твуют конкретному элементу данных, например: i, linebfr[i] или
*(linebfr+i).
Попробуем вычислить значение переменной i, а затем изменить
его. После того, как для получения значения переменной i вы наж-
мете Ввод, отладчик выведет это значение в поле результата. Пе-
рейдите (с помощью клавиши со стрелкой вниз) в поле нового значе-
ния и введите в него то значение, которое вы хотите присвоить i.
Например, вы можете ввести i+1 (с целью увеличения i на страницу)
или же просто 17. После того, как вы нажмете Ввод, отладчик вы-
числит введенное вами выражение, изменит значение i, а затем вы-
ведет в поле результата новую величину. (Замечание: Запомните!
После того, как в поле новой величины в нажмете Ввод, значение
переменной будет измененно в соответствии с введенным выражением
и нажатие после этого клавиши Esc не приведет к отмене сдаланных
изменений.) Для того, чтобы выйти из Debug/Evaluate, нажмите Esc,
затем снова выберите Debug/Evaluate и посмотрите значение пере-
- 159,160 -
менной i с тем, чтобы убедиться в том, что оно изменилось. После
этого восстановите старое значение i и опять выйдите из
Debug/Evaluate.
Изменив значение какого-либо выражения, вы можете избежать
проявления программной ошибки, что позволит вам запустить свою
программу дальше, в результате чего могут быть найдеты какие-либо
дополнительные ошибки. Кроме этого изменение значений выражений
позволяет исследовать поведение отдельных частей программы. В ка-
честве примера предположим, что вы хотите проверить поведение от-
дельных частей программы. Например, предположим, что вы хотите
проверить поведение какой-либо функции, при передаче ей некор-
ректных параметров. Вполне возможно, что сделать так, чтобы ваша
программа передала в функцию требуемое для этого значение, вам
сложно, однако вы можете получить нужный результат, изменив зна-
чение какой-либо переменной непосредственно перед тем, как прог-
рамма обратится к проверяемой функции.
Если для выхода из поля нового значения вы воспользуетесь
клавишей Esc (а не Ввод), то отладчик не будет менять значение
выражения. Мы советуем вам пользоваться этой клавишей в случае,
если вы по какой-либо причине изменили свои намерения и решили не
вводить нового значения.
Вы можете вычислить значение любого допустимого в Си
выражения, при условии, что оно не содержит:
# вызовы функции;
# описанные символы (с помощью define) или типы (с помощью
typedef). Например: *wordptr == 0x20 верно, тогда как *wordptr ==
SPACE - не допускается, так как SPACE является описанием);
# локальные или статические переменные, находящиеся вне
области действия выполняемой функции (за исключением случая, ког-
да они полностью специфицированы).
- 161,162 -
Спецификация имен переменных
-----------------------------------------------------------------
Существует две типичные ситуации, в которых возникает необ-
ходимость подробной спецификации имен переменных, использзуемых в
выражении:
# когда вы хотите проконтролировать значения переменных типа
static, находящихся в различных модулях;
# или, когда вы хотите просмотреть переменные типа auto (ло-
кальные) или типа static, находящиеся в другой функции.
Для полного описания имени переменной воспользуйтесь
следующим синтаксисом:
.<имя модуля>.<имя функции>.<имя переменной>
Отметим, что как имя модуля, так имя функции в определенных
случаях могут пропускаться. Например, если вы трассируете свою
функцию main и хотите узнать значение статической переменной с
именем myvar, находящуюся в другом модуле с именем mysubs, то вам
нужно ввести .mysubs.myvar. Если же переменная myvar находится в
функции myfunc модуля mysubs, то для определения ее значения вы
должны ввести .mysubs.myfunc.myvar. С другой стороны, если пере-
менная myfunc, расположенная в том же самом моделе, что и функция
main, то для определения ее значения вам достаточно ввести только
.myfunc.myvar.
- 163,164 -
Спецификаторы формата
-----------------------------------------------------------------
Для управления информацией, выводимой в окно Debug/Evaluate,
Турбо Си предоставляет вам спецификаторы формата выражений в поле
вычислений (которые подходят и для окна просмотра (Watch)). Спе-
цификаторы формата следуют за выражением, разделяются запятой и
могут быть набраны на верхнем или нижнем регистрах.
Спецификатор формата состоит из необязательного повторителя
(целого), следующего за ним символа формата; причем между повто-
рителем и символом формата не должно быть пробелов. В таблице 4.1
приводится список имеющихся символов формата и их описание.
Повторитель используется для вывода следующих подряд пере-
менных, типичным примером которых может служить массив. Например,
если list массив из 10 чисел, то выражение list будет выведено
следующим образом:
list: ( 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 )
Если вы хотите посмотреть часть массива, вы можете указать
индекс первого элемента и повторитель:
list[5],3: 60, 70, 80
Эта техника в частности полезна при выводе массивов большой
размерности, которые не помещаются в одной строке.
Применение повторителей не ограничивается массивами; за
любой переменной может следовать повторитель. Выражение
синтаксиса var,<n> просто выводит последовательность из n пере-
менных такого же типа что и var, начиная с адреса var. Заметьте,
однако, что повторитель игнорируется если ваше выражение не соот-
ветствует переменной. В данной конструкции может использоваться
переменная если она находится в левой части оператора присваива-
ния или, если она используется в качестве аргумента функции.
- 165,166 -
Таблица 4.1 ОТЛАДОЧНЫЕ СПЕЦИФИКАЦИИ ФОРМАТА
-----------------------------------------------------------------
Символ Функция
-----------------------------------------------------------------
С Символ. Служит для замены управляющих символов
(ASCII от 0 до 31) на специальные символы. Напри-
мер ^С будет выведен как "Счастливое лицо" (Happy
face). Применяется для символов и строк.
S Строка. Выводит управляющие символы (ASCII от 0 до
31) как величины ASCII, в соответствии с принятыми
в Си управляющими последовательностями. Так как
функция выводит символ и строку по умолчанию, то
спецификатор S полезен только со спецификатором M.
D Десятичный. Все целые величины выводятся в
десятичном виде. Применяется для простых числовых
выражений, таких как массивы и структуры
содержащие числа.
H или X Шестнадцатиричный. Все целые величины выводятся в
шестнадцатеричном виде с префиксом 0x. Применяется
для простых числовых выражений, таких как массивы
и структуры, содержащие целые числа.
F<n> С плавающей точкой. n - целое от 2 до 18,
обозначает количество значащих цифр,
предназначенных для вывода. Применяется только для
величин с плавающей точкой.
M Дамп памяти. Выводит дамп памяти, начиная с
адреса, указанного в выражении. Выражение может
быть конструкцией, допустимой в левой части опера-
тора присваивания, т.е. конструкцией, обозначающей
адрес памяти; в остальных случаях спецификатор M
игнорируется. По умолчанию, каждый байт переменной
представляется двумя шестнадцатиричными цифрами.
Если спецификатор M дополнен спецификатором D, то
байт представляется в виде десятичных цифр, если
же X или H, то в виде шестнадцатиричных. В случае
спецификаторов C или S переменные выводятся как
строки ( с или без специальных символов). По умол-
чанию, количество байт соответствует размеру пере-
- 167,168 -
менной, но может быть использован повторитель для
точного указания количества байт.
P Указатель. Выводит указатели в виде seg:ofs с
дополнительной информацией об адресе, это
предпочтительнее чем принятый по умолчанию машинно
-зависимый seg:ofs формат. Формат seg:ofs говорит
вам об области памяти, в которой размещен сегмент
и имени переменной по адресной ссылке. Ниже
приведены области памяти:
--------------------------------------------------
Область памяти Пояснение
--------------------------------------------------
0000:0000-0000:03FF Таблица вектора прерывания
0000:0400-0000:04FF Область данных BIOS
0000:0500-Турбо Си MSDOS/TSR's
Турбо Си - Програм- Турбо Си
мы пользователя PSP
PSP процесса поль- PSP процесса пользователя
зователя
Процесс пользовате- Имя статической переменной
ля - начало RAM пользователя, если ее адрес
лежит вне памяти размещенных
переменных; в противном случае
ничего.
- 169,170 -
A000:0000-AFFF:FFFF Видео-память EGA
B000:0000-B7FF:FFFF Память монохромного дисплея
B800:0000-BFFF:FFFF Память цветного дисплея
C000:0000-EFFF:FFFF EMS страница/Адаптер BIOS
ROM's
F000:0000-FFFF:FFFF BIOS ROM's
--------------------------------------------------
R Структуры/объединения. Отображает имена полей, так
и их значения, например {X:1, Y:10, Z:5}. Применя-
ется только для структур и объединений.
-----------------------------------------------------------------
Ниже приведены некоторые основные правила применения
спецификаторов формата:
1. Спецификаторы формата эффективны только в том случае,
если они применяются для переменной соответствующего типа. В
противном случае они игнорируются.
Обратите внимание, что если вам необходимо вычислить выраже-
ние состоящее из множества объектов (например, структура), то
нужно указать несколько спецификаторов формата для каждого объек-
та. Например, если вы ввели struct,F5H для вывода структуры со-
держажей целые и действительные числа, то целые будут выведены в
шестнадцатеричном (Н) виде, а действительные в виде чисел с пла-
вающей точкой с пятью значащими цифрами (F5).
2. Если вы ввели более обного спецификатора формата, то
выбран будет один с наивысшим приоритетом. Это, конечно, имеет
значение только для структур и объединений, а для простых
переменных и массивов простых переменных вы будете обычно
использовать только один спецификатор формата.
- 171,172 -
Таблица 4.2 Приоритет и правила умолчания в
классификации спецификаторов формата
----------------------------------------------------------------
Тип Спецификаторы в порядке По умолчанию
возрастания приоритета
----------------------------------------------------------------
char C S H D S
unsigned char C S H D S
int H D C* S* D
unsigned int H D C* S* D
long H D C* S* D
unsigned long H D C* S* D
char ptr C S P H S
other ptr P H P
enum H D C* S* D(следует
за именем)
float Fn F7
double Fn F15
long double Fn F18
array of char C S H D S
другие массивы элементы заключаются в квадратные скобки ([])
и разделяются запятыми
structure R
* символ типа спецификаторов формата допустим только для величины
в интервале (-128 до 127 для типа со знаком (signed), и 0 до 255
для типа без знака (unsigned)).
-----------------------------------------------------------------
Замечание: Спецификатор формата Н, используемый для вывода
переменной типа указатель, выводится как шестнад-
цатиричное целое число.
Продемонстрируем использование спецификаторов формата, пола-
гая, что следующие структуры и переменные были объявлены:
struct {
int account;
char name[10];
} client = { 5000, "ДЖОН" }
- 173,174 -
int list[5] = {0,10,20,30,40};
char *ptr = list;
void main()
{
}
После ввода следующих выражений в поле вычислений
(Evaluate), будет производиться пересылка вычисленных значений в
поле результатов (Result).
-----------------------------------------------------------
Evaluate Result
-----------------------------------------------------------
list { 0, 10, 20, 30, 40 }
list[2],3 20, 30, 40
list[2],3x 0x14, 0x1E, 0x28
list,m 00 00 0A 00 00 14 00 1E 00 28 00
ptr DS:0198
ptr,p DS:0198 [_list]
*ptr,3 0, 10, 20
client { 5000, "ДЖОН\0\0\0\0\0" }
client,r { account:5000, name: "ДЖОН\0\0\0\0\0"}
-----------------------------------------------------------
- 175,176 -
Упражнение 2: поиск ошибки в wordlen
-----------------------------------------------------------------
Сейчас давайте вырнемся к WORDCNT и попробуем определить
причину неправильной работы функции wordlen.
Если вы все еще продолжаете сеанс отладки, начатый в первом
уроке, произведите отмену, набрав Run/Program Reset (или
Ctrl-F2). По этой команде Турбо Си освободит память, занимаемую
WORDCNT, закроет открытый файл и завершит выполнение WORDCNT.
С другой стороны, если вы вышли из интегрированной среды или
использовали ее для выполнения других программ, подобно тому, как
мы это делали в первом уроке, то начните снова, выберите файл
WORDCNT.PRJ. Затем установите контрольную точку в строке
содержащей оператор while (fgets(...
Теперь выберите Run/Run и начните отладку. Турбо Си
подготовит WORDCNT к выполнению и начнет ее выполнять до тех пор
пока не встретит контрольную точку.
На запрос имени файла, введите WORDCNT.DAT и нажмите Ввод.
Выберите Run/Step Over, выполните WORDCNT до оператора
вызова wordlen. Затем выберите Run/Trace Into (или F7), чтобы
войти в функцию wordlen раньше чем выполнится оператор ее вызова.
Run/Trace Into пошагово выполнит программу, подобно Run/Step
Over, но вместо вызова функций осуществляет вход в них. В нашем
случае, шкала выполнения переместится в wordlen.
Сейчас важно понять логику работы wordlen. Функции передает-
ся один параметр (указатель), который указывает на слово в буфере
строки и называется wordptr. Значение wordptr присваивается ло-
кальной переменной wordlimit. Затем начинается цикл while до тех
пор, пока не встретится пробел (конец слова) или null-символ (ко-
нец строки). И наконец происходит возврат разницы между wordlimit
и wordptr, как длины слова.
Выполните две строки wordlen, описанные через оператор прис-
воения. Для этого вы можете воспользоваться Run/Trace Into или
Run/Step Over. Поскольку wordlen не вызывает никаких функций, то
эти две команды будут работать одинаково.
Оцените строку, содержащую wordlimit и присваивающую указа-
- 177,178 -
тель. Выполните оператор while. Маркер выполнения должен перемес-
титься на следующую строку, в которой происходит увеличение
wordlimit. Вместо этого он перемещается на строку дальше, на опе-
ратор return. Это может быть ошибка, которую мы ищем.
Замечание: вы вероятно захотите дважды нажать Esc, для того,
чтобы вернуться в редактор Турбо Си. Вместо этого можно
воспользоваться F10.
Обсудим, что происходит дальше. Поскольку wordlimit не уве-
личивается, то разница между wordptr и wordlimit равна 0, и
wordlen тоже возвратит 0. Это именно та ошибка, которую мы иска-
ли. Мы сузили область ошибки с "ошибка в wordlen" до "ошибка в
операторе while функции wordlen". Раз так, то нужно внимательно
посмотреть на текст программы и сделать вывод, что же неверно.
Мы можем вычислить выражение, стоящее в операторе while по
частям и посмотреть, как они работают вместе. Величина *wordlimit
равна Т кода ASCII. Мы не можем вычислить *wordlimit != SPACE,
так как SPACE символ определенный с помощью #define; но мы можем
вычмслить *wordlimit != ' ' (SPACE это ' '), и его величина равна
1 (истина). Значением выражения в целом должна быть истина, но
оно ложно. Значит все дело в операторе &.
В данном случае оператор & применен неверно. В Си он
определен как оператор поразрядного И, то есть операция И
применяется к каждому биту одного операнда с соответствующим ему
битом другого операнда. Так как *wordlimit != SPACE всегда равен
0 или 1 , *wordlimit & *wordlimit != SPACE равно 0 всякий раз. И
здесь необходимо применить оператор &&, результатом которого
является 1, если оба операнда ненулевые. (Попробуйте вычислить
все выражение сначала с одним & и затем с && и сравните
результат).
Путаница, возникающая & с && и ! с !!, является одной из
самых общих ошибок, которые допускают новички программирующие на
Си. После того, как вы нашли ошибку в вашей программе, и в
дальнейшем подобные ошибки вы легко узнаете.
- 179,180 -
Устранение ошибки
-----------------------------------------------------------------
Для того, чтобы исправить ошибку, вам нужно всего лишь
изменить & на &&. Сохраните исправленную программу в файле (наж-
мите F2), для защиты выполненных изменений, в случае случайного
разрушения программы во время последующего сеанса отладки. Затем
выполните Run /Run снова. Так, как вы модифицировали исходный
текст, вам будет сделан запрос на пересоздание программы. Нажмите
Y и программа будет перекомпилирована и перекомпонована. Теперь
возможна дальнейшая отладка откорректированной программы.
Прежде чем приступить к дальшему поиску ошибок дадим корот-
кое резюме нашим действиям, закрывая взгляд на встретившиеся воз-
можности отладчика.
Что вы достигли
-----------------------------------------------------------------
Вы завершили первый сеанс отладки с помощью Run/Program
Reset (или Ctrl-F2). Затем вы выполнили WORDCNT до вызова функции
wordlen с помощью установленной контрольной точки.
Потом вы провели трассировку wordlen, войдя в нее с помощью
Run/Trace INFO (или F7), начали ее пошаговое выполнение и нашли
ошибку. Вы исправили ошибку, сохранили исходный текст, и подгото-
вили программу к дальнейшей отладке.
- 181,182 -
Немного о контрольных точках
-----------------------------------------------------------------
Если вы не выходили из Турбо Си после первого урока, то
контрольная точка, установленная вами в строке содержащей опера-
тор while( fgets(... , остается, когда мы стартуем и во второй
раз. Вот почему WORDCNT выполнится до котрольной точки, а не до
конца программы. Как вы видите контрольные точки сохраняются от
одного сеанса к другому. Даже если между ними вы проводили редак-
тирование или преобразование программы. Турбо Си перемещает каж-
дую контрольную точку выше или ниже, держа ее справа оператора.
Контрольные точки сохраняются если вы установили их в прог-
раммном файле и сохранили его. Контрольные точки будут потеряны
только в том случае, когда вы:
# вышли из интегрированной среды;
# уничтожили строку исходного файла, в которой была установ-
лена контрольная точка;
# убрали все контрольные точки с помощью Break/Watch/Clear
All Breakpoints.
Однако Турбо Си может потерять контрольные точки в двух
случаях:
# если вы редактируете файл, содержащий контрольные точки, а
затем потеряли (не сохранили) отредактированную версию
файла. Турбо Си не помнит положение контрольных точек в
оригинальной версии файла и может поместить их в неверные
строки.
Если вы потеряли отредактированную версию исходного файла,
то вам необходимо убрать все контрольные точки
(выберите Break/Watch/Clear All Breakpoints), а затем соз-
дать их вновь. Заметьте, что Break /Watch/Clear All
Breakpoints уничтожает все контрольные точки вашей прог-
раммы, а не только те, которые вы редактировали в исходном
файле.
# если вы редактируете файл и продолжаете сеанс, не выполнив
перередактирование и компиляцию. Контрольные точки стоят
на своих местах, но так как исходный файл не соответствует
- 183,184 -
выполняемой программе, то индикаторы контрольных точек
указывают не на те строки. (Маркер выполнения также нахо-
дится на неверной строке).
Вы не можете попасть в такую ситуацию случайно потому, что
Турбо Си выдает запрос "Source modified, rebuild (Исходный
текст модифицирован, перестроить)?", когда вы пытаетесь
продолжить или начать сеанс вновь.
Перед компиляцией вы можете установить контрольные точки в
строках, содержащих невыполняемые операторы, таких как коммента-
рии или пустые строки. В этом случае Турбо Си сообщит вам, что
контрольные точки установлены в строках, содержащих невыполняемый
код. После компиляции файла Турбо Си становятся известны строки,
содержащие выполняемые операторы и вы будете об этом предупрежде-
ны, если попытаетесь установить контрольную точку в этих строках.
Переместить курсор к следующей контрольной точке можно с по-
мощью выбора Break/View Next Breakpoints. Заметьте, что по этой
команде курсор перемещается к следующей контрольной точке в текс-
те программы, а не к той, которая бы выполнилась, если бы прог-
рамма работала. Для просмотра контрольных точек программы можно
испоьзовать Break/Watch/Next Breakpoints, это полезно, когда вы
хотите уничтожить только часть контрольных точек.
- 185,186 -
Упражнение 3: возвращение к программе
-----------------------------------------------------------------
Давайте проверим, исправили ли вы ошибку.
Если вы вышли из интегрированой среды или решали другие
программы после упражнения 2, запустите ее снова и создайте файл
WORDCNT.PRJ с текущим проектом. Выберите Run/Run и начните новый
сеанс отладки. Выполните WORDCNT сначала до вызова wordlen, трас-
сируя ее шаг за шагом. На этот раз она работает корректно и зна-
чение, возвращаемое функцией wordlen, равно 2. Это успех!
Однако ваша работа не завершена. Большая часть программы еще
не проверена и может иметь другие ошибки. На следующем этапе вы
должны провести тестирование внутреннего цикла while в main. Бо-
лее полный подход к тестированию включает в себя пошаговое выпол-
нение main до тех пор, пока не будет отработана первая введенная
строка, и проверку на выход из цикла. Проверьте, чтобы каждый шаг
правильно выполнялся и выход из цикла выполнялся в нужной точке.
Но контроль всех выражений - большая работа. Окно Watch от-
ладчика позволяет это сделать довольно легко. В окне Watch выво-
дится одно или более выражений и их текущие значения. Во время
каждого останова отладчик производит пересчет каждого выражения в
окне Watch. Таким образом вы можете наблюдать изменение значений
выражений во время решения вашей программы.
Создайте выражения для всех элементов данных, входящих в
внутренний цикл while (переменная i, строка, начинающаяся с
wordptr и несколько первых элементов wordlencnt).
Окно Watch обычно размещается в нижней части экрана, там же,
где находится окно Message во время компиляции и редактирования.
Оно инициализирует одну яркую пустую строку. Если окно Watch не-
видимо, то это потому, что окно Edit расширено на весь экран;
нажмите F5 и отладчик вернется в полиэкранный режим и окно Watch
восстановится.
Выберите Break/Watch/Add Watch (или комбинацию Ctrl-F7). От-
ладчик откроет окно и выдаст запрос на ввод выражения. Подобно
Debug/Evaluate, Break/Watch/Add Watch использует по умолчанию вы-
ражение, у которого стоит курсор в окне Edit. Если выражение в
окне не i, введите i и нажмите Ввод. Отладчик вычислит текущее
значение i и выведет его в окне Watch.
- 187,188 -
Повторите эту процедуру для пяти выражений: wordptr и
wordlencnt[1]...wordlencnt[4]. После ввода каждого выражения окно
Watch будет вырастать и приспосабливаться к нему. Теперь пошагово
выполните цикл while WORDCNT до тех пор, пока не будет обработана
первая введенная строка. Как только начнете, вы сможете наблюдать
продвижение вперед wordptr и увеличение соответствующего элемента
wordlencnt. Остановитесь, когда wordptr достигнет конца строки и
маркер выполнения выйдет из внутреннего цикла while. Цикл работа-
ет правильно.
Редактирование и уничтожение выражений
--------------------------------------
Вы можете редактировать и уничтожать выражения в окне Watch,
также как и добавлять их.
Для того, чтобы отредактировать или уничтожить выражения -
активизируйте окно Watch. Если вы находитесь в меню системы, то
нажмите F10 и выйдите из него. Нажмите F6, чтобы переключиться с
окна Edit на окно Watch. Редактор высветит выражение, которое вы
можете изменить или уничтожить; перемещение поля выполняется с
помощью клавиш управления курсором.
Попробуйте изменить выражение. Для этого переместите поле на
wordlencnt[4] и выберите Break/Watch/Edit Watch (или Ввод). От-
ладчик откроет окно, содержащее выражение и запрос на его редак-
тирование.
Измените индекс массива с 4 на 6 и нажмите Ввод. Отладчик
изменит выражение в окне WINDOW и выведет его новое значение.
Переместите поле на выражение wordlencnt[3] и выберите Break
- 189,190 -
/WATCN/Delete Watch (или Del, или Ctrl-Y). Отладчик уничтожит вы-
ражение.
Нажмите F6 для активизации окна Edit. Заметьте, что когда
окно Watch разактивизируется, перед выражением, помеченным повы-
шенной яркостью, появляется символ "бриллиант формы" (код F0).
Для того, чтобы активизировать окно Watch, нажмите F6.
Вы можете уничтожить все выражения с помощью
Break/Watch/Remove All Watches. (Для этого окно Watch должно быть
активно). Сейчас уничтожьте все видимые выражения. Окно Watch
возвратится в свое исходное состояние. Для активизации окна Edit
нажмите F6. С помощью этой команды вы можете переключаться с окна
Edit на окно Watch, или наоборот.
Расширение и переключение окон
------------------------------
Правила расширения и переключения окон в отладчике расширены
по сравнению с правилами, которые вы уже изучили при работе с
редактором, компилятором и компановщиком.
Работа с окнами Watch и Edit выполняется в полиэкранном ре-
жиме, подобно тому, как это делается при работе с окнами Edit и
Message при компиляции и редактировании.
Для того, чтобы произвести переключение между двумя окнами -
Edit и Message или Edit и Watch, - нажмите F6.
Чтобы расширить окно до полного экрана - нажмите F8. Для
возврата в полиэкранный режим нажмите F5. Попробуйте это сделать
с окнами Edit и Watch.
Для вывода экрана выполнения выберите Run/User Screen или
Alt-F5. Попробуйте это.
Используйте Alt-F6 для изменения содержимого окна:
- 191,192 -
# когда активно окно Edit, загруженный файл перезагружается
# если активно окно Watch или Message, то нажатие Alt-F6 яв-
ляется переключателем между окном Watch и окном Message
tracking.
Прокрутка выражений в окне Watch
---------------------------------
При добавлении выражений в окно Watch может возникнуть ситу-
ация, когда окно займет около половины экрана. И при добавлении
выражений в этом случае возникает прокрутка содержимого окна. Уп-
равление содержимым окна выполняется с помощью клавиш PgUp, PgDn,
стрелка вверх и стрелка вниз. Если выражение очень длинное и не
помещается в окно, то вы можете воспользоваться горизонтальной
прокруткой с помощью ключей Home, End, стрелка влево и стрелка
вправо (этим средством можно пользоваться и при просмотре длинных
значений выражения).
Упражнение 4: отладка цикла печати
-----------------------------------------------------------------
Вы отладили цикл while, который считывает и обрабатывает
входной файл. В этом цикле есть несколько мест, где вас могут
ожидать ошибки, и для того, чтобы их найти вам нужно чтобы на
входе WORDCNT чувствовал специфичные типы, например, такие, как
- 193,194 -
пустые строки. Однако, мы отложим поиск таких ошибок и приступим
к проверке цикла for, который выводит результаты.
Вам необходимо выполнить WORDCNT до строки, содержащей цикл
for (приблизительно 117 строка). Как вам известно, можно
переместить курсор в эту строку, установить контрольную точку и
выбрать опцию Run/Run. Но сейчас мы будем использовать более
удобную технику, если вы предполагаете останавливаться только в
определенных точках.
Переместите курсор к строке, в которой начинается цикл for,
и выберите Run/Go to Cursor (или F4). Отладчик выполнит WORDCNT и
остановится в той строке, где находится курсор. (Останов может
произойти раньше в контрольной точке, но в этой части программы
они не устанавливались).
Выполните один оператор, шкала выполнения переместится на
оператор printf цикла for. Заметим, что в цикле for нет контроль-
ной точки. Run/Go to Cursor является одноразовой операцией; она
не устанавливает контрольную точку.
Начните пошаговое выполнение цикла for с помощью Run/Trace
Into. Обратите внимание, что Run/Trace Into трассировку функции
printf не выполняет. Это происходит потому, что функция printf не
определена в исходном файле. Отладчик может только выполнять та-
кие функции, но не может их трассировать. (В этом случае нет дру-
гого выхода, трассировка этой функции невозможна: исходного текс-
та функции printf в вашем файле нет).
Каждый раз, когда вы выполняете строку, содержащую вызов
printf, отладчик открывает экран выполнения (Execution). На него
будет выводиться текст, выводимый printf. Отладчик сохранит экран
выполнения до тех пор, пока будет выполняться оператор, содержа-
щий функцию printf.
Вы вероятно не сможете прочитать вывод программы в столь ко-
роткий промежуток времени, пока экран выполнения виден, но с по-
мощью Run/User Screen (или Alt-F5) можно перейти в режим экрана
пользователя и наблюдать работу WORDCNT.
Замечание: С другой стороны, если заметить, что вывод прог-
раммы переписывается в ваш исходный текст в окно Edit, то вы мо-
жете избавиться от этого, выбрав Debug/Refresh Display для реге-
нерации экрана. Затем проконтролируйте, чтобы опция Debug/Display
- 195,196 -
Swapping была установлена в состояние Smart или Always.
(Более полная информация об этой опции рассматривается в
Главе 5).
На этом четвертое упражнение заканчивается. Мы покидаем вас,
а вы можете поискать ошибки в цикле for, и если их обнаружите, то
исправьте.
Замечание: Мы обещали, что WORDCNT содержит несколько оши-
бок. Вы нашли их? Каждая из них упоминается в разделе "Алгоритм
для эффективного тестирования программного обеспечения", приве-
денного в конце этой главы.
Упражнение 5: работа с большими программами
-----------------------------------------------------------------
Отладчик имеет несколько дополнительных возможностей, помо-
гающих вам при работе с большими исходными текстами и многофайло-
выми программами. В следующем разделе мы продемонстрируем их. Для
того, чтобы использовать одну из них, вы должны компилировать ва-
шу программу с включенной опцией Options/Compiler/Code Generation
/Standard Stack Frame. Проверьте эту опцию. Если она выключена,
то включите ее, затем убедитесь, что WORDCNT загружен в окно Edit
и перекомпилируйте, нажав Alt-F9. Начните сеанс отладки с помощью
Run/Step Over или, нажав F8.
Поиск определенной функции
---------------------------
Debug/Find Function выполняет прокрутку описаний функций в
окне Edit. Она поможет найти некоторую функцию в программе, кото-
рую можно откомпилировать с флагом Debug/Source Debugging и O/O/C
/OBJ Debugging Information в состояние On.
- 197,198 -
Debug/Find Function полезна в том случае, если введенная ва-
ми ошибка в одной части программы, должна быть исправлена с по-
мощью изменений в других частях.
Для проверки этой команды, переместите курсор в
небезизвестную вам строку, содержащую вызов wordlen. Выберите
Run/Go to Cursor. Затем переместите курсор на wordlen и выберите
Debug/Find Function. Отладчик откроет окно и выдаст приглашение
для ввода имени функции; так как wordlen это функция, которую мы
хотели найти, то вводите ее имя, и нажмите Ввод. Отладчик выведет
описание функции в окне Edit.
Стек вызова
-----------
Когда вы отлаживаете программу, содержащую вызовы функций
различных уровней, вам иногда будет нужно просмотреть стек вызо-
ва, который содержит имена вызывавшихся функций в порядке их вы-
полнения. Для этой возможности необходимо включить опцию Options/
Compiler/Code Generation/Standard Stack Frame.
Войдите в wordlen, затем выберите Debug/Call Stack (или Ctrl
-F3). Отладчик выведет стек вызова во всплывающем окне. Функция,
выполняемая в данный момент, будет находиться в вершине стека, а
на дне стека будет main. Заметьте, что в стек выводятся не только
имена функций, но и значения их параметров.
Для того, чтобы посмотреть, какая строка выполняется, вам
необходимо переместить светящееся поле в окне стека на имя функ-
ции и нажать Ввод. Например, для того, чтобы узнать, какая строка
main выполняется в данный момент, переместите поле в окне стека
на main и нажмите Ввод. Отладчик прокрутит часть main и выведет
строку, содержащую вызов wordlen в окне Edit. Если имеются допол-
нительные точки входа стека вызова, то вы можете вывести выполня-
ющуюся строку любой другой функции, выбрав Debug/Call Stack снова
и другую точку входа стека.
Возврат в позицию выполнения
----------------------------
- 199,200 -
Вы можете также использовать опцию Debug/Call Stack для
возврата к маркеру выполнения после просмотра других частей прог-
раммы. Для этого необходимо выбрать самую верхнюю функцию стека
вызова. Попробуйте.
На этом упражнения с отладчиком заканчиваются.
О многофайловых исходных текстах
-----------------------------------------------------------------
Все команды отладчика работают с программами, состоящими из
нескольких файлов. Например, если вы выбрали Debug/Find Function
для того, чтобы найти функцию, которая описана в другом файле,
отладчик загрузит трубуемый файл в окно Edit. Если захотите сде-
лать изменения текущего файла, отладчик спросит хотите ли вы сох-
ранить файл, который был загружен предыдущим.
Подобно этому, когда вы выбираете функцию из стека вызова
(используя Debug/Call Stack), отладчик перезагружает окно Edit.
Точно так же отладчик дает запрос на сохранение. Если программа
состоит из большого количества файлов, то одновременно отлаживать
лучше только несколько.
- 201,202 -
Обзор команд отладчика и зарезервированных клавиш
-----------------------------------------------------------------
Это введение представляет вам наиболее часто используемые
команды интегрированного отладчика. Когда вы приобретете некото-
рое мастерство при работе с отладчиком, большинство этих команд
вы запомните. Наиболее часто используемые команды приведены в
таблицах 4.3 и 4.4.
Многие команды отладчика и команды меню могут быть выполнены
с помощью зарезервированных клавиш или их комбинаций. Для того,
чтобы избежать беспорядка, мы укажем только самые важные из них.
В таблице 4.3 приведены все зарезервированные клавиши команд от-
ладчика, которые вы изучили.
Табл. 4.3: Команды отладки и клавиши быстрого вмешательства.
-----------------------------------------------------------------
Клавиша Команда меню Описание
-----------------------------------------------------------------
F4 Run/Go to Cursor Выполнение программы продолжа-
ется до строки с курсором. Бу-
дет инициирован сеанс отладки.
Ctrl-F2 Run/Program Reset Прекращает текущий сеанс отлад-
ки, освобождает распределенную
память и закрывает файл. Имеет
действие только в сеансе отлад-
ки.
F7 Run/Trace Into Выполняет следующий оператор
текущей функции. Будучи вызван-
ной и, если самый нижний уро-
вень функции был откомпилирован
с опциями O/C/C/OBJ Debug
Information и Debug/Sourse
Debudding в состоянии On, осу-
- 203,204 -
ществляет трассировку внутри
функции. Будет инициирован се-
анс отладки.
F8 Run/Step Over Выполняет следующий оператор
текущей функции. Не выполняется
трассирование внутри вызываемой
функции. Будет инициирован се-
анс отладки.
O/C/C/Standard Опция Options/Compiler/Code
Stack Frame Generation/Standard Stack Frame
Если присутствует Debug/Call
Stack, то для правильной работы
программы при компилировании
эта опция должна быть установ-
лена в состояние On.
O/C/C/OBJ Debug Опция O/C/C/Debug Information.
Information Только файлы откомпилированные
и скомпанованные с этой опцией
в состоянии On, могут быть от-
лажены.
Ctrl-F4 Debug/Evaluate Вычисляет выражение; позволяет
вам изменить значение перемен-
ной.
Debug/Find Function Наxодит определение функции и
отображает его в окне редакти-
рования. Имеет действие только
в сеансе отладки.
Ctrl-F3 Debug/Call Stack Отображает вызов стека. Вы мо-
жете вывести на дисплей текущую
выполняемую строку функции,
указав имя функции из вызова
стека. Имеет действие только в
сеансе отладки.
Debug/Source Debugging Проверяет разрешена ли отладка.
При установленном значении On -
процесс отладки разрешен. Когда
установлено значение None, ин-
- 205,206 -
формация отладчика в файле .EXE
будет отсутствовать, и, поэто-
му, программа не сможет быть
отлажена другим отладчиком.
Ctrl-F7 Break/Watch/Add Watch Добавляет наблюдаемое выраже-
ние.
Break/Watch/Delete Удаляет наблюдаемое выражение.
Watch
Break/Watch/Edit Watch Позволяет вам редактировать
наблюдаемое выражение.
Break/Watch/Remove Удаляет все наблюдаемые выраже-
All Watches ния.
Ctrl-F8 Break/Watch/Toggle Устанавливает или удаляет точку
Breakpoint останова в строке, в которой
расположен курсор.
Break/Watch/Clear Удаляет в программе все точки
Breakpoints останова.
Break/Watch/View Выводит на дисплей следующую
Next Breakpoint точку останова.
-----------------------------------------------------------------
В таблице 4.4 приведены другие команды меню, часто использу-
емые в интегрированном отладчике. (Для изучения остальных заре-
зервированных клавиш и команд меню смотрите Главу 5).
Табл. 4.4: Команды меню и "горячие" клавиши отладчика.
-----------------------------------------------------------------
Клавиша Команда меню Описание
-----------------------------------------------------------------
F5 Увеличивает до полного экрана и
уменьшает активное окно.
Alt-F5 Переключает дисплей в пользова-
тельский экран. Нажатие любой
клавиши приведет к возврату в ин-
тегрированную среду.
- 207,208 -
F6 Переключает активное окно между
окном редактирования и окном наб-
людения или сообщений.
Alt-F6 Если окно редактора активно - пе-
реключает в файл, который был
загружен последним. Если активно
нижнее окно - переключает между
окном наблюдения и сообщений.
Ctrl-F9 Run/Run Запускает программу на выполнение
с или без отладчика. Компилирует
исxодный файл (файлы) и компонует
программу, если это необxодимо.
Запускает программу на выполнение
до точки останова или до конца,
если компиляция и компоновка
программы были выполнены с Debug/
Source Debugging и O/C/C/OBJ
Debug Informatoin в положении On.
Project/Remove Удаляет содержимое окна сообще-
Messages ний.
Советы для эффективного тестирования программ.
-----------------------------------------------------------------
Методов отладки больше, чем знаний как использовать
отладчик. Объяснение поведения программы - одна из наиболее
ответственных фаз в программировании.
Остаток этой главы посвящен технике программирования, кото-
рая облегчит отладку.
Развитие стандартного подхода
-----------------------------------------------------------------
Развитый стандартный подход к тестированию программного
обеспечения заключается в серии шагов, которые в результате
эксперимента позволят вам судить о надежности программы.
Для тестирования не выбирайте только один правильный путь;
список шагов будет зависеть от типа написанной программы, вашей
квалификации как программиста и вашего индивидуального стиля.
- 209,210 -
Список шагов, приведенных ниже, может вам служить в качестве
отправной точки.
# Делайте вход программы простым, но не тривиальным. Трасси-
ровку проводите используя Debug/Evaluate и почаще контро-
лируйте значение элементов данных. Одновременно находите и
исправляйте несколько ошибок.
# Подавайте на вход различные наборы данных, чтобы проверить
те части программы, которые вы не смогли оттестировать на
предыдущем шаге.
# Проверьте каждый оператор программы. Вы можете найти ошиб-
ки там, где меньше всего ожидаете. В WORDCNT, например,
проверяя каждый оператор, обнаружили случайную ошибку с
запятой после else, которая создает нежелательный эффект,
когда программа встречает слово длиной больше чем
MAXWORDLEN. (Точка с запятой обычно находится за правым
краем окна Edit и маловероятно, что вы ее заметите в дру-
гих случаях).
# Будьте осторожны с операторами или выражениями, которые не
могут быть проверены за один раз, подобных этому:
if (strcmp( a, b ) )...
strcmp может вернуть три значения - 0 (a равно b), -1 (a
иеньше b) или 1 (a больше b). Это предположение делает не-
обходимым проверить оператор с тремя наборами входных ве-
личин и в каждом случае оператор должен работать правиль-
но.
x = (x>0) ? fun(x) : 0;
Этот оператор содержит условное выражение, которое может
порождать два результата.
# Обратите особое внимание на граничные условия: условия при
которых программа выходит из цикла, заполняет массив и
т.д. Ошибки, в особенности подобные failures to handle,
часто вызваны нарушением граничных условий.
WORDCNT содержит два примера ошибок, связанных с граничны-
ми условиями. Во-первых, цикл for не выведет элемент
- 211,212 -
wordlencnt, который соответствует словам длиной в
MAXWORDLEN символов. Во-вторых, wordlencnt размещенный для
одного элемента будет слишком мал, и элемент будет не дос-
тупен.
# Полностью проверьте программу без отладчика. Если програм-
му будут использовать другие люди, они будут предполагать,
что она работает правильно, поэтому проверьте ее реакцию
на каждый тип ошибки, возможных при обработке. О програм-
ме, обрабатывающей большинство типов оишбок, говорят как
об ошибкоустойчивой.
# Если вашей программой будут пользоваться другие люди, то
они должны иметь по крайней мере один индивидуальный тест.
Выберите кого-нибудь, кто является типичным представителем
пользователей вашей программы, который с упорством и энту-
зиазмом будет искать ошибки и сможет правильно сообщить о
них. Не выбирайте программиста, если только ваша программа
не предназначена для программистов.
Тщательно проверяйте изменения
-----------------------------------------------------------------
Когда вы изменили программу, тщательно оттестируйте все зат-
ронутые части программы. Необходимо также проверить работу тех
частей программы, которые не изменились, но связаны с этим изме-
нением. Если программа сложная, то сохраните запись тестов. Когда
вы будете модифицировать программу, эта запись поможет повторить
вам все тесты связанные с изменением. Если тест включает входные
файлы - сохраните эти файлы.
- 213,214 -
Как избежать некоторых трудностей
-----------------------------------------------------------------
Пишите ясные программы с последовательным отступом вправо,
отражающим вложенность операторов, с подробными комментариями и
описаниями имен переменных.
Сохраните текст вашей программы простым. Старайтесь выражать
запутанные операции с помощью нескольких простых операторов, а не
сбивать их в одну кучу. Турбо Си прооптимизирует код с максималь-
ной эффективностью, в свою очередь, исходный текст должен легко
отлаживаться, читаться и модифицироваться.
Старайтесь строить программу из функций с простыми и хорошо
определенными целями. Это облегчит создание тестов и анализ их
результатов. Это также сделает программу легко читаемой и модифи-
цируемой.
Пытайтесь минимизировать число элементов данных требующихся
каждой функции и число элементов, которые она изменяет. Это то же
облегчит создание тестов и анализ их результатов. Такой подход
ограничивает количество функций с ошибками и позволяет проверить
функцию несколько раз за один сеанс отладки. Программа, разрабо-
танная таким образом, является практически независимой.
При написании программы, не экономьте биты. Обычно такая
экономия приводит к усложнению программы, в ней тяжело разобрать-
ся и сложно отлаживать. Если ваша программа медленно выполняется,
то найдите какая часть программы нуждается в улучшении и решите
как это лучше сделать.
Будьте осторожны при написании функций, которые используются
в программе в нескольких местах или могут использоваться другими
программами. Написание и отладка одной обобщенной функции прохо-
дит обычно легче чем нескольких специализированных.
- 215,216 -
Отладка снизу вверх
-----------------------------------------------------------------
На сколько возможно сосредоточьтесь на отладке функций
нижнего уровня (это функции, которые не вызывают другие функции).
Затем принимайтесь за отладку функций более верхнего уровня и
т.д. по направлению к main. В этом случае, вы будете иметь
фундамент из нужных функций, и эти функции вы можете выполнить за
один шаг, когда они вызываются в других частях программы.
Исправление подобных ошибок
-----------------------------------------------------------------
Когда вы нашли ошибку, проконтролируйте наличие подобной
ошибки в остальных частях программы. Например, если вы нашли, что
вызов функции выглядит следующим образом:
fp = fopen("rb",filename);
который должен выглядеть
fp = fopen(filename,"rb");
то исправьте ошибку и проверьте вызовы fopen и других простых
функций с целью обнаружения подобных ошибок.
- 217,218 -
Отладка встроенного кода ассемблера
-----------------------------------------------------------------
Если вы используете ассемблер ТАСМ фирмы Borland, совместно
с интегрированным окружением, то можете шаг за шагом проверить
код на уровне ассемблера, без использования внешнего отладчика.
(Для полной поддержки отладки на уровне ассемблера вы можете, ко-
нечно, использовать и автономный отладчик фирмы Borland.)
Когда модуль, написанный на ассемблере, проассемблирован с
опцией TASM -zi, Турбо Си может распознавать исходные строки и
идентификаторы ассемблера. Если же вы вышли на уровень функций
ассемблера, то Турбо Си будет показывать вам на экране исходный
текст ассемблера для этой функции. Для вашего исходного текста на
языке ассемблера вы можете использовать обычные команды отладчика
TC, такие как Debug/Go to cursor (F4), Debug/Trace Into (F7) и
Debug/Step Over (F8).
Большинство идентификаторов, определенных в вашем исходном
тексте на ассемблере, будет доступно для использовании в оценке
значений выражений и их отображения. Кроме того, вы имеете доступ
к псевдорегистрам (_AX, _BX, и т.д.) и специальной переменной
_FLAGS, отражающей состояние регистра флагов центрального процес-
сора. Конечно, с TC и средой разработки Си, вы не распознаете
каждую конструкцию или выражение ассемблера.
- 219,220 -