是否在未定义的 NULL 指针上调用函数? [复制]

Posted

技术标签:

【中文标题】是否在未定义的 NULL 指针上调用函数? [复制]【英文标题】:Is calling a function on a NULL pointer undefined? [duplicate] 【发布时间】:2012-03-18 09:03:34 【问题描述】:

可能重复:When does invoking a member function on a null instance result in undefined behavior?C++ standard: dereferencing NULL pointer to get a reference?

假设我有课:

class A

public:
   void foo()  cout << "foo"; 
;

然后像这样调用 foo:

A* a = NULL;
a->foo();

我怀疑这会调用未定义的行为,因为它等效于 (*a).foo()(或者是吗?),并且取消引用 NULL UB,但我找不到参考。谁能帮我吗?还是定义了?

不,函数不是virtual。不,我没有访问任何成员。

编辑:我投票结束这个问题,但不会删除它,因为我自己找不到重复的内容,我怀疑这个标题可能更容易被其他人找到。

【问题讨论】:

我认为标准中没有任何限制,例如,通过 vtable 查找实现 all 成员函数。因此,在这样的实现中,您确实会取消对 NULL 的引用(而不仅仅是静态绑定到 a::foo)。不过,我无法提供标准报价。 所以问题是:“什么是引用说你不能取消引用 NULL 指针?” 本帖对题目有详细分析:***.com/questions/669742/… 相关:***.com/questions/5248877/… [in this post][1] 对此主题进行了广泛的讨论。 [1]:***.com/questions/2474018/… 【参考方案1】:

这里有介绍:http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232

IS 中至少有几个地方表明通过 空指针产生未定义的行为:1.9 [intro.execution] 第 4 段给出了“取消引用空指针”作为示例 未定义的行为,以及 8.3.2 [dcl.ref] 第 4 段(在注释中)使用 这种本应未定义的行为作为 不存在“空引用”。

但是,5.3.1 [expr.unary.op] 第 1 段,它描述了一元 "*" 运算符,并不表示行为未定义,如果 正如人们所期望的那样,操作数是一个空指针。此外,至少 一段给出了取消引用空指针定义明确的行为: 5.2.8 [expr.typeid] 第 2 段说

如果通过应用一元*运算符获得左值表达式 指向一个指针并且指针是一个空指针值(4.10 [conv.ptr]), typeid 表达式抛出 bad_typeid 异常 (18.7.3 [bad.typeid])。

这是不一致的,应该清理。

如果您想了解更多信息,请在链接中阅读更多信息。

【讨论】:

这是一个很好的引用并且非常中肯。但是,这几乎没有“覆盖”它;它只是同意这是一个需要回答的问题;它不会自己回答。 @LightnessRacesinOrbit:我不会抄袭整个页面。 如果你这样做了,它不会改变任何东西。未解决的问题不是规范。【参考方案2】:

我知道至少有一种情况,这种习惯用法不仅被允许而且被依赖:Microsoft 的 MFC 类 CWnd 提供了一个成员函数 GetSafeHwnd,它测试是否为 this==NULL 并在不访问任何成员变量的情况下返回。

当然有很多人会声称 MFC 是一个非常糟糕的例子。

无论行为是否未定义,在实践中它都不太可能表现得不好。编译器会将a-&gt;foo() 视为A::foo(a),只要foo 不是虚拟的,它就不会在调用站点进行取消引用。

【讨论】:

这就是我要问的确切原因。我在生产代码中看到了这一点,我假设它是 UB,但我也想证明它。 @LuchianGrigore,这是个好问题。 GetSafeHwnd 的实现总是让我有点紧张,尽管我明白它为什么起作用。【参考方案3】:

我正在寻找表明a-&gt;x 等同于(*a).x 的参考。

这里是:

[C++11: 5.2.5/2]: 对于第一个选项(点),第一个表达式应具有完整的类类型。对于第二个选项(箭头),第一个表达式应具有指向完整类类型的指针。 表达式E1-&gt;E2 被转换为等价形式(*(E1)).E2 5.2.5 的其余部分将仅处理第一个选项(点)。在任何一种情况下,id-expression 都应命名该类或其基类之一的成员。 [ 注意: 因为一个类的名字被插入到它的类范围中(第 9 条),一个类的名字也被认为是这个类的一个嵌套成员。 —结束注释 ] [ 注释: 3.4.5 描述了如何在.-&gt; 运算符之后查找名称。 ——尾注 ]

不幸的是,没有直接引用来取消引用 UB 的 NULL 指针。您可能会在此问题下找到更多信息:When does invoking a member function on a null instance result in undefined behavior?

【讨论】:

8.3.2/4 "References"):注意:特别是,空引用不能存在于定义良好的程序中,因为创建此类引用的唯一方法是将其绑定到通过取消引用空指针获得的“对象”,这会导致未定义的行为。 ***.com/questions/2727834/… @0A0D:这是不规范的。 GMan 在我链接到的帖子中更详细地探讨了该主题,包括引用您在评论中发布的同一非规范性段落,但以一些平衡的推理更进一步。我建议阅读它。【参考方案4】:

是的,因为 a 的 UB 在被取消引用之前还没有被初始化为指向一个有效的内存位置。

【讨论】:

我正在寻找标准的报价。您的回答没有提供比我在问题中已经说明的更多信息。 PS我没有投反对票。 @LuchianGrigore:众所周知,这是 UB,但标准不是免费的,我也没有。 有一些免费的草稿,和正式版差别不大。 @LuchianGrigore:好的,我应该买一个。我忘了草稿实际上是免费的。

以上是关于是否在未定义的 NULL 指针上调用函数? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

当我在 NULL 对象指针上调用成员函数时会发生啥? [复制]

不理解为什么调用一级指针作函数参数时候,就不能把myp1 = NULL,

在未初始化的对象(空指针)上调用方法

将 NULL 指针转换为对象并调用其成员函数之一有实际好处吗?

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

Vue 警告我无法在未定义、null 或原始值上设置反应属性:null