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

В C и C++, Освобождая Нулевого указателя не приведет ни к чему сделанному.

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

Действительно ли это верно? Что продолжается под капотом когда Вы свободная память дважды?

23
задан Eric Leschinski 13 June 2014 в 20:31
поделиться

8 ответов

int *p = malloc(sizeof(int));
//value of p is now lets say 0x12345678

*p = 2;
free(p); //memory pointer is freed, but still value of p is 0x12345678
         //now, if you free again, you get a crash or undefined behavior.

Итак, после free в первый раз,вы должны сделать p = NULL , поэтому, если (по какой-либо случайности) free(p) будет вызван снова, ничего не произойдет.

Вот почему освобождение памяти дважды не определено: Почему свободные сбои при вызове дважды

23
ответ дан 29 November 2019 в 01:04
поделиться

free () освобождает пространство памяти, на которое указывает ptr, которое должно было быть возвращено предыдущим вызовом malloc (), calloc () или realloc ( ). В противном случае, или если free (ptr) уже был вызван ранее, возникает неопределенное поведение . Если ptr равен NULL, никакая операция не выполняется.

Итак, вы получаете неопределенное поведение, и все может случиться.

2
ответ дан 29 November 2019 в 01:04
поделиться

Да, «неопределенное поведение», которое почти всегда приводит к сбою. (хотя «неопределенное поведение» по определению означает «что угодно», различные типы ошибок часто ведут себя вполне предсказуемым образом. В случае бесплатного (), поведение неизменно является segfault или соответствующей характеристикой «ошибки защиты памяти» для ОС.)

То же самое, если вы free () укажете на что-либо еще, кроме NULL или что-то, что вы malloc'd.

char x; символ * p = & x; free (p); // сбой.

4
ответ дан 29 November 2019 в 01:04
поделиться

Во избежание двойного освобождения я всегда использую МАКРОС для освобождения памяти:

#ifdef FREEIF
# undef FREEIF
#endif
#define FREEIF( _p )  \
if( _p )              \
{                     \
        free( _p );   \
        _p = NULL;    \
}

этот макрос устанавливает p = NULL, чтобы избежать зависания указателя.

4
ответ дан 29 November 2019 в 01:04
поделиться

Когда вы вызываете free для указателя, ваш указатель не будет установлен в NULL. Свободное пространство возвращается в пул только для того, чтобы снова стать доступным для распределения. Вот пример для тестирования:

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

int main(){
    int* ptr = (int*)malloc(sizeof(int));
    printf("Address before free: %p\n", ptr);
    free(ptr);
    printf("Address after free: %p\n", ptr);
    return 0;
}

Эта программа выводит для меня:

Address before free: 0x950a008
Address after free: 0x950a008

, и вы можете видеть, что free ничего не сделал с указателем, а только сообщил системе, что память доступна для повторного использования.

3
ответ дан 29 November 2019 в 01:04
поделиться

Это неопределенное поведение, которое может привести к повреждению кучи или другим серьезным последствиям.

free () для нулевого указателя просто проверяет значение указателя внутри и возвращает. Эта проверка не поможет против освобождения блока дважды.

Вот что обычно происходит. Реализация кучи получает адрес и пытается «стать владельцем» блока по этому адресу, изменяя свои собственные служебные данные. В зависимости от реализации кучи может случиться что угодно. Может быть, это работает и ничего не происходит, возможно, данные службы повреждены и у вас повреждена куча.

Так что не делай этого. Это неопределенное поведение. Что бы ни случилось плохого.

9
ответ дан 29 November 2019 в 01:04
поделиться

1) Обработка динамической памяти не выполняется компилятором. Существуют библиотеки времени выполнения, которые заботятся об этом. Например, glibc предоставляет API, такие как malloc и free, которые внутри делают системные вызовы (sys_brk) для обработки области кучи.

2) Освобождение одной и той же памяти дважды относится к такому состоянию: Предположим, у вас есть char *cptr;

Вы выделяете память, используя : cptr = (char *) malloc (SIZE);

Теперь, когда вам больше не нужна эта память, вы можете освободить ее, используя следующее : free(cptr);

Теперь происходит то, что память, на которую указывает cptr, освобождается для использования.

Предположим, что в более поздний момент времени в программе вы снова вызовете free(cptr), тогда это не является допустимым условием. Этот сценарий, когда вы дважды освобождаете одну и ту же память, известен как проблема "освобождения памяти дважды".`

1
ответ дан 29 November 2019 в 01:04
поделиться

Освобождение памяти не приводит к обнулению указателя. Указатель по-прежнему указывает на память, которой он раньше владел, но теперь право собственности передано обратно диспетчеру кучи.

Менеджер кучи мог с тех пор перераспределить память, на которую указывает устаревший указатель.

Повторное освобождение - это не то же самое, что сказать бесплатно (NULL) , и приведет к неопределенному поведению.

19
ответ дан 29 November 2019 в 01:04
поделиться