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

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

本文关键字:内存 是否 何测试 测试 c++      更新时间:2023-10-16

我有一些代码:

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;,编译器会抛出一个错误。这将是一个很好的证据,证明记忆是自由的。

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

如果您的代码取决于特定内存是否可用,那么您可能会遇到一些设计问题。但是,如果您确实想测试,您可以重载operator newoperator delete(以及它们相应的数组/类版本),这样它们就可以跟踪在程序可用的某些全局数据结构中分配了哪些内存位置。下面是一些玩具示例(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/deletestd::map memory编写一个自定义分配器。

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

释放内存不会改变指针的值,也不一定会改变它所指向的内存(无论如何,释放内存后都不应该尝试访问)。

您应该做的是在删除它时将指针设置为nullptr,然后您可以检查它是否为nullptr,看看它是否已释放。

示例:

int* ip = new int;
*ip = 42;
delete ip;
ip = nullptr;
// ...
if (ip) {
delete ip;
ip = nullptr;
}

为了完整起见,这里有一个链接到Doug Lea的免费商店实现页面:http://g.oswego.edu/dl/html/malloc.html.

在第一个图,即内存布局中,您可以看到每个分配块的用户数据前面都有记账信息,其中包括一个"使用中"标志。组块形成了一个链表:从给定的组块中,可以访问之后的所有组块(实际上,也可以访问之前的组块)。

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

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

为了查明给定指针所指向的内存是否已被释放,必须遍历内存块的链表,大概是从一个先于其他对象分配的虚拟对象开始。当然可能,但不可取,也不稳健@vsoftco的想法要好得多。

最后一点:总是从main返回int的人可能会哀叹这一切都是UB。他们是对的。