代码之家  ›  专栏  ›  技术社区  ›  Akaanthan Ccoder

我的内存在哪里被分配,堆栈或堆,我能在运行时找到它吗?

  •  1
  • Akaanthan Ccoder  · 技术社区  · 16 年前

    我知道使用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 回复  |  直到 16 年前
        1
  •  2
  •   utnapistim    16 年前

    是否有方法知道指针指向堆或堆栈的位置?

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

    如果你使用 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
    
        2
  •  2
  •   ypnos    16 年前

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

    创建内存的人应该总是删除它的人。如果在某些情况下指针可能丢失(或被覆盖),那么您必须保留一份指针的副本,以便进行正确的删除。

        3
  •  2
  •   anon    16 年前

    在标准C++中,没有一种方法可以确定指针是否指向动态分配的内存。注意,字符串文本没有在堆栈上分配。

        4
  •  2
  •   valdo    16 年前

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

    而且,正如许多用户指出的,这是一种有点反常的情况,在这种情况下,您将一个指针传递给一个函数,如果它被分配到堆上,那么该函数将自动删除它。

    但是如果你坚持的话,仍然有一些方法可以发现哪一种记忆属于哪一种类型。

    你实际上处理三种类型的记忆

    • 栈
    • 堆
    • 全球的

    例如:

    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 或CRT包装 malloc / free 或 new / delete )

    您可以通过 删除 只有当您确定它是堆栈/全局指针或是通过 新的 .

    总之:

    1. 这是个有点变态的把戏。一般不应使用。最好用指针提供一些额外的信息,指示如何释放它。
    2. 只有在确定内存分配在哪个堆上时(如果是堆内存),才能使用它。
        5
  •  1
  •   PeterK    16 年前

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

        6
  •  0
  •   travis0xff    16 年前

    在情况2中,mydata=“value”会导致内存泄漏,因为不再有对从new返回的内存的引用。

        7
  •  0
  •   INS    16 年前

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

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

    推荐文章