c++ - programacion - Liberando la memoria dos veces
manual de programacion android pdf (9)
free () libera el espacio de memoria apuntado por ptr, que debe haber sido devuelto por una llamada previa a malloc (), calloc () o realloc (). De lo contrario, o si ya se ha llamado antes a (ptr), se produce un comportamiento no definido. Si ptr es NULL, no se realiza ninguna operación.
Entonces, obtienes un comportamiento indefinido, y cualquier cosa podría pasar.
En C y C ++, liberar un puntero NULL no dará como resultado nada.
Aún así, veo gente diciendo que la corrupción de memoria puede ocurrir si "liberas memoria dos veces".
¿Es esto cierto? ¿Qué está pasando debajo del capó cuando liberas memoria dos veces?
1) El compilador no realiza el manejo de la memoria dinámica. 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;
Usted asigna memoria usando: cptr = (char *) malloc (SIZE);
Ahora, cuando ya no necesite esta memoria, puede liberarla usando esto: free (cptr);
Ahora, aquí lo que sucede es que la memoria apuntada por cptr es gratuita.
Supongamos que en un punto 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".
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 para probar:
#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 produce para mí:
Address before free: 0x950a008
Address after free: 0x950a008
y puede ver que, gratis, no le hizo nada al puntero, pero solo le dijo al sistema que la memoria está disponible para su reutilización.
Este es un comportamiento indefinido, que puede provocar corrupción en el montón u otras consecuencias graves.
free()
para un puntero nulo simplemente comprueba el valor del puntero dentro y regresa. Esa comprobación no ayudará a liberar un bloque dos veces.
Esto es lo que sucede generalmente. 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.
Entonces no lo hagas Es un comportamiento indefinido. Cualquier cosa mala que pueda pasar
Liberar memoria más de una vez puede tener malas consecuencias. 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 usan una función de contenedor que maneja 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
Liberar memoria no establece el puntero a 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 la memoria a la que apunta su puntero obsoleto.
Liberarlo de nuevo no es lo mismo que decir free(NULL)
, y dará como resultado un comportamiento indefinido.
Para evitar gratis dos veces siempre uso MACRO para memoria libre:
#ifdef FREEIF
# undef FREEIF
#endif
#define FREEIF( _p ) /
if( _p ) /
{ /
free( _p ); /
_p = NULL; /
}
esta macro establece p = NULL para evitar el puntero colgante.
Sí, "comportamiento indefinido" que casi siempre resulta en un bloqueo. (Mientras que "comportamiento indefinido" significa "cualquier cosa", varios tipos de errores a menudo se comportan de manera bastante predecible. En el caso de free (), el comportamiento invariablemente es segfault o respectivo "error de protección de memoria" característico del SO).
Lo mismo si liberas () un puntero a cualquier cosa que no sea NULL o algo que hayas mallocrado.
char x; char* p=&x; free(p);
// bloqueo.
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.
Entonces, después de free
la primera vez, debe hacer p = NULL
, entonces si (por casualidad), se llama de nuevo a free(p)
, no pasará nada.
Esta es la razón por la cual no se define la liberación de la memoria dos veces: ¿Por qué se bloquea gratis cuando se llama dos veces?