2010-03-18 23 views
23

En C y C++, liberar un puntero NULL no hará que se haga nada.Liberar memoria dos veces

Aún así, veo a la gente diciendo que la corrupción de memoria puede ocurrir si "el doble de memoria libre".

¿Es esto cierto? ¿Qué está pasando debajo del capó cuando liberas memoria dos veces?

+3

No se libera memoria cuando libera un puntero nulo. Si libera un puntero * no nulo * dos veces, la memoria se libera dos veces, y eso es un problema. – jalf

Respuesta

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. 

Así que, después free ing primera vez, usted debe hacer p = NULL, así que si (por casualidad), free(p) se llama de nuevo, no pasará nada.

Aquí es la razón por la liberación de memoria dos veces no está definido: Why free crashes when called twice

+6

+1 por la corrección de la respuesta, incluso si no estoy de acuerdo en configurar el puntero a NULL inmediatamente después, eso podría ocultar un error permanente (llamar 'free' la segunda vez) y prefiero que falle duro y rápido para que el error puede ser manejado que escondido. –

+0

@David: Estoy totalmente de acuerdo, si 'free' se llama dos veces en un puntero, es una implementación (algo) mala. –

+1

@David: Esa es una buena estrategia siempre que sepa cómo depurar situaciones 'libres-dos veces'. Si no lo haces, preferiría anular el puntero justo después de liberarlo y comprobar su nulidad justo antes de cada declaración 'libre'. –

9

Este es un comportamiento indefinido, que pueden dar lugar a daños en la pila u otras consecuencias graves.

free() para un puntero nulo simplemente comprueba el valor del puntero en el interior, y vuelve. Esa comprobación no ayudará a liberar un bloque dos veces.

Esto es lo que sucede habitualmente. La implementación del montón recibe la dirección e intenta "apropiarse" del bloque en esa dirección modificando sus propios datos de servicio. Dependiendo de la implementación del montón, cualquier cosa puede suceder. Tal vez funcione y no pase nada, tal vez los datos del servicio estén corruptos y tengas corrupción del montón.

Así que no lo hagas. Es un comportamiento indefinido. Cualquier cosa mala que pueda pasar

+0

También diría explícitamente que 'liberar' un puntero no cambia (neceaarily) su valor y, en general, no es nulo después del 'libre' (por eso el segundo 'libre' causa corrupción). – Ari

4

Sí, "un comportamiento indefinido", que casi siempre resulta en un accidente. (aunque el "comportamiento indefinido" significa "cualquier cosa", varios tipos de errores suelen comportarse de forma bastante predecible. En el caso de free(), el comportamiento invariablemente es segfault o la característica respectiva de "error de protección de memoria" en el SO).

mismo si libre() un puntero a cualquier otra cosa de NULL o algo que malloc'd.

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

+0

+1 Crash 101 allí. – kenny

+3

Eres demasiado optimista sobre los resultados del comportamiento indefinido. –

+0

Estoy con Roger.De hecho, liberar dos veces casi nunca resulta en un colapso en el punto en que se produce la libertad, lo que lo convierte en uno de los errores más difíciles de rastrear. –

2

libre() libera el espacio de memoria señaló por ptr, que debe haber sido devuelto por una llamada previa a malloc(), calloc() o realloc(). De lo contrario, o si se ha llamado a ( ) antes, se produce un comportamiento indefinido de . Si ptr es NULO, no se realiza ninguna operación.

Por lo tanto, obtendrá un comportamiento indefinido, y podría pasar cualquier cosa.

1

1) Manipulación de la memoria dinámica no es realizado por el compilador. Hay bibliotecas en tiempo de ejecución que se encargan de esto. Por ej. : glibc proporciona APIs como malloc y free, que realizan internamente llamadas al sistema (sys_brk) para manejar el área del montón.

2) Liberar la misma memoria dos veces se refiere a una condición como esta: Supongamos que tiene char * cptr;

Se asigna memoria usando: cptr = (char *) malloc (SIZE);

Ahora, cuando ya no necesite esta memoria, puede liberarla usando esto: libre (cptr);

Ahora, lo que ocurre es que la memoria apuntada por cptr es gratuita.

Supongamos que en un momento posterior del programa vuelve a llamar a un servicio gratuito (cptr), entonces esta no es una condición válida. Este escenario en el que está liberando la misma memoria dos veces se conoce como problema de "liberar una memoria dos veces".

19

La memoria de liberación no establece el puntero en nulo. El puntero sigue apuntando a la memoria que solía tener, pero que ahora se ha transferido la propiedad nuevamente al heap manager.

El heap manager puede haber reasignado desde entonces la memoria a la que apunta su antiguo puntero.

Liberarlo de nuevo no es lo mismo que decir free(NULL), y dará como resultado un comportamiento indefinido.

3

Cuando llama libre en un puntero, su puntero no se establecerá en NULL. El espacio libre solo se devuelve a un grupo para estar disponible para la asignación nuevamente. Aquí un ejemplo de prueba:

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

Este programa para mí:

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

y se puede ver, que el libre no hicieron nada para el puntero, pero sólo dijo que el sistema que la memoria está disponible para reutilizar.

4

Para evitar libre dos veces i todos los días usando MACRO para la memoria libre:

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

este conjunto macro p = NULL para evitar colgando puntero.

+2

Este es un truco agradable pero prefiero tratar de arreglar la causa real. Si se llama a free() dos veces en la misma variable, esto es claramente un error. – user206268

0

Liberar memoria más de una vez puede tener consecuencias negativas. Puede ejecutar esta pieza de código para ver qué puede pasar con su computadora.

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

Muchas bibliotecas estándar como CSparse utilizan una función de contenedor que se encarga de los problemas de memoria. copié la función aquí:

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

    } 

Esta función puede manejar los problemas con la memoria. Tenga en cuenta que debe tener en cuenta la condición de que malloc devuelve NULL en algunos casos