在 C++ 中,delete 运算符实际上是如何工作的?(在内存级别)

In C++, how does the delete operator actually work? (At the memory level)

提问人:canh25xp 提问时间:8/11/2023 最后编辑:canh25xp 更新时间:8/11/2023 访问量:159

问:

我知道它从堆中释放内存。但是程序如何知道内存正在释放(或未释放)。如果我必须猜测,程序内存中的某个地方有某种“可用内存列表”。如果是这样的话,这个列表是如何构成的?它是由程序管理还是由操作系统管理?

我创建了一个简单的程序来尝试找出发生了什么:

#include <iostream>
int main () {
    int* ptr = new int;

    *ptr = 0xFFFFFFFF;

    std::cout << ptr << std::endl;      //some random 64-bit address
    std::cout << *ptr << std::endl;     //-1

    delete ptr;

    std::cout << ptr << std::endl;      // always 0x0000000000008123
    std::cout << *ptr << std::endl;     // Exception thrown: read access violation.

    return 0;
}

ptr 在 delete 运算符之前指向的内存位置:

memory location pointed by ptr

相同的内存位置,但在删除运算符之后:

That same memory location but after the delete operator

为什么旧的内存位置在被删除后总是充满0xDDDDDDDD?它是否表示内存已释放?如果它已经在假设的“可用内存列表”中跟踪它,为什么还要费心这样做?我的意思是写到记忆中需要一些努力(尽管在这个例子中可以忽略不计),为什么不把它留在那里,直到其他东西覆盖它。

虽然最初 ptr 指向某个随机地址,但在删除操作后,它始终指向 0x0000000000008123。它是否也是一个标记,指示指针现在指向无法访问的内存块?

(我在 Windows 10 64 位操作系统上使用 Visual Studio 2022)

只是一些指向好文章的链接也会有所帮助。谢谢。

C++ 内存管理 new-operator 删除运算符

评论

6赞 tadman 8/11/2023
您是否处于调试版本中?我不确定版本(优化)版本中是否有任何变化。在调试版本中,有这样的便利来暴露释放后使用错误。在删除内存后,通过在此处引用来有效地创建一个内存,并且内存覆盖会导致预期的崩溃。*ptr
4赞 Drew Dormann 8/11/2023
“程序是怎么知道的”您的程序不需要“知道”。删除对象后,访问该内存只是未定义的行为。
3赞 πάντα ῥεῖ 8/11/2023
可能的重复:编译器何时以及为什么会在 malloc/free/new/delete 上将内存初始化为 0xCD、0xDD 等?
1赞 tadman 8/11/2023
分配由操作系统和堆管理器处理,在 C++ 中,堆管理器封装在分配器概念中。尽管不同操作系统、不同编译器的细节差异很大,但即使在不同的版本中,用户进程(程序)也会从操作系统请求内存块,然后将这些内存块划分为内部管理的较小分配。
7赞 Paul Sanders 8/11/2023
@tadman 评论的快速跟进:该行为是特定于 Windows 的(MSVC,而不是 MinGW),并且仅适用于调试版本。正如他所说,这是为了保护你免受自己的伤害。不要指望它在其他任何地方。

答:

4赞 Eljay 8/11/2023 #1

为什么旧的内存位置在删除后总是充满0xDDDDDDDD?

平台的调试版本在释放后对内存执行0xDD。memset

标准中未规定。这是特定于您的平台的实现细节。

Q.V. 值的幻数DDDDDDDD

它是否表示内存已释放?如果它已经在假设的“可用内存列表”中跟踪它,为什么还要费心这样做?我的意思是写到记忆中需要一些努力(尽管在这个例子中可以忽略不计),为什么不把它留在那里,直到其他东西覆盖它。

它在那里帮助调试。

虽然最初 ptr 指向某个随机地址,但在删除操作之后,它始终指向 0x0000000000008123。它是否也是一个标记,指示指针现在指向无法访问的内存块?

可能不是。即使在语句之后访问也是未定义的行为。您应该在之后再次访问该值是合法的。ptrdelete ptr;ptr = nullptr;deleteptr