删除 null void* 指针是未定义的行为吗?

Posted

技术标签:

【中文标题】删除 null void* 指针是未定义的行为吗?【英文标题】:Is it undefined behaviour to delete a null void* pointer? 【发布时间】:2011-09-04 13:26:31 【问题描述】:

我知道delete一个空指针是无操作的:

在任一替代方法中,如果 delete 的操作数的值为空指针,则该操作无效。(C++ 标准 5.3.5 [expr.delete] p2

此外,删除void* 指针是未定义的行为,因为没有void 类型的对象,所以无法调用析构函数:

在第一种选择(delete object)中,delete 的操作数的值应该是指向非数组对象的指针或指向表示此类对象的基类的子对象的指针。 如果不是,则行为未定义。(C++ 标准 5.3.5 [expr.delete] p2

现在,通常我认为首先列出的内容会覆盖后面列出的内容,但是如下所示的 null void* 指针呢?

void* p = 0;
delete p; // UB or well-defined?

【问题讨论】:

我希望无论类型如何,都会有一个定义明确的行为,即陷入“删除 NULL 指针”。理想情况下,实际释放和调用析构函数应该遵循对 NULL 的检查。 在 g++ 上,我收到警告 deleting ‘void*’ is undefined。我认为它的 UB(不是 100% 肯定)。 @Prasoon:VS2010 不会通过/W4 发出这样的警告。另外,我相信 g++ 在这种情况下不会检查指针的值,而只会作用于类型? 最大应该是无害的UB。 :)) 投票重新打开:问题的关键是指针的值何时为NULL。上下文不同,并且不满足标准引用的先决条件(即“如果对象的静态类型与动态类型不匹配并且没有虚拟析构函数,UB”)(有没有对象,所以考虑静态和动态类型没有意义),我们可以依赖前面提到的空指针是有效参数。所以我的回答是:这不是 UB。 【参考方案1】:

我想知道您如何才能达到仅当指针为空时才删除指针的情况。但停留在语言律师模式...

在 C++ 03 中

5.3.5/1

delete 的操作数应为指针类型或类类型,可单次转换为指针类型。

void* 是指针类型,所以空指针满足静态要求。

5.3.5/2

在任一备选方案 [deletedelete[]] 中,如果 delete 的操作数的值为空指针,则该操作无效。

这给出了想要的行为。

5.3.5/3

在第一种选择(删除对象)中,如果操作数的静态类型与其动态类型不同,则静态类型应为操作数动态类型的基类,并且静态类型应具有虚拟析构函数或行为未定义。

这无关紧要,空指针不引用要检查附加约束的对象。

在 C++ 0X 中

5.3.5/1

操作数应具有指向对象类型的指针,或具有单个非显式转换函数 (12.3.2) 到指向对象类型的指针的类类型。

void* 不是指向对象类型的指针,因此应该被拒绝。

【讨论】:

有趣的是,这将不再适用于 C++0x。 :) 另外,请注意,您在顶部的假设并不完全正确:如果我愿意,我只会有条件地删除一个空 void* 指针。 ;) 在任何其他情况下,我都会删除一个有效的对象。 类似this。查看默认构造函数,我不想检查有效的_delete 函数指针,我知道deleteing 0 是无操作的,现在唯一的问题是:我应该对@ 进行什么参数化987654328@函数? :) 在 void 上专门设置 erased_delete,使其成为 noop。如果有人给你一个非 null void* 并且将在未来的 C++0X 中编译,它就不是 UB。 (顺便说一句,我假设您为了简单起见而省略了赋值运算符) erased_delete<void> 没有在我的最终实现中使用,我只是将其转换为 int*。 :) 但是专注于void 无论如何可能是个好主意,所以我可以在其中使用static_assert【参考方案2】:

§5.3.5/3 说,

在第一种选择中(删除 对象),如果是静态类型 操作数与其动态不同 类型,静态类型应为基类 操作数的动态类型的类 并且静态类型应具有 虚拟析构函数或行为是 不明确的。在第二种选择中 (删除数组)如果是动态类型 要删除的对象不同于 它的静态类型,行为是 未定义73

在脚注中说,

73 - 这意味着不能使用 void* 类型的指针删除对象,因为没有 void 类型的对象。

是的,它的 UB。

现在,一旦它进入未定义行为的城市,它是否为 null 都无关紧要,因为它的行为不能保持精确地定义,因为它已经 在未定义行为的城市中获得了住所。


编辑:

得到另一个同样引用并说它的 UB 的主题:

Is it safe to delete a void pointer?

【讨论】:

+1,良好的链接和参考;但我们也应该记住,指针是0,那会不会有害? @Nawaz:你的帖子只是Neil Butterworth's post here的副本。 @Nawaz :我看不出你的帖子的相关性。 :-) @iammilind:问题是一旦你有未定义的行为,编译器就会做一些疯狂的事情。因此,参数的无效性是不相关的,因为它不一定会导致运行时检查。阅读 LLVM 博客上 Chris Lattner 的优秀系列文章,了解 C 中的一些未定义行为,了解编译器的作用:) 这里没有对象,引用无关紧要。【参考方案3】:

我相信它的未定义行为。不允许使用new void(不允许创建void 类型的对象),因此在void* 上调用delete 也不应该有意义。它是否指向NULL 并不重要。我永远不会在我的代码中的任何地方使用这样的东西。

【讨论】:

我也不会使用它,它只是在涉及模板化删除函数、指向此类函数的函数指针和我的void* 的默认值的某些情况下才想到的。 :)

以上是关于删除 null void* 指针是未定义的行为吗?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在已删除指针上调用非虚拟成员函数是未定义的行为?

如果 a 未初始化,a^a 或 a-a 是未定义的行为吗?

别名可变原始指针 (*mut T) 会导致未定义的行为吗?

我们可以减去 NULL 指针吗?

计算地址差异是未定义的行为吗?

C 中的 sum+++i 是未定义的行为吗? [复制]