¿Cómo saber si un puntero apunta al montón o a la stack?

Ejemplo:

bool isHeapPtr(void* ptr) { //... } int iStack = 35; int *ptrStack = &iStack; bool isHeapPointer1 = isHeapPtr(ptrStack); // Should be false bool isHeapPointer2 = isHeapPtr(new int(5)); // Should be true /* I know... it is a memory leak */ 

Por qué, quiero saber esto:

Si tengo en una clase un apuntador-miembro y no sé si el objeto apuntador es nuevo-asignado. Entonces debería usar esa utilidad para saber si tengo que delete el puntero.

Pero:
Mi diseño aún no está hecho. Entonces, lo progtwigré de esa manera, siempre tengo que delete . Voy a evitar la progtwigción de basura

No hay forma de hacerlo, y si necesita hacerlo, hay algo mal con su diseño. Hay una discusión sobre por qué no se puede hacer esto en C ++ más efectivo .

En el caso general, no tiene suerte, me temo, dado que los indicadores pueden tener algún valor, no hay forma de diferenciarlos. Si tiene conocimiento de la dirección y el tamaño de inicio de la stack (desde su TCB en un sistema operativo integrado, por ejemplo), es posible que pueda hacerlo. Algo como:

 stackBase = myTCB->stackBase; stackSize = myTCB->stackSize; if ((ptrStack < stackBase) && (ptrStack > (stackBase - stackSize))) isStackPointer1 = TRUE; 

La única solución “buena” que puedo pensar es sobrecargar al operator new para esa clase y rastrearlo. Algo como esto (código comstackdo por el cerebro):

 class T { public: void *operator new(size_t n) { void *p = ::operator new(n); heap_track().insert(p); return p; } void operator delete(void* p) { heap_track().erase(p); ::operator delete(p); } private: // a function to avoid static initialization order fiasco static std::set& heap_track() { static std::set s_; return s_; } public: static bool is_heap(void *p) { return heap_track().find(p) != heap_track().end(); } }; 

Entonces puedes hacer cosas como esta:

 T *x = new X; if(T::is_heap(x)) { delete x; } 

Sin embargo, desaconsejaría un diseño que requiera que se pueda preguntar si se asignó algo en el montón.

Bueno, saca tu libro de ensamblador y compara la dirección de tu puntero con el puntero de stack:

 int64_t x = 0; asm("movq %%rsp, %0;" : "=r" (x) ); if ( myPtr < x ) { ...in heap... } 

Ahora x contendría la dirección a la que tendrá que comparar su puntero. Tenga en cuenta que no funcionará para la memoria asignada en otro hilo, ya que tendrá su propia stack.

aquí está, funciona para MSVC:

 #define isheap(x, res) { \ void* vesp, *vebp; \ _asm {mov vesp, esp}; \ _asm {mov vebp, ebp}; \ res = !(x < vebp && x >= vesp); } int si; void func() { int i; bool b1; bool b2; isheap(&i, b1); isheap(&si, b2); return; } 

es un poco feo, pero funciona. Funciona solo para variables locales. Si pasa el puntero de stack de la función de llamada, esta macro devolverá verdadero (significa que es montón)

Primero, ¿por qué necesitas saber esto? ¿Qué problema real estás tratando de resolver?

La única forma que conozco para hacer este tipo de determinación sería sobrecargar al operator new global operator new y operator delete . Luego, puede preguntarle a su administrador de memoria si un puntero le pertenece (el montón) o no (stack o datos globales).

Incluso si pudieras determinar si un puntero estaba en un montón particular o en un montón particular, puede haber montones múltiples y montones múltiples para una aplicación.

En función del motivo de la consulta, es extremadamente importante que cada contenedor tenga una política estricta sobre si “posee” punteros o no. Después de todo, incluso si esos punteros apuntan a la memoria asignada en el montón, alguna otra pieza de código también podría tener una copia del mismo puntero. Cada puntero debe tener un “propietario” a la vez, aunque la propiedad se puede transferir. El propietario es responsable de la destrucción.

En raras ocasiones, es útil que un contenedor realice un seguimiento de los punteros propios y no propios, ya sea mediante el uso de indicadores o almacenándolos por separado. La mayoría de las veces, sin embargo, es más simple establecer una política clara para cualquier objeto que pueda contener punteros. Por ejemplo, la mayoría de los punteros inteligentes siempre poseen sus punteros reales de contenedor.

Por supuesto, los indicadores inteligentes son importantes aquí: si desea un puntero de seguimiento de la propiedad, estoy seguro de que puede encontrar o escribir un tipo de puntero inteligente para abstraer esa molestia.

En los sistemas operativos convencionales, la stack crece desde la parte superior mientras que la stack crece desde la parte inferior. Por lo tanto, puede verificar heurísticamente si la dirección está más allá de un valor grande, para alguna definición de “grande”. Por ejemplo, lo siguiente funciona en mi sistema Linux de 64 bits:

 #include  bool isHeapPtr(const void* ptr) { return reinterpret_cast(ptr) < 0xffffffffull; } int main() { int iStack = 35; int *ptrStack = &iStack; std::cout << isHeapPtr(ptrStack) << std::endl; std::cout << isHeapPtr(new int(5)) << std::endl; } 

Tenga en cuenta que es una heurística cruda que podría ser interesante para jugar, pero no es apropiada para el código de producción.

A pesar de fuertes afirmaciones de lo contrario, es claramente posible hacer lo que quiera, de una manera dependiente de la plataforma. Sin embargo, solo porque algo es posible, eso no lo convierte automáticamente en una buena idea. Una simple regla de stack == sin eliminar, de lo contrario == eliminar es poco probable que funcione bien.

Una forma más común es decir que si asigné un buffer, entonces tengo que eliminarlo. Si el progtwig me pasa un buffer, no es mi responsabilidad eliminarlo.

p.ej

 class CSomething { public: CSomething() : m_pBuffer(new char[128]) , m_bDeleteBuffer(true) { } CSomething(const char *pBuffer) : m_pBuffer(pBuffer) , m_bDeleteBuffer(false) { } ~CSomething() { if (m_bDeleteBuffer) delete [] m_pBuffer; } private: const char *m_pBuffer; bool m_bDeleteBuffer; }; 

Intentas hacerlo de la manera difícil. Aclare su diseño para que quede claro quién es el “propietario” de los datos y permita que ese código se ocupe de su duración.

aquí hay una forma universal de hacerlo en Windows usando TIP:

 bool isStack(void* x) { void* btn, *top; _asm { mov eax, FS:[0x08] mov btn, eax mov eax, FS:[0x04] mov top, eax } return x < top && x > btn; } void func() { int i; bool b1; bool b2; b1 = isStack(&i); b2 = isStack(&si); return; } 

La única manera que conozco de hacer esto de manera semi-confiable es si puede sobrecargar al operator new para el tipo para el que necesita hacer esto. Lamentablemente, hay algunas trampas importantes allí y no puedo recordar cuáles son.

Sé que una de las trampas es que algo puede estar en el montón sin haber sido asignado directamente. Por ejemplo:

 class A { int data; }; class B { public: A *giveMeAnA() { return &anA; } int data; A anA; }; void foo() { B *b = new B; A *a = b->giveMeAnA(); } 

En el código anterior, a in foo termina con un puntero a un objeto en el montón que no fue asignado con new . Si su pregunta es realmente “¿Cómo puedo saber si puedo llamar delete en este puntero”? La sobrecarga del operator new para hacer algo complicado podría ayudarlo a responder esa pregunta. Sigo pensando que si tienes que hacer esa pregunta, has hecho algo muy malo.

¿Cómo es posible que no sepas si hay algo alojado en un montón o no? Debe diseñar el software para tener un único punto de asignación.

A menos que esté haciendo cosas verdaderamente exóticas en un dispositivo integrado o trabaje en lo profundo de un kernel personalizado, simplemente no veo la necesidad de hacerlo.

Mire este código (sin verificación de errores, por el bien del ejemplo):

 class A { int *mysweetptr; A() { mysweetptr = 0; //always 0 when unalloc'd } void doit() { if( ! mysweetptr) { mysweetptr = new int; //now has non-null value } } void undoit() { if(mysweetptr) { delete mysweetptr; mysweetptr = 0; //notice that we reset it to 0. } } bool doihaveit() { if(mysweetptr) return true; else return false; } ~A() { undoit(); } }; 

En particular, observe que estoy usando el valor nulo para determinar si el puntero ha sido asignado o no, o si necesito eliminarlo o no.

Su diseño no debe depender de la determinación de esta información (como otros han señalado, no es realmente posible). En cambio, su clase debe definir explícitamente la propiedad de los punteros que toma en su constructor o métodos. Si su clase toma posesión de esos punteros, entonces es un comportamiento incorrecto pasar un puntero a la stack o global, y debe eliminarlo con el conocimiento de que el código de cliente incorrecto puede bloquearse. Si su clase no toma posesión, no debería estar borrando el puntero.