如何在c++中释放内存

How is memory deallocated in c++

本文关键字:释放 内存 c++      更新时间:2023-10-16

我完全理解内存的指针分配,但只在更高的级别上释放内存。我最好奇的是C++是如何跟踪哪些内存已经被释放的?

int* ptr = new int;
cout << ptr;
delete ptr;
cout << ptr;
// still pointing to the same place however it knows you can't access it or delete it again
*ptr // BAD
delete ptr // BAD

C++怎么知道我释放了那个内存。如果它只是把它变成任意的垃圾二进制数,那么当我取消引用指针时,我不是正在读取那个垃圾数字吗?

当然,c++知道这些都是分段错误。

C++如何知道我释放了内存。

当您使用delete表达式时,"C++知道"您释放了该内存。

如果它只是把它变成任意垃圾二进制数

C++不会"将[已释放的内存]变成任意垃圾二进制数"。C++只是使内存可用于其他分配。改变内存的状态可能是使用该内存的程序的其他部分的副作用——现在可以自由地这样做了

当我取消引用指针时,我不是正在读取那个垃圾号吗?

间接通过指针时,程序的行为是未定义的。

当然,c++知道这些都是分段错误。

这是你的操作系统介入的地方。你做了一些没有意义的事情,操作系统杀死了行为不端的进程。当程序的行为未定义时,这是可能发生但可能不会发生的许多事情之一。

C++不会为您跟踪内存。它不知道,也不在乎。这取决于你:程序员。(De)分配是对底层操作系统的请求。或者更准确地说,它是对libc++(或者可能是其他一些lib)的调用,可能访问也可能不访问操作系统,这是一个实现细节。无论哪种方式,操作系统(或其他库)都会跟踪哪些内存部分可供您使用。

当你试图访问操作系统没有分配给你的内存时,操作系统会发出segfault(从技术上讲,它是由CPU提出的,假设它支持内存保护,这有点复杂)。这是一个很好的情况。这样,操作系统告诉你:嘿,你的代码中有一个bug。请注意,操作系统并不关心您是否使用C++、C、Rust或其他任何东西。从操作系统的角度来看,一切都是机器代码。

然而,更糟糕的是,即使在delete之后,内存仍然可能归您的进程所有(还记得那些跟踪内存的库吗?)。因此,访问这样的指针是一种未定义的行为,任何事情都可能发生,包括代码的正确执行(这就是为什么通常很难找到这样的错误)。

如果它只是把它变成任意的垃圾二进制数,那么当我取消引用指针时,我不是正在读取那个垃圾数字吗?

谁说它变成了垃圾?底层内存的实际情况(无论操作系统是否回收它,或者它是否充满了零或一些垃圾,或者可能什么都没有)与您无关。您需要知道的是,在delete之后,使用指针不再安全。即使(或特别是)看起来还可以。

我认为您想知道delete实际上做了什么。这是:

  • 首先,它会破坏对象。如果对象有一个析构函数,它就会被调用,并执行任何编程要做的事情

  • CCD_ 5然后继续解除分配存储器本身。这意味着解除定位器函数(在C++中的大多数情况下为::operator delete())通常会获取内存对象,并将其添加到自己的内部数据结构中。也就是说,它确保对::operator new()的下一次调用能够找到释放的内存板。下一个new可能会将该内存板重新用于其他目的。

整个内存管理都是通过使用您看不到或需要知道它们存在的数据结构来实现的。::operator new()::operator delete()的实现如何组织其内部数据完全取决于实现。这与你无关。

您关心的是,语言标准定义,在将内存对象传递给delete运算符之后,对内存对象的任何访问都是未定义的行为。未定义的行为并不意味着内存需要神奇地消失,或者它变得不可访问,或者它充满了垃圾。通常这些都不会立即发生,因为使内存不可访问或用垃圾填充内存需要CPU的明确操作,因此实现通常不会触及内存中写入的内容。您只是被禁止进行进一步的访问,因为现在由系统将内存用于它喜欢的任何其他目的。

C++在内存寻址方面仍然具有强大的C继承性。C的发明是为了构建一个操作系统(Unix的第一个版本),在这个操作系统中,使用众所周知的寄存器地址或任何低级操作都是有意义的。这意味着,当你通过指针寻址内存时,作为程序员应该知道那里有什么,而语言只是信任你。

在常见的实现中,该语言向操作系统请求新的动态对象的内存块,并跟踪已使用和未使用的内存块。目标是为新的动态对象重用空闲块,而不是要求操作系统进行每次分配和取消分配。

仍然对于常见的实现,在新分配或释放的块中没有任何变化,但指针保持了空闲块的列表。AFAIK很少将内存返回到操作系统,直到进程结束。但是一个空闲的块以后可以被重新使用,这就是为什么当一个粗心的程序员试图访问一个包含已被重新使用的指针的内存块时,SEGFAULT就不远了,因为程序可以尝试使用无法映射到进程的任意内存地址。


BTW,标准所要求的唯一一点是访问对象超过其寿命,特别是在delete语句调用Undefined Behavior之后使用指针。换句话说,任何事情都可能发生,从立即崩溃到正常结果,再到后来的崩溃或程序无关位置的异常结果。。。