如何知道指针是指向堆还是堆栈?
例:
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 */
为什么,我想知道这一点:
如果我有一个类的成员指针,我不知道指针对象是否新分配。 然后,我应该使用这样的实用程序来知道是否必须delete
指针。
但:
我的devise还没有制定。 所以,我将这样编程,我总是要delete
它。 我要避免垃圾程序
没有办法做到这一点 – 如果你需要这样做,你的devise有什么问题。 有一个讨论为什么你不能在更有效的C ++中做到这一点。
在一般情况下,你运气不好,我怕 – 因为指针可以有任何价值,所以没有办法把它们分开。 如果您已经知道堆栈的起始地址和大小(例如,来自embedded式操作系统中的TCB),那么您可能可以做到这一点。 就像是:
stackBase = myTCB->stackBase; stackSize = myTCB->stackSize; if ((ptrStack < stackBase) && (ptrStack > (stackBase - stackSize))) isStackPointer1 = TRUE;
我能想到的唯一的“好”解决scheme是重载该类的operator new
并跟踪它。 像这样的东西(大脑编译的代码):
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<void*>& heap_track() { static std::set<void*> s_; return s_; } public: static bool is_heap(void *p) { return heap_track().find(p) != heap_track().end(); } };
那么你可以做这样的东西:
T *x = new X; if(T::is_heap(x)) { delete x; }
不过,我会build议不要这样的devise,要求您能够问问堆上是否分配了某些东西。
那么,拿出你的汇编手册,把你的指针的地址和栈指针进行比较:
int64_t x = 0; asm("movq %%rsp, %0;" : "=r" (x) ); if ( myPtr < x ) { ...in heap... }
现在x将包含你必须把你的指针比较的地址。 请注意,它不会在另一个线程中分配的内存,因为它将有自己的堆栈。
在这里,它适用于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; }
它有点丑陋,但工程。 仅适用于本地variables。 如果你从调用函数传递堆栈指针,这个macros将返回true(意味着它是堆)
首先,你为什么需要知道这个? 你想要解决什么样的问题?
我意识到做出这种决定的唯一方法是重载全局operator new
和operator delete
。 那么你可以问你的内存pipe理器,如果一个指针属于它(堆)或不(堆栈或全局数据)。
即使你可以确定一个指针是在一个特定的堆上,还是在一个特定的堆栈上,对于一个应用程序也可以有多个堆栈和多个堆栈。
根据要求的原因,对每个集装箱是否拥有它所拥有的指示器都有严格的政策是非常重要的。 毕竟,即使这些指针指向堆分配内存,其他一些代码也可能具有相同指针的副本。 每个指针应该一次拥有一个“所有者”,虽然所有权可以转移。 业主负责破坏。
在极less数情况下,容器可以跟踪拥有的和非拥有的指针(无论是使用标志还是单独存储)。 但是大多数情况下,为任何可以容纳指针的对象设置一个明确的策略就简单多了。 例如,大多数智能指针总是拥有它们的容器实指针。
当然,智能指针在这里很重要 – 如果你想要一个所有权跟踪指针,我相信你可以find或写一个智能指针types来抽象这个麻烦。
在主stream操作系统中,堆栈从顶部增长,而堆栈从底部增长。 所以你可以启发式地检查地址是否超出一个很大的值,对于一些“大”的定义。 例如,以下在我的64位Linux系统上工作:
#include <iostream> bool isHeapPtr(const void* ptr) { return reinterpret_cast<unsigned long long int>(ptr) < 0xffffffffull; } int main() { int iStack = 35; int *ptrStack = &iStack; std::cout << isHeapPtr(ptrStack) << std::endl; std::cout << isHeapPtr(new int(5)) << std::endl; }
请注意,这是一个粗略的启发式,可能会有趣的玩,但不适合生产代码。
尽pipe大声疾呼,显然有可能以平台依赖的方式做你想做的事情。 不过,只是因为有可能,这不会自动成为一个好主意。 一个简单的规则堆栈==不删除,否则==删除不太可能工作。
更常见的方法是说如果我分配了一个缓冲区,那么我必须删除它,如果程序传递给我一个缓冲区,那么删除它不是我的责任。
例如
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; };
你正在努力做到这一点。 明确你的devise,清楚谁拥有数据,让代码处理它的生命周期。
这里是通用的方法来做到这一点在Windows使用提示:
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; }
我知道这样做半可靠的唯一方法是,如果你可以重载operator new
的types,你需要做到这一点。 不幸的是,在那里有一些重大的陷阱,我不记得他们是什么。
我知道,有一个陷阱就是没有直接分配的东西可以堆在堆上。 例如:
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(); }
在上面的代码中, a
在foo
的指针指向堆中没有被分配new
。 如果你的问题真的是“我怎么知道我是否可以在这个指针上调用delete
”。 重载operator new
来做一些棘手的事情可能会帮助你回答这个问题。 我仍然认为,如果你不得不问这个问题,你做了一些非常错误的事情。
你怎么可能不知道是否有堆分配? 您应该devise软件以获得单一的分配点。
除非你在embedded式设备上做了一些真正奇特的东西,或者在自定义的内核中深入工作,否则我只是看不到它的需要。
看看这个代码(为了举例,没有错误检查):
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(); } };
尤其要注意的是,我使用空值来确定指针是否已被分配,或者是否需要删除。
你的devise不应该依赖于确定这些信息(正如其他人指出的,这是不可能的)。 相反,你的类应该明确地定义它在构造函数或方法中所使用的指针的所有权。 如果你的类取得了这些指针的所有权,那么把一个指针传递给堆栈或全局是不正确的行为,你应该删除它,并知道不正确的客户端代码可能会崩溃。 如果你的class级没有取得所有权,那么不应该删除指针。