visual tutorial studio ordenar microsoft linea fuente español ejemplos configurar codigo code cambiar ajuste c++ pointers visual-studio-2012 memory-management delete-operator

c++ - tutorial - ¿Qué hace Visual Studio con un puntero eliminado y por qué?



visual studio code ejemplos (6)

También indica que el puntero continuará apuntando a la misma ubicación hasta que se reasigne o se establezca en NULL.

Esa es definitivamente información engañosa.

¡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?

Esto está claramente dentro de las especificaciones del idioma. ptr no es válido después de la llamada a delete . El uso de ptr después de que se haya delete d es causa de un comportamiento indefinido. No lo hagas El entorno de tiempo de ejecución es libre de hacer lo que quiera con ptr después de la llamada para delete .

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 en lugar de alguna dirección aleatoria?

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 eso sería malo. El programa se comportaría de una 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.

Un libro de C ++ que he estado leyendo dice que cuando se elimina un puntero utilizando el operador de delete , la memoria en la ubicación a la que apunta se "libera" y se puede sobrescribir. También indica que el puntero continuará apuntando a la misma ubicación hasta que se reasigne o se establezca en 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?


Creo que está ejecutando algún tipo de modo de depuración y VS está intentando reorientar su puntero a una ubicación conocida, para que se pueda rastrear e informar más intentos de desreferenciarlo. Intente compilar / ejecutar el mismo programa en modo de lanzamiento.

Los punteros generalmente no se cambian dentro de 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 será bueno en la mayoría de los escenarios complejos, ya que es probable que el puntero que se está eliminando 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?


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 tanto, puede ocultar un error cuando pruebe más tarde contra NULL .

Otro punto es que algunos optimizadores de tiempo de ejecución pueden verificar ese valor y cambiar sus resultados.

En épocas anteriores, MS estableció el valor en 0xcfffffff .


Me di cuenta de que la dirección almacenada en ptr siempre se sobrescribía con 00008123 ...

Esto parecía extraño, así que hice un poco de investigación 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 ++".

... las comprobaciones de NULL son una construcción de código común, lo que significa que una comprobación existente de NULL combinada con el uso de NULL como valor de desinfección podría ocultar fortuitamente un problema de seguridad de memoria genuino cuya causa raíz realmente necesita ser abordada.

Por esta razón, hemos elegido 0x8123 como valor de desinfección: desde la perspectiva del sistema operativo, se encuentra en la misma página de memoria que la dirección cero (NULL), pero una infracción de acceso en 0x8123 se destacará mejor para el desarrollador ya que necesita una atención más detallada .

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 establecerlo en 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:

ptr = 007CBC10 ptr = 007CBC10

"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 dejará apuntando a la ubicación no válida.

Visual Studio podría prepararlo para una situación difícil al no documentar esta falla en su diseño.


Verá los efectos secundarios de la opción de compilación /sdl . 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 :

  • Realiza una desinfección limitada del puntero. En las expresiones que no implican desreferencias y en tipos que no tienen destructor definido por el usuario, las referencias de puntero se establecen en una dirección no válida después de una llamada para eliminar. Esto ayuda a evitar la reutilización de referencias de puntero obsoleto.

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.


delete ptr; cout << "ptr = " << ptr << endl;

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 ) es un comportamiento definido por la implementación. Esto se introdujo en CWG # 1438 . Ver también here .

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.