Linux: Полное руководство
Шрифт:
22.1. Ошибки и отладка
Самыми страшными являются не синтаксические, а так называемые логические ошибки. Ваша программа может содержать хоть сотню мелких синтаксических ошибок — там не так функцию написали, там забыли указать параметр, а где-то пропустили точку с запятой. После исправления всех этих ошибок программа будет работать.
Если же ваша программа содержит логическую ошибку — например, вы выбрали неправильный алгоритм или неправильно его использовали, — то компилятор может даже не выдать предупреждения. Вроде бы ошибок нет, программа работает, но результат выдает неправильный или в какой-то момент вообще рушится. Мне запомнился один афоризм: «Программа делает то, что вы ей сказали, но не то, что вам хочется». Это и есть самое удачное, на мой взгляд, описание логической ошибки.
Если вы заметили ошибку до того, как ваша программа «увидела свет», то можете считать, что вам
Например, если ваша программа использует какую-нибудь СУБД для обработки информации, вы могли установить размер поля меньший, чем нужно. Первые два месяца программа работала отлично, а в один прекрасный момент оператор ввел очень длинную фамилию очень важного клиента, и ваша программа не внесла эту информацию в базу. Но это тривиальная ошибка, и ее можно исправить очень быстро.
А вот когда вы пишете программу для управления устройством или для обработки показаний внешних датчиков, подключенных к компьютеру, бывает очень сложно найти ошибку, связанную с конфликтом на аппаратном уровне. Например, пользователь установил новое устройство, которое конфликтует с вашим контроллером. Или вы написали модуль для поддержки одного контроллера, а пользователь подключил два, и оба теперь не работают.
Какие же ошибки часто совершают начинающие (и не только) программисты? Самая тривиальная — неправильное использование операций инкремента и декремента. Например, следующие выражения не эквивалентны:
В первом случае переменной x будет присвоено значение 15, а во втором — 16.
Следующей по частоте является ошибка неучтенной единицы. Например, вам нужен массив, состоящий из 10 элементов, вы его объявляете:
А затем инициализируете его с помощью цикла:
Этот фрагмент кода попытается инициализировать несуществующий элемент — а[10].
Или еще один распространенный случай: программист забывает, что нумерация элементов массива начинается с 0, и не инициализирует первый элемент массива:
Особое место в зоопарке ошибок занимают ошибки, связанные с неправильным использованием указателей. Все эти ошибки можно условно разделить на три группы, которые я сейчас кратко перечислю.
1) Неправильное использование операторов * и &. Это самая распространенная группа ошибок начинающих программистов. Вот характерный пример такой ошибки:
2) Выделение недостаточного для адресации объекта объема памяти. Например, мы получим такую ошибку, если попытаемся скопировать в строку s (вышеприведенный фрагмент кода) строку, состоящую из 30 символов,
3) Использование неинициализированных указателей. Такие ошибки часто встречаются при работе с динамическими структурами. Например, с линейными списками: вы забыли инициализировать главный элемент (head = NULL) и пытаетесь добавить в список новый элемент.
Использование рекурсивных вызовов может повлечь за собой ошибку переполнения стека, если вы неправильно зададите условие завершения рекурсии. Как правило, рекурсивная функция вызывает саму себя с несколько измененными параметрами. Рано или поздно такая функция должна, в зависимости от переданных параметров, возвратить какое-нибудь значение, а не опять вызвать саму себя.
Для облегчения поиска ошибок были созданы специальные программы — отладчики. Одним из самых удачных отладчиков для Linux является gdb (The GNU Debugger). Этот отладчик входит в состав всех распространенных дистрибутивов (за исключением их «урезанных» версий — для рабочих станций), и для его установки достаточно установить пакет gdb.
С помощью gdb вы сможете:
♦ запустить вашу программу с определенными аргументами;
♦ запустить программу в пошаговом режиме;
♦ установить точки останова (breakpoint);
♦ установить условие останова программы;
♦ узнать, что случилось, если программа неожиданно завершилась.
22.2.
Отладчик gdbФормат вызова отладчика gdb следующий:
Ключи отладчика описаны в таблице 22.1.
Ключи командной строки gdb Таблица 22.1
Ключ | Назначение |
---|---|
– help или -h | Вывод краткого описания всех параметров |
– nx или -n | Не обрабатывать команды файла инициализации .gdbinit |
– q | Не выводить приветствие и информацию об авторских правах |
– batch | Командный режим. Отладчик возвращает 0, если были выполнены все команды, указанные в файле, заданном параметром -x (и файле .gdbinit, если его использование разрешено). Если же хотя бы одна из команд не выполнена, возвращается ненулевое значение |
– cd=каталог | Установить рабочий каталог (по умолчанию используется текущий каталог) |
– f или -fullname | Данная опция нужна, если вы планируете использовать интерфейс текстового процессора Emacs для отладки ваших программ с помощью gdb. Для более подробного описаний обратитесь к справочной системе |
– b bps (bits per second) | Установить скорость обмена информацией по последовательному интерфейсу, если вы отлаживаете вашу программу удаленно |
– tty=терминал | Установить терминал в качестве стандартного ввода и вывода для отлаживаемой программы. |
– s файл или -symbols=файл | Читает таблицу символов из указанного файла |
– write | Разрешить запись в исполняемые и core-файлы |
– e программа | Использовать указанную программу в качестве фильтра дампа |
– se=файл | Читать таблицу символов из указанного файла и использовать указанный файл в качестве исполнимого |
– core=файл или -с файл | Указать файл дампа |
– command=файл или -x файл | Выполнить указанные в файле команды (используется в командном режиме) |
– d каталог | Добавить каталог к списку поиска исходных текстов |
[prog|core|procID] | Последний параметр задает объект, который нужно отлаживать. Вы можете задать программу (prog), или дамп-файл (core), который будет создан в случае ошибки программы (Segmentation fault), или же подсоединиться к уже запущенному процессу (procID) |
– p PID | Подключиться к уже запущенному процессу (данная опция стала доступной в версии gdb 5.2) |
Чтобы использовать gdb для отладки вашей программы, нужно добавить в исполняемый файл отладочную информацию. Для этого откомпилируете вашу программу с опцией -g:
Данная опция включает отладочную информацию в родном для операционной системы формате, с которым может работать gdb.
Затем нужно вызвать gdb так:
Если после запуска вашей программы произошла ошибка и был создан дамп-файл (core), можно передать отладчику и этот файл:
Можно также подключиться к уже запущенному процессу, для этого нужно передать его PID:
Только убедитесь сначала в том, что у вас нет файла 1111, поскольку gdb сначала ищет исполняемый файл, затем core-файл, а уже затем PID.
После запуска отладчика в интерактивном режиме вы можете использовать команды, самые важные из которых перечислены в таблице 22.2. Об остальных можно узнать в справочной системе: man gdb.