No se permiten llamar free
en la memoria no asignada, la norma indica que con toda claridad (ligeramente parafraseados, el subrayado es mío):
La función free
hace que el espacio apuntado por su argumento para cancelar la asignación, es decir, disponible para una asignación adicional. Si el argumento es un puntero nulo, no se produce ninguna acción. De lo contrario, si el argumento no coincide con un puntero devuelto anteriormente por una función de gestión de memoria, o si el espacio ha sido desasignado por una llamada a free o realloc, el comportamiento es indefinido.
¿Qué ocurre, por ejemplo, si la dirección que es de doble liberación se ha reasignado en medio de un nuevo bloque y el código que asigna que acaba de pasar a almacenar algo allí que parecía una verdadera malloc -block encabezado? Me gusta:
+- New pointer +- Old pointer
v v
+------------------------------------+
| <Dodgy bit> |
+------------------------------------+
Caos, eso es qué.
Las funciones de asignación de memoria son una herramienta como una motosierra y, si las usa correctamente, no debería tener problemas. Si se les hace mal uso, sin embargo, las consecuencias son su propia culpa, ya sea de memoria que se dañe o peor, o cortar uno de sus brazos :-)
Y respecto al comentario:
.. .puede comunicarse con gracia al usuario final sobre la duplicación de la misma ubicación libre.
corto de mantener un registro de todas las llamadas malloc
y free
para asegurarse de que no lo haga doble libre de un bloque, no puedo ver esto como siendo viable. Se requeriría una gran sobrecarga y todavía no solucionaría todos los problemas.
¿Qué pasaría si:
- hilo Una asignado y liberado memoria en la dirección 42.
- hilo B memoria asignada una dirección 42 y empezado a utilizar.
- thread A liberó esa memoria por segunda vez.
- hilo C asignó a la memoria una dirección 42 y comenzó a usarla.
Luego tiene los hilos B y C, ambos pensando que son dueños de esa memoria (no tienen que ser hilos de ejecución, estoy usando el término hilo aquí como una pieza de código que se ejecuta, podría todos están en el único hilo de ejecución pero se llaman secuencialmente).
No, creo que la actual malloc
y free
están bien siempre que las use correctamente. Por supuesto piense en implementar su propia versión, no veo nada de malo en eso, pero sospecho que se encontrará con algunos problemas de rendimiento bastante espinosos.
Si hace quieren poner en práctica su propia envoltura alrededor free
, puede que sea más seguro (a costa de un pequeño impacto de rendimiento), específicamente con algo así como los myFreeXxx
llamadas siguientes:
#include <stdio.h>
#include <stdlib.h>
void myFreeVoid (void **p) { free (*p); *p = NULL; }
void myFreeInt (int **p) { free (*p); *p = NULL; }
void myFreeChar (char **p) { free (*p); *p = NULL; }
int main (void) {
char *x = malloc (1000);
printf ("Before: %p\n", x);
myFreeChar (&x);
printf ("After: %p\n", x);
return 0;
}
el resultado del código es que se puede llamar myFreeXxx
con un puntero al puntero y lo hará tanto:
- libera la memoria; y
- establece el puntero a NULL.
Este último bit significa que, si intenta liberar el puntero nuevamente, no hará nada (porque la liberación de NULL está específicamente cubierta por el estándar).
Se se no le protegerá de todas las situaciones, como por ejemplo si se hace una copia del puntero en otro lugar, liberar la original, y luego liberar la copia:
char *newptr = oldptr;
myFreeChar (&oldptr); // frees and sets to NULL.
myFreeChar (&newptr); // double-free because it wasn't set to NULL.
Si' Para usar C11, hay una manera mejor que tener que llamar explícitamente a una función diferente para cada tipo ahora que C tiene una sobrecarga de función de tiempo de compilación. Se puede utilizar selecciones genéricas a llamar a la función correcta al tiempo que permite la seguridad de tipos:
#include <stdio.h>
#include <stdlib.h>
void myFreeVoid (void **p) { free (*p); *p = NULL; }
void myFreeInt (int **p) { free (*p); *p = NULL; }
void myFreeChar (char **p) { free (*p); *p = NULL; }
#define myFree(x) _Generic((x), \
int** : myFreeInt, \
char**: myFreeChar, \
default: myFreeVoid )(x)
int main (void) {
char *x = malloc (1000);
printf ("Before: %p\n", x);
myFree (&x);
printf ("After: %p\n", x);
return 0;
}
Con eso, sólo tiene que llamar myFree
y va a seleccionar la función correcta en función del tipo.
¿Se puede publicar un fragmento de código donde se produce el error? – ChadNC
¿Por qué el voto a favor? No estoy seguro de si es una estafa, pero esta es una muy buena pregunta. –
C es un lenguaje que valora la velocidad y el control del programador sobre la seguridad garantizada. Se espera que desarrolles hábitos que te mantengan a salvo en lugar de hacer cosas al azar y esperar ser atrapado.Incluso si fuera posible hacer la contabilidad que desea, sería más lento, y no quiero pagar el precio de velocidad para mantener a otros desarrolladores (los tipos de soporte tienden a pensar que siempre son otros desarrolladores que necesitan redes de seguridad). . –