Книга: Linux программирование в примерах

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

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

Довольно часто условие в if или while состоит из нескольких проверок, разделенных && или ||. Если эти проверки являются вызовами функций (или даже не являются ими), невозможно осуществить пошаговое прохождение каждой отдельной части условия. Команды 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) && 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 && rval3 == 0);

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

Наконец, мы обращаем внимание, что не все программисты-эксперты согласились бы здесь с нашим советом. Когда каждый компонент условия является вызовом функции, можно установить на каждую контрольную точку, использовать step для входа в каждую функцию, а затем использовать finish для ее завершения. GDB сообщит вам возвращаемое функцией значение, и с этого места вы можете использовать для продолжения cont или step. Нам нравится наш подход, поскольку результаты сохраняются в переменных, которые можно проверить (и неоднократно) после вызова функции и даже спустя несколько операторов.

Оглавление книги


Генерация: 1.250. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз