1

我知道使用 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 中,它不是将值分配给堆内存,而是指向字符串文字。

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

通过这个程序员

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

7 回答 7

2

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

创造记忆的人应该永远是删除它的人。如果在某些情况下指针可能会丢失(或被覆盖),那么您必须保留它的副本以便正确删除。

于 2010-06-07T09:14:47.130 回答
2

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

于 2010-06-07T09:19:35.980 回答
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/访问的原始 Windows 堆HeapFree,或 CRT 包装的malloc/freenew/ delete)。

只有当您delete确定它是堆栈/全局指针或者它是通过new.

综上所述:

  1. 这是一种变态的把戏。一般不宜使用。最好用指针提供一些额外的信息,告诉如何释放它。
  2. 如果您确定内存分配在哪个堆上(如果它是堆内存),则只能使用它。
于 2010-06-07T10:50:33.117 回答
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
于 2010-06-07T11:00:38.390 回答
1

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

于 2010-06-07T09:09:10.257 回答
0

在情况 2 中,MyData = "Value" 导致内存泄漏,因为不再有对从 new 返回的内存的引用。

于 2010-06-07T09:18:45.460 回答
0

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

但话又说回来 - 你为什么需要那个?

于 2010-06-07T11:42:31.380 回答