Освобождая память дважды - PullRequest
23 голосов
/ 18 марта 2010

В C и C ++ освобождение указателя NULL ничего не даст.

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

Это правда? Что происходит под капотом, когда вы дважды освобождаете память?

Ответы [ 9 ]

23 голосов
/ 18 марта 2010
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) будет вызван снова, ничего не произойдет.

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

19 голосов
/ 18 марта 2010

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

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

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

9 голосов
/ 18 марта 2010

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

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

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

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

4 голосов
/ 18 марта 2010

Чтобы избежать бесплатного дважды, я всегда использую MACRO для свободной памяти:

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

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

4 голосов
/ 18 марта 2010

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

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

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

3 голосов
/ 18 марта 2010

Когда вы вызываете 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 ничего не сделал с указателем, а только сообщил системе, что память доступна для повторного использования.

2 голосов
/ 18 марта 2010

free () освобождает указанную область памяти к ptr, который должен был возвращено предыдущим звонком malloc (), calloc () или realloc (). В противном случае, или если свободный (ptr) уже был вызван ранее, неопределенный поведение происходит. Если ptr НЕДЕЙСТВИТЕЛЕН, никакая операция не выполнена.

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

1 голос
/ 18 марта 2010

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

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

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

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

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

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

0 голосов
/ 06 августа 2015

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

#include <stdio.h>      /* printf, scanf, NULL */
#include <stdlib.h>     /* malloc, free, rand */

int main ()


  {
  int i,n;
  char * buffer;

  printf ("How long do you want the string? ");
  scanf ("%d", &i);

  buffer = (char*) malloc (i+1);
  if (buffer==NULL) exit (1);

  for (n=0; n<i; n++)
          buffer[n]=rand()%26+'a';
  buffer[i]='\0';

  printf ("Random string: %s\n",buffer);
  free (buffer);
  free (buffer);

  return 0;
}

Многие стандартные библиотеки, такие как CSparse, используют функцию-оболочку, которая обрабатывает проблемы с памятью. Я скопировал функцию здесь:

 /* wrapper for free */
    void *cs_free (void *p)
    {
        if (p) free (p) ;       /* free p if it is not already NULL */
        return (NULL) ;         /* return NULL to simplify the use of    

    }

Эта функция может обрабатывать проблемы с памятью. Обратите внимание, что вы должны позаботиться о том, чтобы malloc в некоторых случаях возвращала NULL

...