А.2. Поиск ошибок в динамической памяти
А.2. Поиск ошибок в динамической памяти
При написании программы зачастую неизвестно, сколько памяти потребуется ей во время выполнения. Например, строка, читаемая из файла, может иметь любую длину. Работа с динамической памятью осуществляется посредством функций malloc(), free() и их вариантов. Следует придерживаться таких правил:
? число запросов на выделение памяти (вызовов функции malloc()) должно в точности совпадать с чистом запросов на освобождение памяти (вызовов функции free());
? операции чтения и записи динамической памяти должны выполняться в рамках выделенной области, не выходя за ее пределы;
? к выделенной области нельзя обращаться после того, как она была освобождена.
Выделение и освобождение динамической памяти происходят на этапе выполнения программы, поэтому статический анализ исходных текстов редко позволяет выявить недочеты. Утилиты проверки памяти сначала загружают программу, а затем определяют, нарушаются ли перечисленные выше правила. Выявляются следующие ошибки:
? чтение памяти до того, как она была выделена;
? запись в память до того, как она была выделена;
? чтение данных по адресу, предшествующему началу выделенной области;
? запись данных по адресу, предшествующему началу выделенной области;
? чтение данных по адресу, стоящее после выделенной области;
? запись данных по адресу, стоящему после выделенной области;
? чтение памяти после того, как она была освобождена;
? запись в память после того, как она была освобождена;
? неудачная попытка освободить выделенную память;
? попытка повторно освободить ту же самую область памяти;
? попытка освободить память, которая не была выделена.
Полезно также предупреждать о выделениях областей размером 0 байтов, так как это обычно свидетельствует об ошибке программиста.
В табл. А.1 описаны возможности четырех диагностических средств. К сожалению, ни одно из них не выявляет все возможные ошибки. Кроме того, ни одно средство не позволяет обнаруживать попытки чтения или записи памяти до того, как она была выделена, хотя такая попытка наверняка приведет к нарушению сегментации. Обнаруживаются те ошибки, которые действительно происходят в процессе работы программы. Если передать программе такие входные данные, что выделять память не понадобится, ошибки обращения к памяти не будут найдены. Для максимально тщательной проверки программы рекомендуется передавать ей самые разные входные данные, чтобы протестировать все возможные пути ее выполнения. Желательно также тестировать программу всеми имеющимися средствами.
Таблица А.1. Возможности средств проверки динамической памяти (X — обнаружение, О — обнаружение в некоторых случаях):
Ошибка Проверка функции malloc() Утилита mtrace Библиотека ccmalloc Библиотека Electric Fence Чтение памяти до того, как она была выделена Запись в память до того, как она была выделена Чтение данных по адресу, предшествующему началу выделенной области X Запись данных по адресу, предшествующему началу выделенной области О О X Чтение данных по адресу, стоящему после выделенной области X Запись данных по адресу, стоящему после выделенной области X X Чтение памяти после того, как она была освобождена X Запись в память после того, как она была освобождена X Неудачная попытка освободить выделенную память X X Попытка повторно освободить ту же самую область памяти X X Попытка освободить память. которая не была выделена X X Выделение памяти нулевого размера X X