Литмир - Электронная Библиотека
A
A

#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.c
в
gawk
:

typedef enum scanstate {

 NOSTATE,  /* сканирование еще не начато (все) */

 INLEADER, /* пропуск начальных данных (RS = &quot;&quot;) */

 INDATA,   /* в теле записи (все) */

 INTERM,   /* терминатор сканирования (RS = &quot;&quot;, RS = regexp) */

} SCANSTATE;

SCANSTATE state;

/* ... остальной код без изменений! ... */

Теперь при просмотре state из GDB мы видим что-то полезное:

(gdb) <b>print state</b>

$1 = NOSTATE

15.4.1.3. При необходимости переставляйте код

Довольно часто условие в

if
или
while
состоит из нескольких проверок, разделенных
&amp;&amp;
или
||
. Если эти проверки являются вызовами функций (или даже не являются ими), невозможно осуществить пошаговое прохождение каждой отдельной части условия. Команды GDB
step
и
next
работают на основе операторов (statements), а не выражений (expressions). (Разнесение их по нескольким строкам все равно не помогает).

Рекомендация: перепишите исходный код, явно используя временные переменные, в которых сохраняются значения или условные результаты, так что вы можете проверить их в отладчике. Первоначальный код должен быть сохранен в комментарии, чтобы вы (или программист после вас) могли сказать, что происходит.

Вот конкретный пример: функция

do_input()
из файла
io.c gawk
:

1  /* do_input --- главный цикл обработки ввода */

2

3  void

4  do_input()

5  {

6   IOBUF *iop;

7   extern int exiting;

8   int rval1, rval2, rval3;

9

10  (void)setjmp(filebuf); /* for 'nextfile' */

11

12  while ((iop = nextfile(FALSE)) != NULL) {

13   /*

14    * Здесь было:

15    if (inrec(iop) == 0)

16     while (interpret(expression_value) &amp;&amp; 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
. Внешний цикл (строки 12 и 33) проходит через файлы данных командной строки. Комментарий в строках 13–19 показывает оригинальный код, который читает из текущего файла каждую запись и обрабатывает ее

Возвращаемое

inrec()
значение 0 означает, что все в порядке, тогда как ненулевое возвращаемое значение
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 &amp;&amp; rval3 == 0);

225
{"b":"576259","o":1}