Примечание
Языки программирования с компиляторами, такие как С, обладают возможностью поймать синтаксические ошибки в процессе компиляции, в то время как интерпретируемые языки, например язык командной оболочки Linux, могут обнаружить синтаксические ошибки только тогда, когда вы попытаетесь выполнить программу. Если проблема в коде обработки ошибки, нелегко будет выявить ее в ходе тестирования.
□ Попытайтесь выполнить основную часть программы на бумаге, этот процесс называют формальным прогоном. Для наиболее важных подпрограмм запишите значения на входе и вычислите шаг за шагом выходные значения. Для отладки совсем не обязательно всегда применять компьютер, иногда именно компьютер создает проблемы. Даже разработчики, пишущие библиотеки, компиляторы и операционные системы, делают ошибки! С другой стороны, не спешите винить во всем используемые программные средства; гораздо вероятнее, что ошибка закралась в вашу новую программу, а не в компилятор.
Общие методы отладки
Существует несколько разных подходов к отладке и тестированию типовой программы Linux. Обычно разработчик запускает программу и смотрит, что происходит. Если программа не работает, необходимо решить, что с ней делать. Можно изменить программу и попробовать снова (анализ программного кода, метод проб и ошибок), можно попытаться получить больше информации о том, что происходит внутри программы (оснащение контрольными средствами) или можно непосредственно проанализировать работу программы (контролируемое выполнение). Отладка включает в себя пять следующих этапов:
□ тестирование — поиск существующих изъянов или ошибок;
□ стабилизация — обеспечение повторяемости ошибок;
□ локализация — определение строки кода, отвечающей за ошибку;
□ корректировка — исправление программного кода;
□ проверка — подтверждение того, что исправление работает.
Программа с ошибками
Давайте рассмотрим пример программы, содержащей ошибки. Читая данную главу, вы будете пробовать отладить эту программу. Она написана во время разработки большой программной системы. Ее задача — протестировать единственную функцию sort, которая предназначена для реализации сортировки массива структур типа item методом "пузырька". Элементы сортируются по возрастанию поля key. Программа вызывает функцию sort для сортировки контрольного примера, чтобы протестировать функцию. В реальной жизни вы никогда не стали бы обращаться к этому конкретному алгоритму из-за его очень низкой эффективности. Мы же применяем его, потому что он короткий, относительно простой и его легко превратить в неправильный. На самом деле в стандартной библиотеке языка С есть функция с именем qsort, выполняющая эту задачу.
К сожалению, исходный код программы нелегко читается, в нем нет комментариев, и автор уже недоступен. Вам придется биться с ней самостоятельно, начиная с основной подпрограммы debug1.c.
/* 1 */ typedef struct {
/* 2 */ char *data;
/* 3 */ int key;
/* 4 */ } item;
/* 5 */
/* 6 */ item array[] = {
/* 7 */ {"bill", 3},
/* 8 */ {"neil", 4},
/* 9 */ {"john", 2},
/* 10 */ {"rick", 5},
/* 11 */ {"alex", 1},
/* 12 */ };
/* 13 */
/* 14 */ sort(a, n)
/* 15 */ item *a;
/* 16 */ {
/* 17 */ int i = 0, j = 0;
/* 18 */ int s = 1;
/* 19 */
/* 20 */ for(; i < n && s != 0; i++) {
/* 21 */ s = 0;
/* 22 */ for(j = 0; j < n; j++) {
/* 23 */ if(a[j].key > a[j + 1].key) {
/* 24 */ item t = a[j];
/* 25 */ a[j] = a[j+1];
/* 26 */ a[j+1] = t;
/* 27 */ s++;
/* 28 */ }
/* 29 */ }
/* 30 */ n--;
/* 31 */ }
/* 32 */ }
/* 33 */
/* 34 */ main()
/* 35 */ {
/* 36 */ sort(array,5);
/* 37 */ }
Теперь попытайтесь откомпилировать эту программу:
$ <b>сс -о debug1 debug1.с</b>
Она компилируется успешно без каких-либо сообщений об ошибках или предупреждений.
Прежде чем выполнять эту программу, вставьте фрагмент кода для вывода результата. В противном случае вы не будете знать, отработала ли программа. Вы добавите несколько дополнительных строк для отображения массива после сортировки. Назовите новую версию debug2.c.
/* 33 */ #include <stdio.h>
/* 34 */ main()
/* 35 */ {
/* 36 */ int i;
/* 37 */ sort(array, 5);
/* 38 */ for(i = 0; i < 5; i++)
/* 39 */ printf("array[3d] = (%s, %d)n",
/* 40 */ i, array[i].data, array[i].key);
/* 41 */ }
Этот дополнительный код, строго говоря, не является частью, позже добавленной программистом. Мы заставили вас добавить его только для тестирования программы. Следует быть очень внимательным, чтобы не внести новых ошибок в ваш тестовый код. Теперь снова откомпилируйте программу и на этот раз выполните ее:
$ <b>cc -о debug2 debug2.с</b>
$ <b>./debug2</b>
Что произойдет, когда вы сделаете это, зависит от вашей версии Linux (или UNIX) и особенностей ее установки. В своих системах мы получили следующий результат:
array[0] = {john, 2}
array[1] = {alex, 1}
array[2] = {(null), -1}
array[3] = {bill, 3}