Стоит также распечатать справочную карточку GDB, которая поставляется в дистрибутиве GDB в файле gdb/doc/refcard.tex
. Создать печатную версию справочной карточки для PostScript после извлечения исходника и запуска configure можно с помощью следующих команд:
$ cd gdb/doc /* Перейти о подкаталог doc */
$ make refcard.ps /* Отформатировать справочную карточку */
Предполагается, что справочная карточка будет распечатана с двух сторон листа бумаги 8,5×11 дюймов[168] (размер «letter») в горизонтальном (landscape) формате. В ней на шести колонках предоставлена сводка наиболее полезных команд GDB. Мы рекомендуем распечатать ее и поместить под своей клавиатурой при работе с GDB.
15.4. Программирование для отладки
Имеется множество методик для упрощения отладки исходного кода, от простых до сложных. В данном разделе мы рассмотрим ряд из них.
15.4.1. Код отладки времени компилирования
Несколько методик относятся к самому исходному коду.
15.4.1.1. Использование отладочных макросов
Возможно, простейшей методикой времени компилирования является использование препроцессора для создания условно компилируемого кода. Например:
#ifdef DEBUG
fprintf(stderr, "myvar = %d\n", myvar);
fflush(stderr);
#endif /* DEBUG */
Добавление -DDEBUG
к командной строке компилятора вызывает fprintf()
при выполнении программы.
Рекомендация: сообщения отладки посылайте в stderr
, чтобы они не были потеряны в канале и чтобы их можно было перехватить при помощи перенаправления ввода/вывода. Убедитесь, что использовали fflush()
, чтобы сообщения были выведены как можно скорее
ЗАМЕЧАНИЕ. Идентификатор DEBUG
, хотя он и очевидный, также часто злоупотребляется. Лучшей мыслью является использование специфического для вашей программы идентификатора, такого как MYAPPDEBUG
. Можно даже использовать различные идентификаторы для отладки кода в различных частях программы, таких, как файловый ввод/вывод, верификация данных, управление памятью и т.д.
Разбрасывание больших количеств операторов #ifdef
по всему коду быстро становится утомительным. Большое количество #ifdef
скрывают также логику программы. Должен быть лучший способ, и в самом деле, часто используется методика с условным определением специального макроса для вывода:
/* МЕТОДИКА 1 --- обычно используемая, но не рекомендуемая, см. текст */
/* В заголовочном файле приложения: */ #ifdef MYAPPDEBUG
#define DPRINT0(msg) fprintf(stderr, msg)
#define DPRINT1(msg, v1) fprintf(stderr, msg, v1)
#define DPRINT2(msg, v1, v2) fprintf(stderr, msg, v1, v2)
#define DPRINT3(msg, v1, v2, v3) fprintf(stderr, msg, v1, v2, v3)
#else /* ! MYAPPDEBUG */
#define DPRINT0(msg)
#define DPRINT1(msg, v1)
#define DPRINT2(msg, v1, v2)
#define DPRINT3(msg, v1, v2, v3)
#endif /* ! MYAPPDEBUG */
/* В исходном файле приложения: */
DPRINT1("myvar = %d\n", myvar);
...
DPRINT2("v1 = %d, v2 = %f\n", v1, v2);
Имеется несколько макросов, по одному на каждый имеющийся аргумент, число которых определяете вы сами. Когда определен MYAPPDEBUG
, вызовы макросов DPRINT
развертываются в вызовы fprintf()
. Когда MYAPPDEBUG
не определен, эти вызовы развертываются в ничто. (Так, в сущности, работает assert()
; мы описали assert()
в разделе 12.1 «Операторы проверки: assert()
».)
Эта методика работает; мы сами ее использовали и видели, как ее рекомендуют в учебниках. Однако, она может быть усовершенствована и дальше с уменьшением количества макросов до одного:
/* МЕТОДИКА 2 --- наиболее переносима; рекомендуется */
/* В заголовочном файле приложения: */
#ifdef MYAPPDEBUG