Издательский дом ООО "Гейм Лэнд"СПЕЦВЫПУСК ЖУРНАЛА ХАКЕР #45, АВГУСТ 2004 г.

Доверяй, но проверяй

Косякин Антон

Спецвыпуск Xakep, номер #045, стр. 045-024-2


Также, если не контролировать длины буферов, случается, что одни данные программы затирают другие. Это приводит ко всяким противным последствиям. Во избежание их в случае со строками были введены функции strn*, имеющие дополнительный параметр – максимальное число символов для обработки. Их использование значительно безопаснее.

Еще одним багом в работе программы является неинициализированная нулями память, что особенно заметно при работе со строками. Чтобы не наткнуться на этот баг, следует использовать вместо malloc функцию calloc, которая не только выделит память, но и обнулит её.

Освобождение памяти

Вспомним объектно-ориентированные языки программирования, например Java с её сборщиком мусора. Мы знаем, что, когда объект нам становится не нужен, вызывается деструктор и вся занимаемая им (объектом) память освобождается. Однако если во время работы программы некоторый объект динамически выделяет необходимую ему память (например, под буферы, массивы), то после вызова деструктора все указатели на выделенные блоки памяти исчезнут, но память так и останется неосвобожденной. Таким образом, программа будет хранить данные, которые ей абсолютно не нужны и к которым даже нет доступа. В больших программах это может привести не только к замедлению их работы, но и к зависанию или даже краху системы. Именно поэтому мы и поговорим сейчас на тему освобождения памяти.

Смотри:

while (true) {

malloc(1);

};

Запусти и увидишь, во что может вылиться такое безобидное действие, как выделение одного байта. И, к тому же, я уверен, что с каждой итерацией цикла количество используемой программой памяти будет возрастать далеко не на один байт.

Кстати, о выделении памяти. Когда делаешь это с помощью функций типа malloc, обязательно проверяй указатель на равенство NULL. Ведь может так случиться, что по определенным причинам система сумела выделить память и вернула NULL. А если обратишься по нулевому указателю, то неминуемо получишь окошечко с сообщением о завершении программы.

Также стоит аккуратно обращаться с указателями на массивы, передаваемыми в функцию. Поясню:

Листинг

int some_func(int *m) {

while (*m != 0) {

printf("%d\n", *m);

m++;

};

}

Эта функция принимает указатель на массив целых и выводит его (массива) содержимое на экран. Так делать нельзя. Кто знает, сколько в этом массиве чисел. Понятно, что эту программу пишешь ты, а уж ты заведомо знаешь, что чисел будет 17486, причем последнее из них является нулем, и все будет хорошо. А вдруг ты захочешь что-нибудь изменить? Вдруг ты решишь сделать 17 чисел, и последнее будет единицей? А функцию исправить забудешь. Или через пару месяцев столкнешься с аналогичной задачей и просто скопируешь часть кода в новую программу? Всякое бывает. Как говорил Петя Нортон, "backup often". В данном случае будь предусмотрительнее.

Чтобы описанная выше функция стала безопасной, необходимо добавить в нее второй параметр – количество элементов массива, а также сделать проверку указателя на NULL.

Назад на стр. 045-024-1  Содержание  Вперед на стр. 045-024-3