Un libro de C ++ que he estado leyendo dice que cuando se elimina un puntero usando el delete
operador, la memoria en la ubicación a la que apunta se "libera" y se puede sobrescribir. También establece que el puntero continuará apuntando a la misma ubicación hasta que sea reasignado o configurado NULL
.
Sin embargo, en Visual Studio 2012; este no parece ser el caso!
Ejemplo:
#include <iostream>
using namespace std;
int main()
{
int* ptr = new int;
cout << "ptr = " << ptr << endl;
delete ptr;
cout << "ptr = " << ptr << endl;
system("pause");
return 0;
}
Cuando compilo y ejecuto este programa obtengo el siguiente resultado:
ptr = 0050BC10
ptr = 00008123
Press any key to continue....
¡Claramente, la dirección a la que apunta el puntero cambia cuando se llama a borrar!
¿Por qué está pasando esto? ¿Tiene esto algo que ver específicamente con Visual Studio?
Y si eliminar puede cambiar la dirección a la que apunta de todos modos, ¿por qué no eliminar automáticamente establecer el puntero en NULL
lugar de alguna dirección aleatoria?
fuente
Respuestas:
Me di cuenta de que la dirección almacenada
ptr
siempre se sobrescribía con00008123
...Esto parecía extraño, por lo que investigé un poco y encontré esta publicación de blog de Microsoft que contiene una sección que trata sobre "Desinfección automática de punteros al eliminar objetos de C ++".
No solo explica lo que Visual Studio hace con el puntero después de que se elimina, sino que también responde por qué eligieron NO configurarlo
NULL
automáticamente.Esta "función" está habilitada como parte de la configuración "Verificaciones SDL". Para habilitarlo / deshabilitarlo, vaya a: PROYECTO -> Propiedades -> Propiedades de configuración -> C / C ++ -> General -> Verificaciones SDL
Para confirmar esto:
Cambiar esta configuración y volver a ejecutar el mismo código produce el siguiente resultado:
"característica" está entre comillas porque en el caso de que tenga dos punteros en la misma ubicación, llamar a eliminar solo desinfectará UNO de ellos. El otro se quedará apuntando a la ubicación no válida ...
ACTUALIZAR:
Después de 5 años más de experiencia en programación en C ++, me doy cuenta de que todo este problema es básicamente un punto discutible. Si usted es un programador de C ++ y todavía usa
new
ydelete
administra punteros sin procesar en lugar de usar punteros inteligentes (que evitan todo este problema), es posible que desee considerar un cambio en la carrera profesional para convertirse en un programador de C. ;)fuente
0x8123
lugar de0
. El puntero sigue siendo inválido, pero causa una excepción al intentar desreferenciarlo (bueno), y no pasa las comprobaciones NULL (también bueno, porque es un error no hacerlo). ¿Dónde está el lugar para los malos hábitos? Realmente es algo que te ayuda a depurar.Verá los efectos secundarios de la
/sdl
opción de compilación. Activado de forma predeterminada para proyectos VS2015, permite verificaciones de seguridad adicionales más allá de las proporcionadas por / gs. Use Proyecto> Propiedades> C / C ++> General> Configuración de comprobaciones SDL para modificarlo.Citando del artículo de MSDN :
Tenga en cuenta que configurar punteros eliminados en NULL es una mala práctica cuando usa MSVC. Derrota la ayuda que obtienes tanto de Debug Heap como de esta opción / sdl, ya no puedes detectar llamadas inválidas gratis / eliminar en tu programa.
fuente
delete
uno, Visual Studio dejará el segundo puntero apuntando a su ubicación original que ahora no es válida.Esa es definitivamente información engañosa.
Esto está claramente dentro de las especificaciones del idioma.
ptr
no es válido después de la llamada adelete
. Usarptr
después de que ha sidodelete
d es causa de comportamiento indefinido. No lo hagas El entorno de tiempo de ejecución es libre de hacer lo que quieraptr
después de la llamadadelete
.Cambiar el valor del puntero a cualquier valor antiguo está dentro de la especificación del lenguaje. En cuanto a cambiarlo a NULL, diría que sería malo. El programa se comportaría de manera más sensata si el valor del puntero se estableciera en NULL. Sin embargo, eso ocultará el problema. Cuando el programa se compila con diferentes configuraciones de optimización o se transfiere a un entorno diferente, es probable que el problema aparezca en el momento más inoportuno.
fuente
delete
dos veces al puntero no causaría ningún problema. Eso definitivamente no es bueno.NULL
pero que también esté definitivamente fuera del espacio de direcciones del proceso, expondrá más casos que las dos alternativas. Dejarlo colgando no necesariamente causará una falla predeterminada si se usa después de ser liberado; configurarloNULL
no causará una segfault si esdelete
d nuevamente.En general, incluso la lectura (como lo hace anteriormente, tenga en cuenta: esto es diferente de la desreferenciación) de los punteros no válidos (el puntero se vuelve inválido, por ejemplo, cuando lo
delete
hace) es un comportamiento definido por la implementación. Esto se introdujo en CWG # 1438 . Ver también aquí .Tenga en cuenta que antes de que la lectura de valores de punteros no válidos fuera un comportamiento indefinido, lo que tiene arriba sería un comportamiento indefinido, lo que significa que cualquier cosa podría suceder.
fuente
[basic.stc.dynamic.deallocation]
: "Si el argumento dado a una función de desasignación en la biblioteca estándar es un puntero que no es el valor de puntero nulo, la función de desasignación desasignará el almacenamiento al que hace referencia el puntero, invalidando todos los punteros que se refieren a cualquier parte del almacenamiento desasignado "y la regla en[conv.lval]
(sección 4.1) que dice que la lectura (conversión lvalue-> rvalue) de cualquier valor de puntero no válido es un comportamiento definido por la implementación.Creo que está ejecutando algún tipo de modo de depuración y VS está intentando reorientar su puntero a una ubicación conocida, de modo que se pueda rastrear e informar más intentos de desreferenciarlo. Intente compilar / ejecutar el mismo programa en modo de lanzamiento.
Por lo general, los punteros no se cambian
delete
por razones de eficiencia y para evitar dar una falsa idea de seguridad. Establecer el puntero de eliminación en un valor predefinido no servirá en la mayoría de los escenarios complejos, ya que es probable que el puntero que se elimine sea solo uno de varios que apuntan a esta ubicación.De hecho, cuanto más lo pienso, más encuentro que VS tiene la culpa al hacerlo, como de costumbre. ¿Qué pasa si el puntero es constante? ¿Todavía lo va a cambiar?
fuente
Después de eliminar el puntero, la memoria a la que apunta puede seguir siendo válida. Para manifestar este error, el valor del puntero se establece en un valor obvio. Esto realmente ayuda al proceso de depuración. Si el valor se estableció en
NULL
, es posible que nunca se muestre como un error potencial en el flujo del programa. Por lo que puede ocultar un error cuando se prueba después contraNULL
.Otro punto es que algunos optimizadores de tiempo de ejecución pueden verificar ese valor y cambiar sus resultados.
En épocas anteriores, MS establece el valor en
0xcfffffff
.fuente