Читать книгу 📗 "Linux программирование в примерах - Роббинс Арнольд"
#define INDATA 3 /* в теле записи (все) */#define INTERM 4 /* терминатор сканирования (RS = RS = regexp) */int state;...state = NOSTATE;...state = INLEADER;...if (state != INTERM) ...На уровне исходного кода это выглядит замечательно. Но опять-таки, есть проблема, когда вы пытаетесь просмотреть код из GDB:
(gdb) <b>print state</b>$1 = 2Здесь вы также вынуждены возвращаться обратно и смотреть в заголовочный файл, чтобы выяснить, что означает 2. Какова же альтернатива?
Рекомендация: Для определения именованных констант используйте вместо макросов перечисления (enum). Использование исходного кода такое же, а значения enum может выводить также и отладчик.
Пример, тоже из
io.cgawktypedef enum scanstate { NOSTATE, /* сканирование еще не начато (все) */ INLEADER, /* пропуск начальных данных (RS = "") */ INDATA, /* в теле записи (все) */ INTERM, /* терминатор сканирования (RS = "", RS = regexp) */} SCANSTATE;SCANSTATE state;/* ... остальной код без изменений! ... */Теперь при просмотре state из GDB мы видим что-то полезное:
(gdb) <b>print state</b>$1 = NOSTATE15.4.1.3. При необходимости переставляйте код
Довольно часто условие в
ifwhile&&||stepnextРекомендация: перепишите исходный код, явно используя временные переменные, в которых сохраняются значения или условные результаты, так что вы можете проверить их в отладчике. Первоначальный код должен быть сохранен в комментарии, чтобы вы (или программист после вас) могли сказать, что происходит.
Вот конкретный пример: функция
do_input()io.c gawk1 /* do_input --- главный цикл обработки ввода */23 void4 do_input()5 {6 IOBUF *iop;7 extern int exiting;8 int rval1, rval2, rval3;910 (void)setjmp(filebuf); /* for 'nextfile' */1112 while ((iop = nextfile(FALSE)) != NULL) {13 /*14 * Здесь было:15 if (inrec(iop) == 0)16 while (interpret(expression_value) && inrec(iop) == 0)17 continue;18 * Теперь развернуто для простоты отладки.19 */20 rvall = inrec(iop);21 if (rvall == 0) {22 for (;;) {23 rval2 = rval3 = -1; /* для отладки */24 rval2 = interpret(expression_value);25 if (rval2 != 0)26 rval3 = inrec(iop);27 if (rval2 == 0 || rval3 != 0)28 break;29 }30 }31 if (exiting)32 break;33 }34 }(Номера строк приведены относительно начала этой процедуры, а не файла.) Эта функция является основой главного цикла обработки
gawkВозвращаемое
inrec()interpret()Строки 20–30 представляют переписанный код, который вызывает каждую функцию отдельно, сохраняя возвращаемые значения в локальных переменных, чтобы их можно было напечатать из отладчика. Обратите внимание, как в строке 23 этим переменным каждый раз присваиваются известные, ошибочные значения: в противном случае они могли бы сохранить свои значения от предыдущих итераций цикла. Строка 27 является тестом завершения, поскольку код изменился, превратившись в бесконечный цикл (сравните строку 22 со строкой 16), тест завершения цикла является противоположным первоначальному.
В качестве отступления, мы признаемся, что нам пришлось тщательно изучить переделку, когда мы ее сделали, чтобы убедиться, что она точно соответствует первоначальному коду; она соответствовала. Теперь нам кажется, что, возможно, вот эта версия цикла была бы ближе к оригиналу:
/* Возможная замена для строк 22 - 29 */do { rval2 = rval3 = -1; /* для отладки */ rval2 = interpret(expression_value); if (rval2 != 0) rval3 = inrec(iop);} while (rval2 != 0 && rval3 == 0);