ChatGPT解决这个技术问题 Extra ChatGPT

删除 NULL 指针是否安全?

删除 NULL 指针是否安全?

它是一种好的编码风格吗?

好的做法是编写 C++ 程序而不调用 delete。请改用 RAII。也就是说,使用 std::vector<T> v(100); 而不是 T* p = new T[100];,使用像 unique_ptr<T>shared_ptr<T> 这样的智能指针来处理删除而不是原始指针等。
感谢 make_shared (c++11) 和 make_unique (c++14) 您的程序应该包含 zero of newdelete
可能仍然有一些罕见的情况需要新建/删除,例如 atomic:atomic> 是不允许的,并且 atomic> 的开销在某些情况下可能是不可接受的。
要使用 RAII 声明一个具有资源管理的类,您需要调用 new 和 delete 对吗?或者您说有一些模板类可以隐藏这个。
@VinGarcia 重点是大多数 user/client(即:非库)代码永远不必编写 newdelete。设计用于管理资源的类,标准组件无法完成工作,当然可以做他们需要做的事情,但关键是 他们用他们管理的内存做丑陋的事情,而不是最终用户代码。因此,创建您自己的库/帮助类来执行 new/delete,并使用该类代替它们。

r
ruslik

delete 无论如何都会执行检查,因此在您这边进行检查会增加开销并且看起来更丑陋。 非常好的做法是在 delete 之后将指针设置为 NULL(有助于避免重复删除和其他类似的内存损坏问题)。

如果默认情况下 delete 将参数设置为 NULL ,我也很喜欢

#define my_delete(x) {delete x; x = NULL;}

(我知道 R 和 L 值,但这不是很好吗?)


请注意,即使您在删除时将一个指针设置为 NULL,仍然可能有几个其他指针指向同一个对象。
在我的代码中的大多数情况下,指针一旦被删除就会超出范围。比仅将其设置为 NULL 安全得多。
一个非常神的做法是在删除后不将指针设置为 NULL。删除指针后将其设置为 NULL 会伪装内存分配错误,这是一件非常糟糕的事情。正确的程序不会两次删除指针,而两次删除指针的程序应该会崩溃。
@Alice:标准在这方面所说的无关紧要。该标准定义删除空指针在 30 年前出于某种荒谬的原因而有效,因此它是合法的(很可能是 C 遗留物)。但是两次删除同一个指针(即使在改变它的位模式之后)仍然是一个严重的程序错误。不是标准的措辞,而是程序逻辑和所有权。与删除空指针一样,由于空指针不对应任何对象,因此不可能删除任何内容。程序必须准确地知道一个对象是否有效、谁拥有它以及何时可以删除它。
@Damon但是,尽管废除了您严格的所有权规则,但无锁结构显然比基于锁的结构更健壮。是的,我的同事确实很喜欢我,因为这些结构提供了增强的执行配置文件以及它们维护的严格的线程安全性,这使得对代码的推理变得更容易(非常适合维护)。但是,这一切以及您暗示的人身攻击都与正确性、有效性或所有权的任何定义无关。您提出的建议是一个很好的经验法则,但它不是普遍规律,也没有载入标准。
C
Chubsdad

来自 C++0x 标准草案。

$5.3.5/2 - "[...]在任一替代方案中,delete 的操作数的值可能是空指针值。[...'"

当然,没有人会“删除”具有 NULL 值的指针,但这样做是安全的。理想情况下,不应该有删除 NULL 指针的代码。但是当指针的删除(例如在容器中)发生在循环中时,它有时很有用。由于删除 NULL 指针值是安全的,因此可以真正编写删除逻辑,而无需显式检查要删除的 NULL 操作数。

顺便说一句,C 标准 $7.20.3.2 还说 NULL 指针上的“免费”没有任何作用。

free 函数会导致 ptr 指向的空间被释放,也就是说,可用于进一步分配。如果 ptr 是空指针,则不会发生任何操作。


如果它没有故意在非优化代码中引入低效率,我真的很喜欢这个答案的引用。正如接受的答案所述,删除空指针是无操作的。因此,在删除指针之前检查指针是否为空是完全无关的。
J
Jonathan Leffler

是的,它是安全的。

删除空指针没有害处;如果未分配的指针被初始化为零然后简单地删除,它通常会减少函数尾部的测试次数。

由于前面的句子引起了混淆,一个例子——不是例外安全的——描述的内容:

void somefunc(void)
{
    SomeType *pst = 0;
    AnotherType *pat = 0;

    …
    pst = new SomeType;
    …
    if (…)
    {
        pat = new AnotherType[10];
        …
    }
    if (…)
    {
        …code using pat sometimes…
    }

    delete[] pat;
    delete pst;
}

示例代码可以挑选出各种各样的细节,但这个概念(我希望)很清楚。指针变量初始化为零,因此函数末尾的delete操作不需要在源代码中测试它们是否为非空;无论如何,库代码都会执行该检查。


我不得不读了几遍才能理解它。您一定是指在方法的顶部或在其期间将它们初始化为零,而不是在尾部,确定吗?否则,您只需同时删除归零和删除。
@EJP:函数的一个不完全不可信的轮廓可能是:void func(void) { X *x1 = 0; Y *y1 = 0; … x1 = new[10] X; … y1 = new[10] Y; … delete[] y1; delete[] x1; }。我没有展示任何块结构或跳转,但由于开始时的初始化,最后的 delete[] 操作是安全的。如果在分配 x1 之后和分配 y1 之前有什么东西跳到最后并且没有初始化 y1,那么就会有未定义的行为 - 虽然代码可以测试空性(x1 y1) 在删除之前,不需要这样做。
很高兴知道它是安全的。但是仍然可以使用“if (mypointer)”子句,因为它向阅读代码的人发出信号,表明所讨论的指针可能尚未设置,并且它表明这不是错误或尴尬,而是一部分设计(我承认也可以使用评论)。
B
Brian R. Bondy

删除空指针无效。这不一定是好的编码风格,因为它不是必需的,但它也不错。

如果您正在寻找良好的编码实践,请考虑改用智能指针,那么您根本不需要delete


人们想要删除 NULL 指针的时间是当他们不确定它是否包含 NULL 时......如果他们知道它是 NULL 那么他们就不会考虑删除并因此询问;-)。
@Tony:我的意思只是它不会产生任何影响,并且删除有时包含 NULL 的指针的这种代码的存在并不一定是坏事。
IMO 冗余检查对于性能、可读性和可维护性来说肯定是不好的。
@paulm OP 当然不是在谈论那种坏事,更多的是 Seg Fault / UB 那种坏事。
T
Toby Speight

为了补充 ruslik's answer,在 C++14 中,您可以使用以下构造:

delete std::exchange(heapObject, nullptr);

佚名

除非您重载删除运算符,否则它是安全的。如果您重载了 delete 运算符并且不处理 null 条件,那么它根本不安全。


您能否为您的答案添加任何解释?
S
Sitesh

有一个关于这个问题的 FAQ 回答了这个问题。

C++ 语言保证,如果 p 为 null,则 delete p 不会执行任何操作。由于您可能会倒退测试,并且由于大多数测试方法强制您显式测试每个分支点,因此您不应该放入多余的 if 测试。


p
pheest

我经历过 delete[] NULL (即数组语法)是不安全的(VS2010)。我不确定这是否符合 C++ 标准。

删除 NULL(标量语法)是安全的。


这是违法的,我不相信。
您应该能够删除任何空指针。因此,如果它对您不利,那么您可能在显示它的代码中存在错误。
§5.3.2 在第二种选择(删除数组)中,delete 的操作数的值可以是空指针值或由前一个数组 new 表达式产生的指针值。
@Opux VS2010 在答案中的行为。正如其他评论中所解释的,delete[] NULL 是安全的。
@Opux这就是为什么我写“我不相信”而不是“那是错误的”。但我仍然没有,这将是一个非常离谱的、愚蠢的违反标准的行为。 VC++ 实际上通常非常擅长遵循标准的限制,并且它违反这些限制的地方在历史上是有意义的。