Czy free (ptr), gdzie ptr to NULL uszkodzona pamięć?

112

Teoretycznie mogę to powiedzieć

free(ptr);
free(ptr); 

to uszkodzenie pamięci, ponieważ zwalniamy pamięć, która została już zwolniona.

Ale co gdyby

free(ptr);
ptr=NULL;
free(ptr); 

Ponieważ system operacyjny będzie zachowywał się w nieokreślony sposób, nie mogę uzyskać faktycznej teoretycznej analizy tego, co się dzieje. Cokolwiek robię, czy to uszkodzenie pamięci, czy nie?

Czy zwolnienie wskaźnika NULL jest prawidłowe?

Vijay
źródło
1
nie jestem pewien co do standardu C, ale w C ++ metoda delete (NULL) jest całkowicie poprawna, więc myślę, że wolny (NULL) również powinien być.
Priyank Bolia
14
@Pryank: delete NULLnie działa w C ++. delete można zastosować do wartości wskaźnika null określonego typu, ale nie do NULL. delete (int*) NULLjest legalne, ale nie delete NULL.
AnT
więc oznacza to, że jeśli wskaźnik wskazuje na NULL free, nic nie wykonuje. czy to oznacza !!!!!! za każdym razem w naszym kodowaniu, jeśli chcesz zwolnić pamięć, możesz po prostu zamienić free (ptr) na ptr = NULL?
Vijay
3
Nie. Jeśli ptrwskazuje na pamięć, a ty jej nie wywołasz free, to pamięć wycieknie. Ustawienie go NULLtak, aby po prostu utraciło dostęp do pamięci i przecieki. Jeśli ptr tak się stanieNULL , wywołanie freejest bez operacji.
GManNickG
1
@benjamin: Hę? Co skłoniło Cię do wniosku, że można zastąpić free(ptr)z ptr = NULL. Nikt nic takiego nie powiedział.
AnT

Odpowiedzi:

225

7.20.3.2 freeFunkcja

Streszczenie

#include <stdlib.h> 
void free(void *ptr); 

Opis

freeFunkcja powoduje miejsca wskazywanego przez ptrbyć zwalniane, czyli udostępnione do dalszej alokacji. Jeśli ptrjest pustym wskaźnikiem, żadna akcja nie jest wykonywana.

Zobacz ISO-IEC 9899 .

Biorąc to pod uwagę, patrząc na różne bazy kodów na wolności, zauważysz, że ludzie czasami robią:

if (ptr)
  free(ptr);

Dzieje się tak, ponieważ niektóre środowiska wykonawcze C (na pewno pamiętam, że miało to miejsce na PalmOS) ulegały awarii podczas zwalniania NULLwskaźnika.

Ale obecnie uważam, że można bezpiecznie założyć, że free(NULL)jest to nop zgodnie z instrukcją zawartą w normie.

Gregory Pakosz
źródło
29
Nie, ptr = NULL nie jest w żaden sposób zamiennikiem za darmo (ptr), oba są zupełnie inne
Prasoon Saurav
7
NIE, oznacza to, że free(ptr)gdzie ptrjest zero, nie ma skutków ubocznych. Ale w każdym razie każda pamięć przydzielona przy użyciu malloc()lub calloc()musi zostać zwolniona później za pomocąfree()
Gregory Pakosz
4
ptr = NULL zapewnia, że ​​nawet jeśli przypadkowo wywołasz free (ptr), Twój program nie przejdzie do segfaultów.
Prasoon Saurav
2
Proszę zauważyć, że chociaż standard C mówi, że nie jest to operacja, nie oznacza to, że każda biblioteka C obsługuje to w ten sposób. Widziałem awarie za darmo (NULL), więc w pierwszej kolejności najlepiej unikać dzwonienia pod numer bezpłatny.
Derick,
6
@WereWolfBoy ma na myśli unikanie free(NULL), testując wskaźnik NULLprzed wezwaniemfree()
Gregory Pakosz
22

Wszystkie zgodne ze standardami wersje biblioteki C traktują darmową (NULL) jako no-op.

To powiedziawszy, kiedyś istniało kilka wersji darmowych, które ulegały awarii na darmowym (NULL), dlatego możesz zobaczyć niektóre obronne techniki programowania:

if (ptr != NULL)
    free(ptr);
R Samuel Klatchko
źródło
8
-1 [potrzebne źródło]. Zmiana stylu kodu ze względu na jakąś teorię archaicznej implementacji pogłosek to zły pomysł.
Tomas
41
@Tomas - Nigdy nie polecałem zmiany stylu, po prostu wyjaśniłem, dlaczego nadal możesz zobaczyć tę rekomendację w niektórych stylach.
R Samuel Klatchko
5
@Tomas 3BSD ( winehq.org/pipermail/wine-patches/2006-October/031544.html ) i PalmOS dla dwojga (drugie rozdanie dla obu).
Douglas Leeder
7
@Tomas: problem dotyczył rzeczy takich jak wersja 7 Unix. Kiedy się uczyłem, free (xyz), gdzie xyz == NULL było przepisem na natychmiastową katastrofę na maszynie, na której się uczyłem (ICL Perq z PNX, który był oparty na wersji 7 Unix z kilkoma dodatkami z Systemu III). Ale od dawna nie koduję w ten sposób.
Jonathan Leffler
2
Netware również ulega awarii podczas
uwalniania
13

Jeśli ptr ma wartość NULL, nie jest wykonywana żadna operacja.

mówi dokumentacja.

Michael Krelin - haker
źródło
czy masz na myśli to, że taht free nic nie wykona?
Vijay
2
benjamin, właśnie to oznacza. Czego spodziewałbyś się, że wykona, jeśli jest świadomy nieważności argumentu?
Michael Krelin - haker
12

Pamiętam, jak pracowałem na PalmOS, gdzie się free(NULL)zawiesił.

jlru
źródło
4
Ciekawe - to sprawia, że ​​druga platforma (po 3BSD) ulega awarii.
Douglas Leeder
2
Jeśli dobrze pamiętam, na Palm nie istniała standardowa biblioteka C. Zamiast tego istniał w większości nieobsługiwany plik nagłówkowy, który mapował wywołania biblioteki standardowej do pakietu Palm OS SDK. Wiele rzeczy zadziałało nieoczekiwanie. Awaria NULLbyła jedną z największych różnic w działaniu zestawu narzędzi Palm w porównaniu ze standardową biblioteką.
Steven Fisher
8
free(ptr);
ptr=NULL;
free(ptr);/*This is perfectly safe */

Możesz bezpiecznie usunąć wskaźnik NULL. W takim przypadku żadna operacja nie zostanie wykonana, innymi słowy free () nie robi nic na wskaźniku NULL.

Prasoon Saurav
źródło
8

Zalecane użycie:

free(ptr);
ptr = NULL;

Widzieć:

man free

     The free() function deallocates the memory allocation pointed to by ptr.
     If ptr is a NULL pointer, no operation is performed.

Jeśli ustawisz wskaźnik na NULLafter free(), możesz free()go ponownie wywołać i żadna operacja nie zostanie wykonana.

stefanB
źródło
3
Pomaga to również w wykrywaniu segfaultów za pomocą debuggera. Jest oczywiste, że segfault w p-> do () z p = 0 to ktoś używający zwolnionego wskaźnika. Mniej widoczne, gdy zobaczysz p = 0xbfade12 w debugerze :)
neuro
6

free(NULL)jest całkowicie legalny w C, jak również delete (void *)0i delete[] (void *)0jest legalny w C ++.

Przy okazji, dwukrotne zwolnienie pamięci zwykle powoduje jakiś błąd w czasie wykonywania, więc niczego nie psuje.

n0rd
źródło
2
delete 0nie jest legalne w C ++. deletejawnie wymaga wyrażenia typu wskaźnika. Dozwolone jest stosowanie deletedo wpisanej wartości wskaźnika null, ale nie do 0(i nie do NULL).
AnT
1
Nie możesz usunąć void*: P Które destruktory ma działać?
GManNickG,
1
@GMan: Użytkownik może usuwać void *tak długo, jak jest to null pointer.
AnT
Ok, w porządku. Zapomniałem, że mamy do czynienia tylko z null.
GManNickG
zazwyczaj niczego nie psuje, ale nie ma gwarancji. ASLR sprawia, że ​​jest to raczej mało prawdopodobne, ale nadal nie jest to niemożliwe: buf1=malloc(X); free(buf1);buf2=malloc(X);free(buf1); - tutaj, jeśli masz pecha, buf2 ma dokładnie ten sam adres, co buf1 i przypadkowo zwolniłeś buf1 dwa razy, więc na drugim miejscu wolnym od buf1 faktycznie zwolniłeś buf2 po cichu, bez casuowania jakikolwiek (natychmiastowy) błąd / awaria / cokolwiek. (ale prawdopodobnie nadal będziesz mieć awarię następnym razem, gdy spróbujesz użyć buf2 - a ten scenariusz jest bardzo mało prawdopodobny, jeśli używasz ASLR)
hanshenrik
3

free(ptr)jest zapisany w C, jeśli ptrjest NULL, jednak większość ludzi nie wie, że NULLnie musi być równa 0. Mam ładny, oldschoolowy przykład: na C64, pod adresem 0, znajduje się port IO. Jeśli napisałeś program w C, który uzyskuje dostęp do tego portu, potrzebujesz wskaźnika o wartości 0. Odpowiednia biblioteka C musiałaby rozróżniać od 0 do tego portu NULL.

Z poważaniem.

andi8086
źródło
Ciekawostka, zaskoczyła mnie. Sprawiło, że poczułem się zmuszony wybrać się na wycieczkę po NULL wskaźnikowych pytaniach / odpowiedziach.
stawonogi
0

nie uszkodzenie pamięci, ale zachowanie zależy od implementacji. Standardowo powinien to być kod prawny.

Pavel Radzivilovsky
źródło
-3

ptr wskazuje na jakąś lokalizację w pamięci, powiedzmy 0x100.

Kiedy zwalniasz (ptr), w zasadzie pozwalasz, aby 0x100 był używany przez menedżera pamięci do innych działań lub procesów, a mówiąc prosto, jest to zwolnienie zasobów.

Kiedy robisz ptr = NULL, ustawiasz ptr na nową lokalizację (nie martwmy się tym, czym jest NULL). W ten sposób straciłeś kontrolę nad danymi pamięci 0x100. To właśnie jest wyciek pamięci.

Dlatego nie jest zalecane używanie ptr = NULL na prawidłowym ptr.

Zamiast tego możesz przeprowadzić bezpieczną kontrolę, używając:

if (ptr! = NULL) {free (ptr);}

Kiedy zwolnisz (ptr), gdzie ptr już wskazuje na NULL, nie wykonuje żadnej operacji, więc jest to bezpieczne.

kishanp
źródło