2013-12-20 91 views
1

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

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

+4

它可能有助于调试。 – 2013-12-20 20:29:12

+0

要明确。 –

+4

理想情况下,你不应该使用拥有指针,而是使用智能指针。 – 111111

回答

1

优化编译器的观点没有任何好处:冗余存储将被淘汰。现代编译器将能够看到该值不被使用并将其删除。

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

我个人的看法是,你应该尽量使用它的最终值尽可能地初始化变量,并尽可能地声明它,让编译器警告你,如果你错过了一个执行路径,那么你不应该能够使用一旦它的值被释放,因为它也超出了范围。使用智能指针释放数据有助于在这里。

0

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

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

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

2

如果您将它初始化为null,然后立即为其指定其他值,或者如果您为其指定了null(删除后),然后立即让它超出范围,那就没有意义了。然而,如果有一些其他代码可能想要使用它指向的对象,而指针仍然存在并且该对象可能不存在,那么您将希望能够表示该代码没有任何东西那里。如果指针设置为null,则代码可以在尝试取消引用指针之前检查null。

0

在任何情况下初始化一个指向NULL/nullptr的指针通常是一个想法,因为悬挂指针可以引用实际的内存。如果您在使用指针后取消其引用,那么现代平台通常会干净地崩溃。

设置一个指针到null完成后可能是一个好主意,出于同样的原因,但我个人认为它是特别有用的,如果你不保留变量周围,就像它是什么时候一个局部变量。

尽管如此,更好的做法是完全避免原始指针。 C++ 11提供了unique_ptr,shared_ptrweak_ptr(在<memory>),它们用这三种语义来包装指针。使用它们,你永远不需要手动删除任何东西,你几乎不需要自己设置任何东西到nullptr

1

为了澄清其他答案,如果您尝试写入或读取由未初始化的指针引用的内存或指向已经为delete d或free d的内存的指针,则程序可能不会崩溃,或者可能在写入/读取之后很长时间会崩溃,使得很难找到该错误。

如果你对空指针做同样的事情,程序会(可能)立即崩溃,使调试更容易。

8

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; 
} 

正如其他人说,你是无论如何,最好使用像智能指针这样的更高级别的构造。

+4

+1对于'在这种情况下崩溃是一件好事。' –

+1

+1“它现在指向一个不存在的对象”之后的后续信息更具信息性(特别是甚至*评估指针值的无效性),因为它的值是* indeterminate *(即我们不知道*这是什么)。但试试我可能在标准中找不到任何提及后“删除”的参考。如果可以找到该参考,如果它存在,那将是非常棒的,但无论如何,如果没有它,这个答案就会很好。 – WhozCraig

+2

@WhozCraig C++ 11§3.7.4.2解除分配函数[basic.stc.dynamic.deallocation]第4段:“如果给标准库中的解除分配函数的参数是一个非空指针的指针 值( 4.10),解除分配函数应释放指针引用的存储空间,使所有指向解除分配存储的任何部分的指针无效。使用无效指针值 (包括将它传递给解除分配函数)的效果未定义。 “ – Casey

1

总是初始化指向的东西是很好的编码习惯。在C++中,未初始化的指针的值是不确定的,所以如果你有这样一行:

int* p; 

p承担任何碰巧在内存p占用(不是它指向的值,但指针值本身的内存)。有时候,将指针初始化为NULL是有意义的,但这取决于你的代码。

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

+2

删除内存两次并不是它解决的唯一问题。如果'p == nullptr',并且您尝试使用'p':'* p = 42;',您将立即得到一个运行时异常。如果'p'是一个“有效”的地址,并且你修改了它,你可能会压缩已被重新分配的内存,并且很难发现错误。 –