指针和"NULL"

Pointers and "NULL"

本文关键字:NULL 指针      更新时间:2023-10-16

我只是想知道初始化指向 NULL 的指针或在删除指针后将其设置为 NULL 是否有任何好处。

我在一些论坛上读到,删除后设置为 NULL 是不必要的,有些编译器甚至不考虑该行。如果它什么也没改变,为什么有些人会使用它?

之后

delete ptr;

指针对象可能仍ptr保持与以前相同的值。它现在指向一个不存在的对象,因此尝试取消引用它,甚至引用它的值,具有未定义的行为。如果您不小心执行以下操作:

delete ptr;
// ...
*ptr = 42;

它可能会悄悄地破坏您不再拥有的内存,或者可能已重新分配给其他对象的内存。

将指针设置为 null:

delete ptr;
ptr = NULL; // or 0 or nullptr

意味着意外尝试取消引用指针更有可能导致程序崩溃。(在这种情况下,崩溃是一件好事。

窃取凯西的评论:

C++11 §3.7.4.2 释放函数 [basic.stc.dynamic.deallocation] 第 4 段:

如果参数给出给标准中的释放函数 库是一个不是空指针值 (4.10) 的指针,即 释放函数应释放 指针,使引用 已解除分配的存储。使用无效指针值的影响 (包括将其传递给释放函数)是未定义的。

如果指针对象处于其生存期的最后,则无需费心,因为不可能意外使用它:

{
int *ptr = new int(42);
// ...
delete ptr;
}

正如其他人所说,无论如何,您可能最好使用一些更高级别的结构,例如智能指针。

如果你把它初始化为 null,然后立即给它分配一些其他值,或者如果你给它分配 null(删除后),然后立即让它超出范围,这是毫无意义的。

但是,如果某些其他代码可能想要使用它指向的对象,而指针仍然存在并且该对象可能不存在,那么您将希望能够向该代码表示不存在任何内容。如果指针设置为 null,则代码可以在尝试取消引用指针之前检查 null。

从优化编译器的角度来看,没有任何好处:冗余存储将被消除。 现代编译器将能够看到该值未被使用并删除它们。

从人类读者的角度来看,可能会有好处:它可能使代码更容易理解,并可能有助于减少因使用未初始化或释放的数据而导致的错误。 但是,在某些情况下,初始化为 NULL 实际上可以隐藏问题,因为如果您尝试使用值而不先初始化它,大多数编译器会发出警告。

不过,我个人的观点是,您应该正确初始化变量,使其最终值尽可能接近声明它,并让编译器警告您,如果您错过了执行路径,那么一旦变量的值被释放,您就不应该使用它,因为它也超出了范围。 使用智能指针释放数据在这里有所帮助。

只是为了澄清其他答案,如果您尝试写入或读取由未初始化的指针引用的内存,或者指向已deleted 或freed 的内存的指针,程序可能不会崩溃,或者可能在写入/读取后很长时间崩溃,因此很难找到错误。

如果使用空指针执行相同的操作,则程序将(可能)立即崩溃,从而使调试更容易。

始终初始化指向某物的指针是一种很好的编码习惯。在C++中,未初始化指针的值是未定义的,因此如果您有一行,例如:

int* p;

p采用p占用的内存中碰巧的任何内容的值(不是它指向的内容,而是指针值本身的内存)。有时初始化指向 NULL 的指针是有意义的,但这实际上取决于您的代码。

至于删除后设置NULL,这是少数人遵循的习惯。好处是可以在 NULL 上毫无问题地调用delete,而指针上的双delete可能会导致未定义的行为。在 RAII 的想法流行之前,有些人遵循"安全总比抱歉好"的方法在多个代码路径上调用delete。在这些情况下,如果未将指针设置为 NULL,则可能会意外delete指针两次,因此将其设置为 NULL 可以缓解该问题。就个人而言,我认为如果您的指针没有适当的所有权,这是设计不佳的标志,您必须猜测何时调用delete

在现代硬件和操作系统上,空指针是无效指针。因此,当您尝试访问它时,您的程序将被您的硬件或操作系统关闭。这可以提供帮助,因为它将保证您的程序不会尝试访问先前释放的某些内存并导致未定义的行为并侥幸逃脱。

它还可以帮助调试,因为您可以查看该内存是否已在断点处释放。

但理想情况下,一个好的C++程序员应该尽量避免使用原始指针,并在可能的情况下使用 RAII。

初始化指向NULL/nullptr的指针,在任何上下文中都是一个普遍的想法,因为悬空指针可以引用实际内存。如果在指针使用后将其取消引用,则现代平台通常会干净地崩溃。

出于同样的原因,在完成null后设置指向的指针可能是一个好主意,但我个人认为,如果您不保留变量,例如当它是局部变量时,它并不是特别有用。

不过,更好的做法是完全避免原始指针。C++11 提供了用这三个语义包装指针的unique_ptrshared_ptrweak_ptr(<memory>)。使用它们,您永远不需要手动删除任何内容,并且几乎不需要设置任何内容来nullptr自己。