c ++如何测试堆中的某一位内存是否空闲?

c++ How to test whether a certain bit of memory in the heap is free?

我有一些代码:

int *ip = new int;

*ip = 42;
cout << *ip << endl;
cout << ip << endl;

delete ip;
cout << *ip << endl;
cout << ip << endl;

输出为:

42
0x100105400
42
0x100105400

从指针的值和它指向的内存上的值,我想我无法知道ip指向的堆中的内存位是否空闲?

我知道如果我在代码后再次添加 delete ip;,编译器会抛出错误。这将是内存空闲的一个很好的证据。

但是,我如何和平地测试它是否免费,以便我可以将其作为决定我代码中进一步操作的条件?

But, how can I test whether it's free peacefully, so that I can use it as a condition to decide further operations in my code?

释放内存不会改变指针的值,也不一定会改变它指向的内存(一旦它被释放,你不应该尝试访问它)。

你应该做的是在删除它的时候将指针指向nullptr,然后你可以检查它是否nullptr,看看它是否被释放了。

示例:

int* ip = new int;

*ip = 42;

delete ip;
ip = nullptr;

// ...

if (ip) {
    delete ip;
    ip = nullptr;
}

如果您的代码取决于特定内存是否空闲,您可能会遇到一些设计问题。但是,如果您确实想要测试,您可以重载 operator newoperator delete(以及它们相应的 array/class 版本),以便它们跟踪在某些可用的全局数据结构中分配了哪些内存位置到你的程序。这是一些玩具示例 (live on ideone.com),它定义了一个位置 new,用于跟踪分配的内存(和大小)。

#include <iostream>
#include <map>

std::map<void*, std::size_t> memory; // globally allocated memory map
struct tag {}; // tag for placement new's so we don't overload the global ones

void* operator new(std::size_t size, const tag&)
{
    void* addr = malloc(size);
    memory[addr] = size;
    return addr;
}

void* operator new[](std::size_t size, const tag&) // for arrays
{
    return operator new(size, tag());
}

void operator delete(void *p) noexcept
{
    memory.erase(p);
    free(p);
}

void operator delete[](void *p) noexcept // for arrays
{
    operator delete(p);
}

void display_memory()
{
    std::cout << "Allocated heap memory: " << std::endl;
    for (auto && elem : memory)
    {
        std::cout << "\tADDR: " << elem.first << " "
                  << "SIZE: "  << elem.second << std::endl;
    }
}

bool is_allocated(void* p)
{
    return (memory.find(p) != memory.end());
}

int main()
{
    int *p = new(tag()) int[10];
    char *c = new(tag()) char;

    // test if p is allocated
    std::cout << std::boolalpha << "Allocated: "
              << is_allocated(p) << std::endl;

    // display the allocated memory
    display_memory();

    // remove p
    delete[] p;

    // test again if p is allocated
    std::cout << std::boolalpha << "Allocated: "
              << is_allocated(p) << std::endl;

    display_memory();

    // remove c
    delete c;

    display_memory();
}

编辑: 我意识到上面的代码可能存在一些问题。在函数

void operator delete(void *p) noexcept
{
    memory.erase(p);
    free(p);
}

memory.erase(p) 也调用了 operator delete,所以你最终可能会遇到一些讨厌的递归(出于某种原因,上面的代码只进入递归一次)。解决方法是为使用 malloc/free 而不是全局 operator new/delete.

std::map memory 编写自定义分配器

为了完整起见,这里是 link Doug Lea 的免费商店实施页面:http://g.oswego.edu/dl/html/malloc.html

在第一个图表中,内存布局,您可以看到每个分配块的用户数据前面都有簿记信息,其中包括一个 "in-use" 标志。这些块形成一个 linked 列表:从一个给定的块可以到达之后的所有块(实际上,也可以到达之前的所有块)。

知道数据大小和对齐要求后,可以在给定系统上猜测该信息可能与 returned 指针相关的信息;在我的 x86_64-pc-cygwin 上,状态字节是 returned 地址之前的 8 个字节,"in use" 标志是第一个字节的 2 位。

虽然这看起来很有希望,但有一个问题:四处寻找我可以看到 delete() 合并相邻块,以便簿记信息仅在融合块的开头有效:如果你有两个指针 p1 , p2 指向按顺序分配的空闲存储中的内存,并且您再次按顺序删除,p2 簿记位置的内存不会刷新以反映 p2 已被释放。相反,p1 之前的簿记区域现在指示以下内存块具有单个块组合的大小。

为了找出给定指针指向的内存是否已被释放,必须遍历 linked 内存块列表,大概是从一个在任何事情之前分配的虚拟对象开始别的。当然可能,但不可取且不稳健。 @vsoftco 的想法要好得多。

最后一点:总是从 main return int 的人可能会抱怨所有这些都是 UB。他们是对的。