Что будет, если дважды вызвать free?cplus-99

Вызов free() на одном и том же указателе дважды - классическая ошибка управления памятью, которая приводит к неопределенному поведению (Undefined Behavior, UB). Вот детальный разбор последствий:

1. Непосредственные последствия

int* ptr = malloc(sizeof(int));
free(ptr);  // Первый вызов - корректный
free(ptr);  // Второй вызов - UB!
  • Неопределенное поведение: Согласно стандарту C/C++, программа теряет валидность
  • Типичные проявления:
    • Аварийное завершение (segmentation fault)
    • Повреждение кучи (heap corruption)
    • Трудновоспроизводимые ошибки в других частях программы

2. Механизм работы кучи

При первом free():

  1. Память возвращается в менеджер кучи
  2. Метаданные кучи обновляются
  3. Указатель может быть помечен как невалидный

При втором free():

  1. Менеджер кучи обнаруживает двойное освобождение
  2. Возможные реакции:
    • Аварийное завершение (в debug-режимах)
    • Тихий отказ (в release-сборках)
    • Повреждение структур данных кучи

3. Примеры опасных сценариев

int* ptr1 = malloc(sizeof(int));
int* ptr2 = ptr1;
free(ptr1);
free(ptr2);  // То же самое UB!

4. Диагностика и защита

Методы предотвращения:

  1. Обнуление указателя после освобождения:
    free(ptr);
    ptr = NULL;  // Последующие free(NULL) безопасны
    
  2. Использование умных указателей (C++)
  3. Инструменты для детектирования:
    • Valgrind (memcheck)
    • AddressSanitizer (ASan)
    • CRT debug heaps (Windows)

5. Особенности разных реализаций

РеализацияПоведение при double-free
glibc (Linux)Может вызвать abort()
MSVC CRT (Windows)Повреждает кучу
jemallocЗащитные механизмы

6. Почему это опасно?

  1. Уязвимости безопасности: Может быть использовано для атак типа "use-after-free"
  2. Трудность отладки: Проблема может проявиться далеко от места ошибки
  3. Повреждение данных: Может затронуть другие объекты в куче

7. Правильные альтернативы

Для C++:

std::unique_ptr<int> ptr(new int); // Автоматическое освобождение

Для C:

#define SAFE_FREE(p) { free(p); (p) = NULL; }

Резюмируем: двойной вызов free() - серьезная ошибка, приводящая к неопределенному поведению. Последствия варьируются от немедленного краха до скрытых повреждений памяти. Всегда обнуляйте указатели после освобождения или используйте современные методы управления памятью.