Книга: 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
. Нам нравится наш подход, поскольку результаты сохраняются в переменных, которые можно проверить (и неоднократно) после вызова функции и даже спустя несколько операторов.
- 15.4.1. Код отладки времени компилирования
- Дополнительные национальные кодовые страницы и порядки сортировки
- Глава 5 Агрессивные формы кода и борьба с ними
- Стиль написания исходного кода
- 1.4. Кодирование информации
- 1.4.1. Кодирование во время выполнения
- Три способа кодирования звука
- Листинг 15.11. Код для загрузки файла с Web-сервера
- 2. Пример создания базового отношения в записи на псевдокоде
- 5. Нормальная форма Бойса – Кодда (NFBC)
- Приложение 10. Коды ошибок
- Можно ли избавиться от необходимости использовать двойной щелчок кнопкой мыши при открытии папки?