2009-05-07 19 views
8

Tengo un puntero a una clase determinada. Digamos, por ejemplo, el puntero es:¿Qué determina qué se escribe en un puntero de C++ cuando se llama a delete?

0x24083094 

que los puntos de puntero a:

0x03ac9184 

que es la tabla de función virtual de mi clase. Eso tiene sentido para mí. En windbg, todo parece correcto.

Elimino dicho puntero. Ahora bien, en 0x24083094 es:

0x604751f8 

Pero no es un poco de basura al azar, esa dirección se pone en allí cada vez, es consistentemente 0x604751f8! Tanto que realmente puedo usar esa dirección para determinar si ese puntero fue eliminado, ¡entre las ejecuciones de mi aplicación!

¿Pero por qué? ¿Cómo se determina que 0x604751f8 se debe escribir allí?

Para que conste, estoy usando ventanas, edificio en Visual Studio 2003.

Sé que no puedo confiar en que se establece que el valor, incluso si parece ser consistente, pero puedo confiar en siendo diferente? Es decir, 0x03ac9184 no estará en 0x24083094 si se borra el puntero, ¿verdad? ¿Qué se pone ahí? Puede ser cualquier cosa, pero 0x03ac9184 definitivamente no estará allí (o aún podría llamar a métodos, ya que esa es la tabla de funciones virtuales). ¿Estoy en lo cierto?

Siento que tengo una respuesta. no puede confiar en nada después de que se elimine. Tal vez algunos antecedentes ayudarán a las personas a ver de dónde vengo. Esencialmente, estoy tratando de arreglar un error donde un puntero se elimina de debajo de mí. Es una larga historia, no entraré en detalles.

Básicamente, estoy tratando de detectar que estoy en esta situación, así que puedo salir de mi función. Supongo que la forma más fácil y mejor es averiguar quién posee este puntero, y preguntarle si algo ha cambiado. Así que voy a implementar una solución como esa. Evita todo este hacker de eliminación de C++ que estuve discutiendo.

Sin embargo,, lo interesante es que en nuestro código tenemos una clase llamada 'BogusObject' que esencialmente actúa como una bandeja que captura personas que accidentalmente desreferencian los objetos liberados. Básicamente, enganchamos nuestras propias funciones de eliminación y bash la clase BogusObject en el vtable de cualquier clase liberada.

Luego, si alguien llama a algo, recibe un bonito mensaje diciendo algo en el sentido de "hey, algo está mal amigo". Esto está sucediendo en mi caso. Es decir, 0x604751f8+(someoffset) está dentro de la clase BogusObject. ¡Pero ya no usamos BogusObject! Literalmente no está configurado en ningún lado (incluso enlaces correctamente si elimino completamente la clase BogusObject), y aún así recibo el bonito mensaje de que algo está mal. Pero ahora soy de la opinión de que es una coincidencia.

Por alguna razón, el tiempo de ejecución está poniendo ese valor 0x604751f8 en este puntero cuando se elimina, y eso simplemente corresponde a la clase que tiene el propósito de detectar situaciones como esta.

+1

¿Por qué no establece un punto de interrupción de datos en su puntero (0x24083094) y ve cuando se escribe 0x604751f8 en él? Cuando el depurador se rompe, rastrea la pila para ver quién te llamó y de dónde vino el valor. –

+0

Sí, lo hice. Se cambia después de regresar de myModule! MyClass :: 'vector deleting destructor '. He entrado en cada línea entre cuando se llama a eliminar y cuando cambia. De hecho, tengo un registro de Windbg que muestra esto, con algunos dt's cambiando. Sin embargo, tiene muchas cosas de la compañía, así que tendré que restregarme antes de compartir. – pj4533

Respuesta

16

Nada en el estándar determina qué se escribe allí.Visual Studio (al menos en modo de depuración) a menudo escribirá valores sentinales en todo el lugar para ayudar a detectar errores de manera temprana.

Este valor no es algo en lo que pueda confiar, pero si alguna vez encuentra ese valor misteriosamente apareciendo en su programa, puede asumir que en algún lugar está haciendo referencia a la memoria eliminada. Consulte this answer para obtener una lista de valores en un compilador.

También es muy posible que sea un puntero de lista libre, que apunta a la siguiente pieza de memoria libre. La mayoría de los asignadores de memoria mantienen su memoria libre en una lista vinculada de clases, utilizando la memoria libre que están rastreando para almacenar los datos de seguimiento.

En cualquier caso, NO DEBE utilizar ese valor de puntero para cualquier cosa que desee seguir trabajando, a menos que llame a Microsoft y obtenga documentación que explique por qué ese valor es lo que es, y garantice que no lo hará cambio. E incluso entonces, sepa que su código ahora está vinculado al comportamiento de un compilador. En C++, acceder a la memoria no asignada es indefinido y malvado.

Editar: Ni siquiera puede confiar en que ese valor cambie después de una eliminación. No hay nada que diga que un compilador necesita modificar los datos en delete.

+0

Sí, sé lo de FEEEFEEE y lo que sea. ¿Pero por qué la consistencia en mi caso para SIEMPRE poner 0x604751f8 en ese lugar? – pj4533

+3

0x604751f8 es probable que sea un puntero a algo (otro bloque libre tal vez). Pero también podría ser un montón de bitflags o una firma –

+5

O basura (no hay nada que diga que la basura no puede ser consistente). –

2

Llamar al operador de eliminación por un puntero no significa que la memoria "eliminada" se borrará. Solo llamará al destructor de los objetos eliminados y marcará la memoria de montón asignada como desasignada. (este es el comportamiento predeterminado del operador de eliminación).

Si necesita borrar el contenido de la memoria al eliminar, debe anular el operador de eliminación.

+0

Correcto, pero algo más debe estar ocurriendo, porque la memoria cambia. Entiendo que no lo aclara ... pero consistentemente lo hace 0x604751f8. ¿Por qué? – pj4533

+0

¿Está revisando justo después de la declaración de eliminación? En ese caso, el destructor altera el puntero o el puntero se cambia de otro hilo. – Jonatan

+0

Cambia cuando se compila en modo Depuración.AFAIK Las funciones de desasignación detrás de la eliminación cambian el contenido de la memoria desasignada para "marcar" la memoria como libre, para fines de depuración. Pero no en el objetivo de lanzamiento, por razones de rendimiento ... –

4

Una vez que elimina un objeto, la memoria que estaba utilizando se vuelve a colocar en la tienda gratuita (montón). Por supuesto, la tienda gratuita tendrá sus propias estructuras de datos (y posiblemente datos de depuración) que se aplicará a esa memoria.

¿Qué significa el valor particular que está buscando? Podría ser casi cualquier cosa.

+0

Aún más, es probable que esté mal alineado en el medio de una estructura de registro de montón. – Joshua

2

Como dijo Josh, hay una serie de valores que se pueden insertar, para hacer la vida más fácil para el depurador con compilaciones de depuración. Estos son específicos del compilador y de ninguna manera se puede confiar. En una compilación de lanzamiento, creo que el comportamiento predeterminado de la mayoría de los compiladores de C++ es no hacer nada con la memoria que se libera, por lo tanto, hasta que ese espacio de direcciones se asigne de nuevo, el contenido será básicamente lo que haya antes, por supuesto, NUNCA deberías confiar en esto.

2

Es casi seguro que haya un significado interno específico para el valor que aparece cada vez que elimina el objeto.

Sin embargo, puede cambiar con la próxima versión de Visual C++, y sin duda será diferente en otros compiladores de proveedores.

El hecho de que parece ser el mismo cada vez que elimina un objeto no significa nada útil. Ni siquiera puede ser potencialmente útil. Suponiendo que hayas encontrado alguna forma de aprovecharlo, sería un hacking espantoso hacerlo, y finalmente te arrepentirías.

¡Trata de olvidarlo!

+0

Podría ser útil como una herramienta de depuración. – florin

+0

@florin, a menos que le guste la depuración estadística que es una mala idea. – Joshua

2

Como dijo Michael Burr anteriormente, el recuerdo vuelve a la tienda gratuita. Algunas tiendas gratuitas se implementan como listas vinculadas, con el -> próximo puntero colocado al comienzo del búfer libre. Es posible que el número mágico que está viendo (0x604751f8) sea el guardián del 'final de la lista'.Puede verificar realizando el siguiente experimento:

Foo* f = new Foo(); 
Bar* b = new Bar(); 

// make a note of the values of f and b _pointers_ 

delete b; // check that b points now to 0x604751f8 
delete f; // check that f points now to 0x604751f8 

// now check that does b point to; it might point to f! 

¡Déjanos saber lo que usted encuentra!

2

Lo más probable es que este valor de puntero sea el vtable de la clase base. Cuando se ejecuta un destructor para una clase derivada, después de que completa su cuerpo normal, 'reprende' la memoria como el tipo base (básicamente, escribe el puntero vtable de la clase base en el objeto) y luego llama al destructor de la clase base.

Tenga en cuenta que este comportamiento es un detalle de implementación interna del soporte C++ en tiempo de ejecución del compilador, por lo que otros compiladores (o versiones futuras del mismo compilador) pueden hacer algo completamente diferente. Pero esto 'convertir vtable a la clase base y llamar al destructor de la clase base' es bastante común y se remonta a la implementación cfront original de C++

2

El programa está tratando de decirle algo. Una fecha, un número de teléfono, ¿quién sabe?

Ahora en serio, esto se no expresados ​​, totalmente dependiente de la implementación , y por supuesto tratar de eliminar la referencia de ese puntero después delete daría lugar a un comportamiento indefinido. Entonces, en resumen, ¿a quién le importa ?

2

No, no puede confiar en que se establezca. Ni siquiera puedes confiar en que sea diferente.

Los gestores de montón de MS-DOS a menudo permiten utilizar la memoria liberada hasta la siguiente llamada a malloc. Nuevo y eliminar en esa era llamado malloc y gratis.

En la actualidad, la mayoría de los administradores de heap son razonables a la hora de devolver la memoria al sistema operativo, ¡lo que significa que ni siquiera puede confiar en que sea legible! Incluso aquellos que todavía lo permiten (glibc tiene un modo bwd-compat que lo permite) estás sujeto a condiciones de carrera de subprocesos.

Además, delete puede cambiar el puntero a NULL si es un lvalue.

Una vez que llame a eliminar, ni siquiera piense en desreferenciar el puntero.

0

Tiene acceso al código fuente CRT en Visual Studio. Podrías echar un vistazo. Lo hice una vez para entender mejor un error que tuve.

Cuestiones relacionadas