2010-03-18 19 views
23

W językach C i C++ zwolnienie wskaźnika NULL spowoduje, że nic się nie stanie.Dwukrotne zwalnianie pamięci

Mimo to, widzę ludzi, którzy mówią, że korupcja pamięci może wystąpić, jeśli „wolnej pamięci dwa razy”.

Czy to prawda? Co się dzieje pod maską, kiedy dwa razy zwalniasz pamięć?

+3

Po zwolnieniu wskaźnika pustego nie zwalnia żadnej pamięci. Jeśli dwa razy zwolnisz * niepustą * wskaźnik, pamięć zostanie zwolniona dwukrotnie, a to jest problem. – jalf

Odpowiedz

22
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. 

Tak, po free ing po raz pierwszy, należy zrobić p = NULL, więc jeśli (przypadkiem), free(p) nazywa się ponownie, nic się nie stanie.

Oto dlaczego uwalniając pamięć dwukrotnie jest niezdefiniowany: Why free crashes when called twice

+6

+1 za poprawność odpowiedzi, nawet jeśli nie zgadzam się przy ustawianiu wskaźnika na NULL zaraz po, który mógłby ukryć stały błąd (wywołanie 'free' po raz drugi) i wolałbym, żeby to zawodziło mocno i szybko, aby błąd może być obsługiwany niż ukryty. –

+0

@David: Całkowicie zgadzam się, jeśli 'free' nazywa się dwukrotnie na wskaźnik, to jest (nieco) złe wdrożenie. –

+1

@David: To dobra strategia, o ile wiesz, jak debugować sytuacje "free-twice". Jeśli tego nie zrobisz, wolałbym unieważnić wskaźnik zaraz po zwolnieniu i sprawdzić go pod kątem zerowej wartości tuż przed każdym "wolnym" stwierdzeniem. –

9

To niezdefiniowane zachowanie, które mogą spowodować uszkodzenie sterty lub innymi poważnymi konsekwencjami.

free() dla null pointer po prostu sprawdza wartość wskaźnika wewnątrz i powroty. Ta kontrola nie pomoże w dwukrotnym zwolnieniu bloku.

Oto, co dzieje się zwykle. Implementacja sterty pobiera adres i próbuje "przejąć własność" bloku pod tym adresem, modyfikując własne dane usługi. W zależności od implementacji sterty wszystko może się zdarzyć. Może to działa i nic się nie dzieje, może dane usługi są uszkodzone i masz zepsucie sterty.

Więc nie rób tego. To niezdefiniowane zachowanie. Cokolwiek złego może się wydarzyć.

+0

Powiedziałbym też wyraźnie, że "swobodny" wskaźnik nie zmienia (niby) jego wartości i ogólnie nie jest zerowy po "wolnym" (dlatego drugi "wolny" powoduje korupcję). – Ari

4

Tak „niezdefiniowane zachowanie”, które prawie zawsze prowadzi do katastrofy. (podczas gdy "niezdefiniowane zachowanie" z definicji oznacza "cokolwiek", różne typy błędów często zachowują się w dość przewidywalny sposób, w przypadku wolnej() zachowanie jest niezmiennie uszkodzeniem lub odpowiednim "charakterystycznym błędem ochrony pamięci" systemu operacyjnego).

samo jeśli free() wskaźnik do niczego innego niż NULL lub coś ty malloc'd.

char x; char* p=&x; free(p); // zderzeniowych.

+0

+1 Awaria 101 tam. – kenny

+3

Jesteś zbyt optymistyczny wobec wyników niezdefiniowanego zachowania. –

+0

Jestem z Rogerem.W rzeczywistości uwolnienie czegoś dwa razy prawie nigdy nie skutkuje awarią w momencie, w którym odbywa się swoboda, co czyni go jednym z najtrudniejszych błędów do wyśledzenia. –

2

free() zwalnia obszar pamięci wskazywany przez ptr , które muszą zostały zwrócony przez poprzednie wywołanie malloc(), calloc() lub realloc(). W przeciwnym razie lub jeśli wolna (ptr) już została wywołana , pojawia się niezdefiniowane zachowanie . Jeśli ptr ma wartość NULL, żadna operacja nie zostanie wykonana.

Otrzymujesz niezdefiniowane zachowanie i wszystko może się zdarzyć.

1

1) Postępowanie z pamięci dynamicznej nie jest wykonywana przez kompilator. Istnieją biblioteki uruchomieniowe, które się tym zajmują. Na przykład : glibc zapewnia interfejsy API takie jak malloc i free, które wewnętrznie wywołują systemowe wywołania (sys_brk), aby obsłużyć obszar sterty.

2) Zwolnienie tej samej pamięci dwukrotnie odnosi się do takiego stanu: Załóżmy, że masz char * cptr;

Przydzielanie pamięci za pomocą: cptr = (char *) malloc (SIZE);

Teraz, gdy nie potrzebujesz już tej pamięci, możesz ją zwolnić, używając: free (cptr);

Teraz tutaj, co się dzieje, pamięć wskazywana przez cptr jest darmowa.

Załóżmy, że w późniejszym czasie programu ponownie zadzwonisz za darmo (cptr), to nie jest poprawny warunek. Ten scenariusz, w którym dwa razy zwalnia się tę samą pamięć, nazywany jest "dwukrotnym uwolnieniem pamięci". "

19

Zwolnienie pamięci nie ustawia wskaźnika na wartość null. Wskaźnik nadal wskazuje na pamięć, która kiedyś była jego własnością, ale która ma teraz własność przeniesioną z powrotem do menedżera sterty.

Menedżer sterty może mieć, ponieważ ponownie przydzielono pamięć, na którą wskazuje nieaktualny wskaźnik.

Zwolnienie go ponownie nie jest tym samym, co powiedzenie free(NULL) i spowoduje niezdefiniowane zachowanie.

3

Po wywołaniu za darmo na wskaźniku, wskaźnik nie zostanie ustawiony na wartość NULL. Wolna przestrzeń jest zwrócona do puli, aby ponownie mogła zostać przydzielona. Oto przykład do testu:

wyjścia
#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; 
} 

Ten program dla mnie:

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

i widać, że za darmo nie zrobił nic do wskaźnika, ale tylko powiedział systemu, że pamięć jest dostępna dla ponowne użycie.

4

Aby uniknąć wolne Dwukrotnie sprecyzowane pomocą makra dla wolnej pamięci:

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

Makro zestaw s = NULL uniknąć zwisające wskaźnik.

+2

To jest fajny hack, ale wolę spróbować naprawić rzeczywistą przyczynę. Jeśli free() zostanie wywołany dwukrotnie na tej samej zmiennej, jest to oczywiście błąd. – user206268

0

Zwolnienie pamięci więcej niż jeden raz może mieć złe konsekwencje. Możesz uruchomić ten fragment kodu, aby zobaczyć, co może się stać z twoim komputerem.

#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; 
} 

Wiele standardowych bibliotek jak CSparse użyć funkcji otoki, który obsługuje problemów z pamięcią. skopiowane funkcję tutaj:

/* 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  

    } 

Ta funkcja może obsłużyć problemy z pamięcią. Pamiętaj, że musisz zadbać o to, aby malloc zwrócił NULL w niektórych przypadkach:

Powiązane problemy