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

4
我知道它是从堆中释放内存。但是程序如何知道正在释放的内存(或未被释放)。如果我要猜测,程序的内存中可能有一种“可用内存列表”。如果是这样的话,那么这个列表是如何构建的呢?它是由程序还是操作系统管理的?
我创建了一个简单的程序来尝试弄清楚发生了什么:
#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;
}

指向 delete 操作符前的 ptr 所指向的内存位置。

memory location pointed by ptr

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

That same memory location but after the delete operator

为什么在删除后,那个旧的内存位置总是被填充为0xDDDDDDDD?这是一个标记,表示内存已被释放吗?如果在假设的“可用内存列表”中已经跟踪了它,为什么还要这样做呢?我的意思是,写入内存需要一些工作(即使在这个例子中可以忽略不计),为什么不只是将其保留在那里,直到有其他东西覆盖它呢?
尽管最初指针(ptr)指向某个随机地址,但在删除操作之后,它总是指向0x0000000000008123。这也是一个标记,表示指针现在指向一个无法访问的内存块吗?
(我正在使用Windows 10上的Visual Studio 2022,64位操作系统)
提供一些好文章的链接也会很有帮助。谢谢。

6
你是在调试版本中吗?我不确定在发布(优化)版本中是否有任何变化。在调试版本中,有一些方便的功能可以暴露出Use-After-Free错误。在这里引用*ptr之后,实际上你正在创建一个已经被删除的指针的使用,并且内存覆写会导致崩溃,这是预期的行为。 - tadman
4
你的程序不需要“知道”。删除一个对象后,访问该内存只是未定义行为。 - Drew Dormann
7
对@tadman评论的一个快速跟进:这种行为是特定于Windows的(MSVC,而不是MinGW),仅适用于调试版本。正如他所说,这是为了保护你自己。不要在其他地方期望这种情况。 - Paul Sanders
1
请注意,C++标准并没有描述内存管理应该如何工作(它不一定是堆)。只是对象具有自动(作用域变量)或动态存储(由new/delete管理)。请注意,您看到的数字是特定于编译器的调试值,func fact的更多信息在这里:https://www.liquisearch.com/deadbeef/magic_debug_values - Pepijn Kramer
显示剩余2条评论
1个回答

4
为什么在删除后,那个旧的内存位置总是被填充为0xDDDDDDDD?
你所使用的平台的调试构建会将内存通过 `memset` 设置为0xDD(十进制)。 这并没有在标准中规定,它是与您的平台相关的实现细节。
有关 `DDDDDDDD` 值,请参考魔数
这是为了帮助调试而存在的。
即使指针最初指向某个随机地址,在执行删除操作后,它总是指向0x0000000000008123。这也是一个指示指针现在指向不可访问内存块的标记吗?
可能不行。即使在delete ptr;语句之后访问ptr,这也是未定义行为。你应该在delete之后将ptr赋值为nullptr,这样才能合法地再次访问ptr的值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接