c++ - ¿Por qué free(p) no establece p en NULL?
memory-management memory-leaks (9)
¿Alguna razón por la cual este no puede ser el comportamiento estándar de free()
?
múltiples punteros apuntando al mismo objeto:
#include <stdlib.h>
#include <stdio.h>
void safefree(void*& p)
{
free(p); p = NULL;
}
int main()
{
int *p = (int *)malloc(sizeof(int));
*p = 1234;
int*& p2 = p;
printf("p=%p p2=%p/n", p, p2);
safefree((void*&)p2);
printf("p=%p p2=%p/n", p, p2);
safefree((void*&)p); // safe
return 0;
}
Asignación de malloc
exige emitir desde void*
viceversa:
safefree()
exige cast safefree()
void*&
(referencia)
¿Qué es esta cosa maníaca con cero? ¡Hay otros números!
¿Por qué un puntero libre debería contener cero más que cualquier otro valor distinguido?
En la práctica, en mi código C ++ a menudo utilizo objetos watchdog y al liberar un puntero, restablézcalo a cero, pero al watchdog. Eso tiene la ventaja adicional de que los métodos del objeto aún pueden llamarse con la interfaz existente y es mucho más seguro y eficiente que restablecer el puntero a cero (evita la prueba de cero, solo llamo al objeto).
Si una función similar like dice zfree (void * & p) establecería p en cero, prohibiría mi estilo de perro guardián (o al menos no ayudaría).
Y como otros apuntan hacia fuera, ¿cuál sería el punto para restablecer un puntero a cero si se sale del alcance? Solo código inútil. ¿Y qué pasa si hay otros punteros que contienen la misma dirección, etc.?
Bjarne Stroustrup discute si el operador de delete
debe poner a cero su operando . No es la función gratuita (), pero es una buena discusión de todos modos. Considere puntos como:
- ¿Qué sería de cero si dijeras libre (p + 1)?
- ¿Qué pasa si hay dos punteros a la memoria? ¿Por qué solo establecer uno en nulo si una referencia pendiente se queda atrás?
También dice que fue pensado pero nunca sucedió con el operator delete
:
C ++ explícitamente permite una implementación de delete para poner a cero un operando de lvalue, y esperaba que las implementaciones lo hicieran, pero esa idea no parece haberse popularizado entre los implementadores.
Con referencia a la cita de Stroustrup sobre la eliminación, Peter Norvig también hace un comentario similar. Escribe (¡no sobre C ++!):
"Nothing is destroyed until it is replaced"
- Auguste Comte (1798-1857) (on the need for revolutionary new
theories (or on the need to do x.f = null in garbage-collected
languages with destructors))
En mi código C, encuentro la siguiente macro muy útil:
#define free(p) free((void *)(p)),(p)=NULL /* zero p */
Esto, como está escrito, usa su argumento dos veces. Pero esto no es un problema, ya que cualquier uso como free (p ++) o free (find_named_object ("foo")) dará un error de tiempo de compilación (lvalue required). Y puede ocultar la macro usando (gratis) (p ++), o llamándolo de otra manera, por ejemplo, GRATIS.
Debido a que los parámetros de la función se pasan por valor en C. Es decir, si p == 0x12345, usted pasa ''0x12345'' a free (), no p "en sí".
En C, llamar a una función nunca puede alterar el valor de los parámetros que usted pasa, por lo tanto, si free(p)
modificó el valor de p
estableciéndolo en NULL
entonces este comportamiento sería muy poco estándar.
Los parámetros de la función C siempre se pasan por valor, por lo que para modificar el puntero pasado a free (), deberá pasar un puntero al puntero que se está desasignado, lo que puede provocar errores causados por operadores olvidados.
En segundo lugar, si ese puntero tuviera algún alias, el programador todavía sería responsable de anularlos. Pude ver los problemas causados por los programadores suponiendo que todas las referencias se establecieron en NULL.
Finalmente, no siempre es necesario establecer el puntero en NULL. Imagine una función que asigna algo de memoria, realiza algún trabajo y la libera antes de regresar. Pude ver cómo establecer el puntero a NULL puede no parecer óptimo.
Respuesta simple: porque podría estar liberando una expresión, por ejemplo, free(find_named_object("foo"))
.
Más detalladamente: free
función free
toma un parámetro void*
, que es la dirección de la memoria para liberar. Esto no confiere a la función ningún conocimiento de la variable original que proporcionó la dirección (o, para el caso, si existe una variable). El solo hecho de establecer el parámetro pasado a NULL tampoco haría nada, ya que es solo una copia local de la dirección.
Si lo hiciera, tendría que pasar un puntero a un puntero a la función:
int * p = malloc( sizeof( int ));
free( & p );
que estoy seguro que muchas personas se equivocan.
No se garantiza que la conversión de la referencia desde int *&
to void *&
funcione. int *
simplemente no tiene que tener el mismo tamaño, representación ni requisitos de alineación como void *
.
La solución adecuada para C ++ sería usar plantillas, como sugiere Neil Butterworth en un comentario. Y ninguna de estas formas funciona en C, obviamente, que es de donde viene free()
.