[Содержание]   [Назад]   [Пред]   [Вверх]   [След]   [Вперед]  


1. Пример сеанса DDD

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

Программа-пример `sample.c' (см. раздел 1.1 Пример программы) обнаруживает следующую ошибку. Программа sample должна сортировать и печатать свои аргументы в виде чисел, как в этом примере: $ ./sample 8 7 5 4 1 3 1 3 4 5 7 8

Однако, при некоторых значениях аргументов она ошибается: $ ./sample 8000 7000 5000 1000 4000 1000 1913 4000 5000 7000

Хотя вывод отсортирован и содержит верное число аргументов, некоторые аргументы пропущены и заменены на странные числа; в данном случае пропущено 8000, а вместо него стоит 1913.(3)

Давайте применим DDD, чтобы увидеть, что происходит. Сначала вы должны скомпилировать `sample.c' для отладки (см. раздел 4.1 Компиляция для отладки), задав при компиляции флаг -g: $ gcc -g -o sample sample.c

Теперь вы можете вызвать DDD (см. раздел 2.1 Вызов DDD) для исполняемого файла sample: $ ddd sample

Через несколько секунд появляется DDD. Окно с исходным текстом содержит код отлаживаемой программы; для прокрутки по файлу используйте полоску прокрутки.

PICS/tut-invoke

Консоль отладчика (в самом низу) содержит информацию о версии DDD, а также подсказку GDB.(4) GNU DDD Version 3.2.1, by Dorothea L@"utkehaus and Andreas Zeller. Copyright (C) 1999 Technische Universit@"at Braunschweig, Germany. Copyright (C) 1999 Universit@"at Passau, Germany. Reading symbols from sample...done. (gdb)

Первое, что нужно сейчас сделать -- установить точку останова (см. раздел 5.1 Точки останова), что заставит sample остановиться в интересующем вас месте. Щелкните на пустом месте слева от инициализации a. Поле аргумента `():' теперь содержит позицию (`sample.c:31'). Теперь щелкните на `Break', чтобы создать точку останова в позиции `()'. Вы увидите, что на строке 31 появился маленький знак "стоп".

Следующее, что нужно сделать, -- действительно запустить программу, чтобы вы могли исследовать ее поведение (см. раздел 6. Запуск программы). Для запуска программы выберите `Program => Run'; появится диалоговое окно `Run Program'.

PICS/tut-run

Теперь вы можете ввести в поле `Run with Arguments' аргументы программы sample. Введите здесь аргументы, приводящие к ошибочному поведению -- то есть, `8000 7000 5000 1000 4000'. Щелкните на `Run', чтобы началось выполнение с заданными вами аргументами.

Теперь GDB запускает sample. Через несколько мгновений, когда достигнута точка останова, выполнение останавливается. Об этом сообщается в консоли отладчика. (gdb) break sample.c:31 Breakpoint 1 at 0x8048666: file sample.c, line 31. (gdb) run 8000 7000 5000 1000 4000 Starting program: sample 8000 7000 5000 1000 4000 Breakpoint 1, main (argc=6, argv=0xbffff918) at sample.c:31 (gdb)

Выполняемая в текущий момент строка обозначена зеленой стрелкой. => a = (int *)malloc((argc - 1) * sizeof(int));

Сейчас вы можете проверить значения переменных. Чтобы проверить простую переменную, вы можете просто поместить указатель мыши над ее именем и задержать его там. Спустя секунду всплывет маленькое окно со значением этой переменной (см. раздел 7.1 Просмотр простых значений с помощью подсказок). Попробуйте проделать это с `argv', чтобы увидеть ее значение (6). Локальная переменная `a' пока не проинициализирована; вы, вероятно, увидите 0x0 или какое-то другое значение неверного указателя.

Чтобы выполнить текущую строку, щелкните на кнопке `Next' из командной панели. Стрелка продвинется на следующую строку. Теперь снова укажите на `a' и увидите, что значение изменилось, и переменная `a' действительно стала инициализированной.

PICS/tut-value

Чтобы исследовать отдельные значения массива `a', введите в поле аргумента `a[0]' (вы можете заранее очистить его, щелкнув на `():'), а затем щелкните на кнопку `Print'. Это напечатает текущее значение `()' в консоли отладчика (см. раздел 7.2 Печать простых значений в консоли отладчика). В нашем случае вы получите (gdb) print a[0] $1 = 0 (gdb)

или какое-то другое значение (заметьте, что `a' была только размещена, но ее содержимое еще не проинициализировано.)

Чтобы увидеть все члены `a' одновременно, вы должны применить особый оператор GDB. Поскольку `a' была размещена динамически, GDB не знает ее размера; вы должны явно указать его, используя оператор `@' (см. раздел 7.3.2.1 Фрагменты массива). Введите в поле аргумента `a[0]@(argc - 1)' и щелкните кнопку `Print'. Вы получите первые argc - 1 элементов `a', или (gdb) print a[0]@(argc - 1) $2 = {0, 0, 0, 0, 0} (gdb)

Вместо того чтобы использовать `Print' для просмотра текущего значения `a' на каждом останове, вы можете также отобразить `a', то есть сделать так, чтобы ее значение показывалось автоматически. Щелкните на `Display' оставив в поле аргумента `a[0]@(argc - 1)'. Содержимое `a' теперь показывается в другом окне, окне данных. Для горизонтального поворота массива щелкните на `Rotate'.

PICS/tut-display

Далее идет присваивание значений членам `a': => for (i = 0; i < argc - 1; i++) a[i] = atoi(argv[i + 1]);

Теперь вы можете щелкать на `Next' и снова на `Next', чтобы увидеть, как происходит присваивание отдельным членам `a'. Измененные члены подсвечиваются.

Для продолжения выполнения цикла используйте кнопку `Until'. Она велит GDB выполнять программу до тех пор, пока не будет достигнута строка, большая текущей. Щелкайте на `Until', пока не окажетесь на вызове `shell_sort': => shell_sort(a, argc);

В этом месте содержимое `a' должно быть равно `8000 7000 5000 1000 4000'. Снова щелкните на `Next', чтобы пройти через вызов `shell_sort'. DDD остановится на цикле => for (i = 0; i < argc - 1; i++) printf("%d ", a[i]);

и вы увидите, что после окончания `shell_sort' содержимое `a' стало равным `1000, 1913, 4000, 5000, 7000' -- то есть `shell_sort' каким-то образом испортил его.

Чтобы выяснить, что же случилось, выполните программу снова. На этот раз не проходите инициализацию, а перескочите прямо на вызов `shell_sort'. Удалите старую точку останова, выбрав ее и щелкнув на `Clear'. Затем создайте новую точку останова в строке 35, перед вызовом `shell_sort'. Для повторного выполнения программы выберите `Program => Run Again'.

Опять же, DDD остановится перед вызовом `shell_sort': => shell_sort(a, argc);

На этот раз вы хотите ближе исследовать, что делает `shell_sort'. Щелкните на `Step', чтобы войти в вызов `shell_sort'. Это оставит вашу программу на первой исполняемой строке, => int h = 1;

тогда как консоль отладчика говорит нам, что произошел вход в функцию: (gdb) step shell_sort (a=0x8049878, size=6) at sample.c:9 (gdb)

Такой вывод, показывающий функцию (и ее аргументы), где остановлено выполнение `sample', называется отображением стека фреймов. Он дает представление о содержимом стека. Вы можете использовать `Status => Backtrace', чтобы узнать, в каком месте стека вы находитесь; если выбрать строку (или щелкнуть на `Up' или `Down'), вы сможете перемещаться по стеку. Обратите внимание на то, что отображение `a' исчезает, когда вы покидаете его фрейм.

PICS/tut-backtrace

Давайте теперь проверим правильность аргументов `shell_sort'. Вернувшись в самый нижний фрейм, введите в поле аргумента `a[0]@size' и щелкните на `Print': (gdb) print a[0] @ size $4 = {8000, 7000, 5000, 1000, 4000, 1913} (gdb)

Сюрприз! Откуда взялось это лишнее значение 1913? Ответ прост: размер массива, передаваемый в функцию `shell_sort' как `size', больше чем нужно на единицу -- странное число 1913 оказалось в памяти после `a'. И это значение также сортируется.

Чтобы понять, действительно ли это является причиной ошибки, вы можете теперь присвоить `size' правильное значение (см. раздел 7.3.3 Присваивание переменных). Выберите в исходном коде `size' и щелкните на `Set'. Появится диалоговое окно, где вы можете отредактировать значение этой переменной.

PICS/tut-set

Измените значение `size' на 5 и щелкните на `OK'. Затем щелкните на `Finish', чтобы продолжить выполнение функции `shell_sort': (gdb) set variable size = 5 (gdb) finish Run till exit from #0 shell_sort (a=0x8049878, size=5) at sample.c:9 0x80486ed in main (argc=6, argv=0xbffff918) at sample.c:35 (gdb)

Получилось! Для `a' теперь показаны корректные значения `1000, 4000, 5000, 7000, 8000'.

PICS/tut-finish

Вы можете убедиться, что эти значения будут на самом деле напечатаны на стандартный вывод, выполнив программу дальше. Щелкните на `Cont', чтобы продолжить выполнение. (gdb) cont 1000 4000 5000 7000 8000 Program exited normally. (gdb)

Сообщение `Program exited normally.' исходит от GDB; оно говорит, что программа sample завершила выполнение.

Найдя причину ошибки, вы теперь можете исправить исходный код. Щелкните на `Edit', чтобы отредактировать `sample.c', и измените строку shell_sort(a, argc);

на корректный вызов shell_sort(a, argc - 1);

Теперь вы можете перекомпилировать sample $ gcc -g -o sample sample.c

и проверить (через `Program => Run Again'), что sample работает хорошо. (gdb) run `sample' has changed; re-reading symbols. Reading in symbols...done. Starting program: sample 8000 7000 5000 1000 4000 1000 4000 5000 7000 8000 Program exited normally. (gdb)

Все готово; сейчас программа работает правильно. Вы можете завершить этот сеанс DDD с помощью `Program => Exit' или Ctrl+Q.

1.1 Пример программы

Это исходный файл `sample.c' программы-примера.

/* sample.c -- Sample C program to be debugged with DDD */ #include <stdio.h> #include <stdlib.h> static void shell_sort(int a[], int size) { int i, j; int h = 1; do { h = h * 3 + 1; } while (h <= size); do { h /= 3; for (i = h; i < size; i++) { int v = a[i]; for (j = i; j >= h && a[j - h] > v; j -= h) a[j] = a[j - h]; if (i != j) a[j] = v; } } while (h != 1); } int main(int argc, char *argv[]) { int *a; int i; a = (int *)malloc((argc - 1) * sizeof(int)); for (i = 0; i < argc - 1; i++) a[i] = atoi(argv[i + 1]); shell_sort(a, argc); for (i = 0; i < argc - 1; i++) printf("%d ", a[i]); printf("\n"); free(a); return 0; }


[Содержание]   [Назад]   [Пред]   [Вверх]   [След]   [Вперед]