我的内存分配,堆栈或堆,我可以在运行时找到它吗?

时间:2010-06-07 09:03:50

标签: c++ heap-memory stack-memory

我知道使用new分配的内存在堆中获取其空间,因此我们需要在程序结束前删除它,以避免内存泄漏。

让我们来看看这个节目......

Case 1:

char *MyData = new char[20];
_tcscpy(MyData,"Value");
.
.
.
delete[] MyData; MyData = NULL;


Case 2:
char *MyData = new char[20];
MyData = "Value";
.
.
.
delete[] MyData; MyData = NULL;

在案例2中,不是将值分配给堆内存,而是指向字符串文字。

现在当我们执行删除时,它会崩溃,因为它没有尝试删除堆内存。 有没有办法知道指针指向堆或堆栈的位置?

由程序员

  • 不会尝试删除任何堆栈内存
  • 他可以调查为什么这个指向堆内存的ponter最初是为了引用本地文字?中间的堆内存发生了什么?它是由另一个指针指向并在其他地方删除吗?

7 个答案:

答案 0 :(得分:2)

只要你需要这些知识,你就已经失去了。为什么?因为即使你省略了错误的删除[],你仍然会有内存泄漏。

创建内存的人应始终是删除内存的人。如果在某些情况下指针可能会丢失(或被覆盖),那么你必须保留一份指针才能正确删除。

答案 1 :(得分:2)

标准C ++中无法确定指针是否指向动态分配的内存。请注意,字符串文字不会在堆栈上分配。

答案 2 :(得分:2)

正如大多数用户所说,没有标准的方法来发现你正在处理的内存。

此外,正如许多用户所指出的那样,它是一种有点变态的情况,你将一个指针传递给一个函数,如果它在堆上分配,它应该自动删除它。

但是如果你坚持,不过有一些方法可以发现哪种内存属于哪种类型。

你实际上处理了3种类型的内存

  • 堆栈
  • 全局

例如:

char* p = new char[10]; // p is a pointer, points to heap-allocated memory

char* p = "Hello, world!"; // p is a pointer, points to the global memory

char p[] = "Hello, world!"; // p is a buffer allocated on the stack and initialized with the string

现在让我们区分它们。我将根据Windows API和x86汇编程序对此进行描述(因为这是我所知道的:))

让我们从堆栈内存开始。

bool IsStackPtr(PVOID pPtr)
{
    // Get the stack pointer
    PBYTE pEsp;
    _asm {
        mov pEsp, esp
    };

    // Query the accessible stack region
    MEMORY_BASIC_INFORMATION mbi;
    VERIFY(VirtualQuery(pEsp, &mbi, sizeof(mbi)));

    // the accessible stack memory starts at mbi.BaseAddress and lasts for mbi.RegionSize
    return (pPtr >= mbi.BaseAddress) && (pPtr < PBYTE(mbi.BaseAddress) + mbi.RegionSize);
}

如果指针是在另一个线程的堆栈上分配的,则应该通过GetThreadContext获取其堆栈指针,而不是仅仅取EIP寄存器值。

全球记忆

bool IsGlobalPtr(PVOID pPtr)
{
    MEMORY_BASIC_INFORMATION mbi;
    VERIFY(VirtualQuery(pPtr, &mbi, sizeof(mbi)));

    // Global memory allocated (mapped) at once for the whole executable
    return mbi.AllocationBase == GetModuleHandle(NULL);
}

如果您正在编写DLL,则应将其模块句柄(实际上是其基本映射指针)放在GetModuleHandle(NULL)上。

理论上你可以假设如果内存既不是全局也不是堆栈 - 它是在堆上分配的。

但实际上这里存在很大的歧义。

您应该知道堆的不同实现(例如HeapAlloc / HeapFree访问的原始Windows堆,或CRT包裹的malloc / freenew / delete)。

只有当您确定它是堆栈/全局指针或通过delete分配时,您才可以通过new运算符删除此类块。

总结:

  1. 这是一种变态的伎俩。一般不应该使用。最好用指针提供一些额外的信息,告诉它如何释放它。
  2. 如果您确定在哪个堆上分配了内存(如果它是堆内存),则只能使用它。

答案 3 :(得分:2)

  

有没有办法知道指针指向堆或堆栈的位置?

只有在分配时记住它,才能知道这一点。在这种情况下,您要做的是将指针存储在智能指针类中,并将其存储在类代码中。

如果您使用boost::shared_ptr作为示例,则可以执行此操作:

template<typename T> void no_delete(T* ptr) { /* do nothing here */ }

class YourDataType; // defined elsewhere
boost::shared_ptr<YourDataType> heap_ptr(new YourDataType()); // delete at scope end

YourDataType  stackData;
boost::shared_ptr<YourDataType> stack_ptr(&stackData, &no_delete); // never deleted

答案 4 :(得分:1)

我认为没有(简单)方法如何判断内存的分配位置(或许可以使用调试器确定它,但这显然不是你想要的)。底线是:永远不要做你在案例2中做的事情。

答案 5 :(得分:0)

在案例2中,MyData =“Value”导致内存泄漏,因为不再引用从new返回的内存。

答案 6 :(得分:0)

这样做没有简单的方法或标准方法。您可以拦截堆分配函数并将每个内存分配区域放在一个列表中。您的“IsHeap”函数应检查传递给函数的区域是否是列表中的区域。这只是一个提示 - 几乎不可能以跨平台的方式做到这一点。

但是又一次 - 为什么你需要那个?