Free (ptr) onde ptr é NULL corrompido memória?

112

Teoricamente posso dizer que

free(ptr);
free(ptr); 

é uma corrupção de memória, pois estamos liberando a memória que já foi liberada.

Mas e se

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

Como o SO vai se comportar de maneira indefinida, não consigo fazer uma análise teórica real sobre o que está acontecendo. O que quer que eu esteja fazendo, isso é corrupção de memória ou não?

Liberar um ponteiro NULL é válido?

Vijay
fonte
1
não tenho certeza sobre o padrão livre C, mas em C ++ delete (NULL) é perfeitamente válido, então eu acho que free (NULL) também deveria ser.
Priyank Bolia
14
@Pryank: delete NULLnão é válido em C ++. delete pode ser aplicado a valores de ponteiro nulo de tipo concreto, mas não a NULL. delete (int*) NULLé legal, mas não delete NULL.
AnT
então isso significa que se um ponteiro está apontando para NULL, livre não realiza nada. isso significa !!!!!! toda vez em nossa codificação, se quisermos liberar uma memória, podemos simplesmente substituir um free (ptr) por ptr = NULL?
Vijay
3
Não. Se ptrapontar para a memória e você não chamá free-lo, a memória vazará. Configurá-lo como NULLapenas perde o controle da memória e vaza. Se ptr acontecer de serNULL , chamar freeé uma operação sem operação.
GManNickG
1
@benjamin: Hã? O que você fez para concluir que você pode substituir free(ptr)com ptr = NULL. Ninguém disse nada parecido.
AnT

Respostas:

225

7.20.3.2 A freefunção

Sinopse

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

Descrição

A freefunção faz com que o espaço apontado por ptrseja desalocado, ou seja, disponibilizado para alocação posterior. Se ptrfor um ponteiro nulo, nenhuma ação ocorre.

Consulte ISO-IEC 9899 .

Dito isso, ao observar diferentes bases de código na natureza, você notará que às vezes as pessoas fazem:

if (ptr)
  free(ptr);

Isso ocorre porque alguns tempos de execução C (com certeza lembro que era o caso no PalmOS) travavam ao liberar um NULLponteiro.

Mas hoje em dia, acredito que seja seguro assumir que free(NULL)é um nop de acordo com as instruções do padrão.

Gregory Pakosz
fonte
29
Não, ptr = NULL não substitui o grátis (ptr), ambos são completamente diferentes
Prasoon Saurav
7
NÃO, isso significa que free(ptr)onde ptré nulo não tem efeitos colaterais. Mas em qualquer caso, toda memória alocada usando malloc()ou calloc()deve ser liberada depois usandofree()
Gregory Pakosz
4
ptr = NULL garante que mesmo se você acidentalmente chamar free (ptr), seu programa não falhará em segfault.
Prasoon Saurav
2
Observe que embora o padrão C diga que é um ambiente autônomo, isso não significa que toda biblioteca C o lide dessa forma. Já vi travamentos de graça (NULL), então é melhor evitar chamar o free em primeiro lugar.
Derick
6
@WereWolfBoy, ele quer dizer evitar free(NULL)testando o ponteiro NULLantes de chamarfree()
Gregory Pakosz
22

Todas as versões da biblioteca C em conformidade com os padrões tratam o Grátis (NULL) como um ambiente autônomo.

Dito isso, ao mesmo tempo havia algumas versões do free que travavam no free (NULL), por isso você pode ver algumas técnicas de programação defensiva recomendadas:

if (ptr != NULL)
    free(ptr);
R Samuel Klatchko
fonte
8
-1 [citação necessária]. Mudar o estilo do código devido a alguma teoria de uma implementação arcaica de boatos é uma má ideia.
Tomas
41
@Tomas - Nunca recomendei mudar o estilo, simplesmente expliquei por que você ainda pode ver essa recomendação em alguns estilos.
R Samuel Klatchko
5
@Tomas 3BSD ( winehq.org/pipermail/wine-patches/2006-October/031544.html ) e PalmOS para dois (2ª mão para ambos).
Douglas Leeder
7
@Tomas: o problema estava em coisas como a versão 7 do Unix. Quando eu estava aprendendo, free (xyz) onde xyz == NULL era uma receita para um desastre instantâneo na máquina onde aprendi (ICL Perq rodando PNX, que era baseado na versão 7 do Unix com alguns extras do System III). Mas eu não codifico dessa maneira há muito tempo.
Jonathan Leffler
2
O Netware trava ao liberar o NULL também ... (acabei de depurar uma falha nele ...)
Calmarius
13

Se ptr for NULL, nenhuma operação será executada.

diz a documentação.

Michael Krelin - hacker
fonte
Você quer dizer que o livre não fará nada?
Vijay
2
benjamin, é exatamente o que significa. O que você espera que ele execute se estiver ciente da nulidade do argumento?
Michael Krelin - hacker de
12

Lembro-me de trabalhar no PalmOS onde free(NULL)travou.

jlru
fonte
4
Interessante - isso faz com que uma segunda plataforma (após 3BSD) quebre.
Douglas Leeder
2
Se bem me lembro, no Palm a C Standard Library não existia. Em vez disso, havia um arquivo de cabeçalho sem suporte que mapeava chamadas de biblioteca padrão para o SDK do Palm OS. Muitas coisas agiram inesperadamente. O crash NULLfoi uma das grandes diferenças da caixa de ferramentas Palm em comparação com a biblioteca padrão.
Steven Fisher de
8
free(ptr);
ptr=NULL;
free(ptr);/*This is perfectly safe */

Você pode excluir com segurança um ponteiro NULL. Nenhuma operação será realizada nesse caso. Em outras palavras, free () não faz nada em um ponteiro NULL.

Prasoon Saurav
fonte
8

Uso recomendado:

free(ptr);
ptr = NULL;

Vejo:

man free

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

Quando você define o ponteiro para NULLdepois, free()pode chamá free()-lo novamente e nenhuma operação será realizada.

StefanB
fonte
3
Isso também ajuda a detectar segfaults com um depurador. É evidente que o segfault em p-> do () com p = 0 é alguém usando um ponteiro liberado. Menos evidente quando você vê p = 0xbfade12 no depurador :)
neuro
6

free(NULL)é perfeitamente legal em C, assim como delete (void *)0e delete[] (void *)0é legal em C ++.

BTW, liberar memória duas vezes geralmente causa algum tipo de erro de tempo de execução, então não corrompe nada.

n0
fonte
2
delete 0não é legal em C ++. deleterequer explicitamente uma expressão do tipo ponteiro. É legal aplicar deletea um valor de ponteiro nulo digitado, mas não a 0(e não a NULL).
AnT
1
Você também não pode excluir void*: P Quais destruidores (s) ele deve ser executado?
GManNickG
1
@GMan: Você pode excluir void *contanto que seja um ponteiro nulo.
AnT
Ok, é justo. Esqueci que estamos lidando apenas com null.
GManNickG
geralmente não corrompe nada, mas não é garantido que o faça. ASLR torna isso bastante improvável, mas ainda não impossível: buf1=malloc(X); free(buf1);buf2=malloc(X);free(buf1); - aqui se você não tiver sorte, buf2 obteve exatamente o mesmo endereço que buf1, e você acidentalmente liberou buf1 duas vezes, então no segundo dia livre de buf1 você realmente liberou buf2 silenciosamente, sem casuing qualquer erro / falha / qualquer coisa (imidar). (mas provavelmente você ainda travará na próxima vez que tentar usar buf2 - e este cenário é muito improvável se você estiver executando em ASLR)
hanshenrik
3

free(ptr)é salvo em C se ptrfor NULL, entretanto, o que a maioria das pessoas não sabe é que NULLnão precisa ser igual a 0. Eu tenho um bom exemplo da velha escola: No C64, no endereço 0, há uma porta IO. Se você escreveu um programa em C acessando esta porta, você precisaria de um ponteiro cujo valor é 0. A biblioteca C correspondente teria que distinguir entre 0 e NULLentão.

Atenciosamente.

andi8086
fonte
Fato interessante, me pegou de surpresa. Me fez sentir compelido a fazer uma viagem em torno de perguntas / respostas de ponteiro NULL.
artrópode de
0

não corrupção de memória, mas o comportamento depende da implementação. Por padrão, deve ser um código legal.

Pavel Radzivilovsky
fonte
-3

ptr está apontando para algum local da memória, digamos 0x100.

Quando você libera (ptr), basicamente você está permitindo que 0x100 seja usado pelo gerenciador de memória para ser usado para outra atividade ou processo e, em palavras simples, é a desalocação de recursos.

Quando você executa ptr = NULL, você está fazendo ptr apontar para um novo local (não vamos nos preocupar com o que é NULL). Ao fazer isso, você perdeu o controle dos dados de memória 0x100. Isso é o que é vazamento de memória.

Portanto, não é aconselhável usar ptr = NULL em um ptr válido.

Em vez disso, você pode fazer uma verificação de segurança usando:

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

Quando você libera (ptr) onde ptr já está apontando para NULL, ele não executa nenhuma operação. Portanto, é seguro fazê-lo.

Kishanp
fonte