[Содержание]   [Назад]   [Пред]   [Вверх]   [След]   [Вперед]  


1. Пример сеанса DDD

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

Программа-пример `sample.c' (см. раздел 1.1 Пример программы) обнаруживает следующую ошибку. Программа sample должна сортировать и печатать свои аргументы в виде чисел, как в этом примере:

$ ./sample 8 7 5 4 1 3
1 3 4 5 7 8

Однако, при некоторых значениях аргументов она ошибается:

$ ./sample 8000 7000 5000 1000 4000
1000 1913 4000 5000 7000

Хотя вывод отсортирован и содержит верное число аргументов, некоторые аргументы пропущены и заменены на странные числа; в данном случае пропущено 8000, а вместо него стоит 1913.(3)

Давайте применим DDD, чтобы увидеть, что происходит. Сначала вы должны скомпилировать `sample.c' для отладки (см. раздел 4.1 Компиляция для отладки), задав при компиляции флаг -g:

$ gcc -g -o sample sample.c

Теперь вы можете вызвать DDD (см. раздел 2.1 Вызов DDD) для исполняемого файла sample:

$ ddd sample

Через несколько секунд появляется DDD. Окно с исходным текстом содержит код отлаживаемой программы; для прокрутки по файлу используйте полоску прокрутки.

PICS/tut-invoke

Консоль отладчика (в самом низу) содержит информацию о версии DDD, а также подсказку GDB.(4)

GNU DDD Version 3.2.1, by Dorothea L@"utkehaus and Andreas Zeller.
Copyright (C) 1999 Technische Universit@"at Braunschweig, Germany.
Copyright (C) 1999 Universit@"at Passau, Germany.
Reading symbols from sample...done.
(gdb) 

Первое, что нужно сейчас сделать -- установить точку останова (см. раздел 5.1 Точки останова), что заставит sample остановиться в интересующем вас месте. Щелкните на пустом месте слева от инициализации a. Поле аргумента `():' теперь содержит позицию (`sample.c:31'). Теперь щелкните на `Break', чтобы создать точку останова в позиции `()'. Вы увидите, что на строке 31 появился маленький знак "стоп".

Следующее, что нужно сделать, -- действительно запустить программу, чтобы вы могли исследовать ее поведение (см. раздел 6. Запуск программы). Для запуска программы выберите `Program => Run'; появится диалоговое окно `Run Program'.

PICS/tut-run

Теперь вы можете ввести в поле `Run with Arguments' аргументы программы sample. Введите здесь аргументы, приводящие к ошибочному поведению -- то есть, `8000 7000 5000 1000 4000'. Щелкните на `Run', чтобы началось выполнение с заданными вами аргументами.

Теперь GDB запускает sample. Через несколько мгновений, когда достигнута точка останова, выполнение останавливается. Об этом сообщается в консоли отладчика.

(gdb) break sample.c:31
Breakpoint 1 at 0x8048666: file sample.c, line 31.
(gdb) run 8000 7000 5000 1000 4000
Starting program: sample 8000 7000 5000 1000 4000

Breakpoint 1, main (argc=6, argv=0xbffff918) at sample.c:31
(gdb) 

Выполняемая в текущий момент строка обозначена зеленой стрелкой.

=> a = (int *)malloc((argc - 1) * sizeof(int));

Сейчас вы можете проверить значения переменных. Чтобы проверить простую переменную, вы можете просто поместить указатель мыши над ее именем и задержать его там. Спустя секунду всплывет маленькое окно со значением этой переменной (см. раздел 7.1 Просмотр простых значений с помощью подсказок). Попробуйте проделать это с `argv', чтобы увидеть ее значение (6). Локальная переменная `a' пока не проинициализирована; вы, вероятно, увидите 0x0 или какое-то другое значение неверного указателя.

Чтобы выполнить текущую строку, щелкните на кнопке `Next' из командной панели. Стрелка продвинется на следующую строку. Теперь снова укажите на `a' и увидите, что значение изменилось, и переменная `a' действительно стала инициализированной.

PICS/tut-value

Чтобы исследовать отдельные значения массива `a', введите в поле аргумента `a[0]' (вы можете заранее очистить его, щелкнув на `():'), а затем щелкните на кнопку `Print'. Это напечатает текущее значение `()' в консоли отладчика (см. раздел 7.2 Печать простых значений в консоли отладчика). В нашем случае вы получите

(gdb) print a[0]
$1 = 0
(gdb) 

или какое-то другое значение (заметьте, что `a' была только размещена, но ее содержимое еще не проинициализировано.)

Чтобы увидеть все члены `a' одновременно, вы должны применить особый оператор GDB. Поскольку `a' была размещена динамически, GDB не знает ее размера; вы должны явно указать его, используя оператор `@' (см. раздел 7.3.2.1 Фрагменты массива). Введите в поле аргумента `a[0]@(argc - 1)' и щелкните кнопку `Print'. Вы получите первые argc - 1 элементов `a', или

(gdb) print a[0]@(argc - 1)
$2 = {0, 0, 0, 0, 0}
(gdb) 

Вместо того чтобы использовать `Print' для просмотра текущего значения `a' на каждом останове, вы можете также отобразить `a', то есть сделать так, чтобы ее значение показывалось автоматически. Щелкните на `Display' оставив в поле аргумента `a[0]@(argc - 1)'. Содержимое `a' теперь показывается в другом окне, окне данных. Для горизонтального поворота массива щелкните на `Rotate'.

PICS/tut-display

Далее идет присваивание значений членам `a':

=>  for (i = 0; i < argc - 1; i++)
        a[i] = atoi(argv[i + 1]);

Теперь вы можете щелкать на `Next' и снова на `Next', чтобы увидеть, как происходит присваивание отдельным членам `a'. Измененные члены подсвечиваются.

Для продолжения выполнения цикла используйте кнопку `Until'. Она велит GDB выполнять программу до тех пор, пока не будет достигнута строка, большая текущей. Щелкайте на `Until', пока не окажетесь на вызове `shell_sort':

=>  shell_sort(a, argc);

В этом месте содержимое `a' должно быть равно `8000 7000 5000 1000 4000'. Снова щелкните на `Next', чтобы пройти через вызов `shell_sort'. DDD остановится на цикле

=>  for (i = 0; i < argc - 1; i++)
        printf("%d ", a[i]);

и вы увидите, что после окончания `shell_sort' содержимое `a' стало равным `1000, 1913, 4000, 5000, 7000' -- то есть `shell_sort' каким-то образом испортил его.

Чтобы выяснить, что же случилось, выполните программу снова. На этот раз не проходите инициализацию, а перескочите прямо на вызов `shell_sort'. Удалите старую точку останова, выбрав ее и щелкнув на `Clear'. Затем создайте новую точку останова в строке 35, перед вызовом `shell_sort'. Для повторного выполнения программы выберите `Program => Run Again'.

Опять же, DDD остановится перед вызовом `shell_sort':

=>  shell_sort(a, argc);

На этот раз вы хотите ближе исследовать, что делает `shell_sort'. Щелкните на `Step', чтобы войти в вызов `shell_sort'. Это оставит вашу программу на первой исполняемой строке,

=> int h = 1;

тогда как консоль отладчика говорит нам, что произошел вход в функцию:

(gdb) step
shell_sort (a=0x8049878, size=6) at sample.c:9
(gdb)

Такой вывод, показывающий функцию (и ее аргументы), где остановлено выполнение `sample', называется отображением стека фреймов. Он дает представление о содержимом стека. Вы можете использовать `Status => Backtrace', чтобы узнать, в каком месте стека вы находитесь; если выбрать строку (или щелкнуть на `Up' или `Down'), вы сможете перемещаться по стеку. Обратите внимание на то, что отображение `a' исчезает, когда вы покидаете его фрейм.

PICS/tut-backtrace

Давайте теперь проверим правильность аргументов `shell_sort'. Вернувшись в самый нижний фрейм, введите в поле аргумента `a[0]@size' и щелкните на `Print':

(gdb) print a[0] @ size
$4 = {8000, 7000, 5000, 1000, 4000, 1913}
(gdb) 

Сюрприз! Откуда взялось это лишнее значение 1913? Ответ прост: размер массива, передаваемый в функцию `shell_sort' как `size', больше чем нужно на единицу -- странное число 1913 оказалось в памяти после `a'. И это значение также сортируется.

Чтобы понять, действительно ли это является причиной ошибки, вы можете теперь присвоить `size' правильное значение (см. раздел 7.3.3 Присваивание переменных). Выберите в исходном коде `size' и щелкните на `Set'. Появится диалоговое окно, где вы можете отредактировать значение этой переменной.

PICS/tut-set

Измените значение `size' на 5 и щелкните на `OK'. Затем щелкните на `Finish', чтобы продолжить выполнение функции `shell_sort':

(gdb) set variable size = 5
(gdb) finish
Run till exit from #0  shell_sort (a=0x8049878, size=5) at sample.c:9
0x80486ed in main (argc=6, argv=0xbffff918) at sample.c:35
(gdb) 

Получилось! Для `a' теперь показаны корректные значения `1000, 4000, 5000, 7000, 8000'.

PICS/tut-finish

Вы можете убедиться, что эти значения будут на самом деле напечатаны на стандартный вывод, выполнив программу дальше. Щелкните на `Cont', чтобы продолжить выполнение.

(gdb) cont
1000 4000 5000 7000 8000 

Program exited normally.
(gdb) 

Сообщение `Program exited normally.' исходит от GDB; оно говорит, что программа sample завершила выполнение.

Найдя причину ошибки, вы теперь можете исправить исходный код. Щелкните на `Edit', чтобы отредактировать `sample.c', и измените строку

shell_sort(a, argc);

на корректный вызов

shell_sort(a, argc - 1);

Теперь вы можете перекомпилировать sample

$ gcc -g -o sample sample.c

и проверить (через `Program => Run Again'), что sample работает хорошо.

(gdb) run
`sample' has changed; re-reading symbols.
Reading in symbols...done.
Starting program: sample 8000 7000 5000 1000 4000
1000 4000 5000 7000 8000 

Program exited normally.
(gdb) 

Все готово; сейчас программа работает правильно. Вы можете завершить этот сеанс DDD с помощью `Program => Exit' или Ctrl+Q.

1.1 Пример программы

Это исходный файл `sample.c' программы-примера.

/* sample.c -- Sample C program to be debugged with DDD */

#include <stdio.h>
#include <stdlib.h>

static void shell_sort(int a[], int size)
{
    int i, j;
    int h = 1;
    do {
        h = h * 3 + 1;
    } while (h <= size);
    do {
        h /= 3;
        for (i = h; i < size; i++)
        {
            int v = a[i];
            for (j = i; j >= h && a[j - h] > v; j -= h)
                a[j] = a[j - h];
            if (i != j)
                a[j] = v;
        }
    } while (h != 1);
}

int main(int argc, char *argv[])
{
    int *a;
    int i;

    a = (int *)malloc((argc - 1) * sizeof(int));
    for (i = 0; i < argc - 1; i++)
        a[i] = atoi(argv[i + 1]);

    shell_sort(a, argc);

    for (i = 0; i < argc - 1; i++)
        printf("%d ", a[i]);
    printf("\n");

    free(a);
    return 0;
}


[Содержание]   [Назад]   [Пред]   [Вверх]   [След]   [Вперед]  
Используются технологии uCoz