当我在 NULL 对象指针上调用成员函数时会发生啥? [复制]
Posted
技术标签:
【中文标题】当我在 NULL 对象指针上调用成员函数时会发生啥? [复制]【英文标题】:What will happen when I call a member function on a NULL object pointer? [duplicate]当我在 NULL 对象指针上调用成员函数时会发生什么? [复制] 【发布时间】:2011-02-01 18:32:15 【问题描述】:面试问题如下:
class A
public:
void fun()
std::cout << "fun" << std::endl;
;
A* a = NULL;
a->fun();
执行这段代码会发生什么,为什么?
另见:
When does invoking a member function on a null instance result in undefined behavior?【问题讨论】:
未定义的行为。或者如果将代码从字面上看作为一个完整的单元,则会出现编译错误。 棘手的问题,还是棘手的问题?它不会编译,因为Class
(大写 C)不是 C++ 关键字。
@duffymo,我已经检查过了,它打印出“有趣”,我不知道为什么,所以我在这里问。
@Rajendra 当你用你的特定编译器编译它时它做什么并不重要,C++ 标准说取消引用一个空指针是未定义的。所以你的编译器的下一次迭代,或者另一个编译器可能会做一些不同的事情,但同样是未定义的。
这有多“棘手”?这是基本的,如果你问我,那是毫无意义的。创建一个指针,将其初始化为 NULL 并在下一行使用它 - 多么“典型”!
【参考方案1】:
这是未定义的行为,所以任何事情都可能发生。
一个可能的结果是它只打印"fun"
,因为该方法不访问它被调用的对象的任何成员变量(不需要访问对象应该存在的内存,所以访问违规不一定会发生)。
【讨论】:
@Kamal 如果函数不是虚拟的,那么它会被静态链接。不需要一张桌子。不需要对象指针。 @Kamal 编译器可以使用静态信息(它知道指针的类型为 A)来查找函数。编译器可以做到这一点的事实确实意味着它必须并且不会阻止这是未定义的行为。 @Kamal:谁说它必须存储?编译器可以将函数调用转换为A::fun(a)
。
@Kamal:编译器知道对象的类型和应该在编译时调用的方法的地址,并且可以“硬编码”地插入该地址。无需在运行时查找任何内容。 (这并不意味着编译器必须这样做,但它可以。)
该对象符合 POD 的条件,因此我不认为编译器允许在对象中包含任何隐藏字段以用于查找成员函数。既然如此,如果编译器找不到正确的成员函数,那将是非常令人惊讶的。由于 POD 类型的生命周期规则,甚至不允许使用将对象指针映射到成员函数的查找表。这里成员函数解析的灵活性很小。实际上,这是未定义行为这一事实允许编译器注入显式空检查以帮助调试。【参考方案2】:
按照标准,这是未定义的行为,因此是一件非常糟糕的事情。实际上,大多数编程平台(跨 X86 和其他几种架构)都可以正常运行。
为什么?考虑如何在 C++ 中实现类函数。这不是虚函数,因此可以是对已知地址的静态调用。 在 x86 汇编中,我们可以将其视为
mov A, 0
mov ecx, A
call a__fun
由于 a__fun 不需要实例数据,即使它接收到 null this 指针,也不会发生任何事情。
仍然是糟糕的代码,任何编译器都会尖叫,但它可以运行。
【讨论】:
是的,这是未定义的行为。是的,这可能在给定架构上按预期运行。然而,明智的做法是始终牢记以下智慧:“未定义行为的最坏结果是它会按照您的预期行事。” 这个答案忽略了所有的“优化”。如果编译器可以看到指针为空,它可以完全忽略调用。您过于关注调用 if 发生的低级细节。但我们称之为未定义是有原因的。这与“基本有效”不同。【参考方案3】:在大多数现代计算机上,最可能的行为是它会运行并打印“有趣”,因为:
C++ 在调用函数之前不会检查指针是否为 NULLfun()
不是虚拟的,因此无需引用 vtable 来调用 fun()
fun()
从不访问 A
中的任何成员变量,因此不需要取消引用
空的this
指针。
【讨论】:
3.A* a = NULL; a->fun();
在函数体之外是非法的。
但我认为我们仍然可以弯曲我们的思想并理解他的意思【参考方案4】:
我们不知道会发生什么。一切可能发生,因为程序暴露了未定义的行为。见Does invoking a member function on a null instance cause undefined behavior?。
【讨论】:
【参考方案5】:我已经尝试过多次,输出总是“有趣”,这是因为函数fun
独立于实例a
。在调用 a->fun();
a
时指向 0,所以这是未定义的行为,但在大多数编译器中应该不会发生崩溃。
【讨论】:
A* a = NULL;
强调不会导致构造函数调用【参考方案6】:
三点可能会有所帮助:
1) 所有函数都存储在代码或文本部分中。
2) 非虚函数在编译时被解析。
3) 在调用类的成员函数时,我们将当前对象作为this
指针传递给该函数。
来到你的问题,这里fun()
函数已经在内存中(代码部分/文本部分)。
由于函数fun()
是非虚拟的,它将在编译时解析(即,对于这一行,它将跳转到代码部分的指令X,this
指针为NULL
)。
由于fun()
函数中没有使用/调用任何成员变量和虚函数,所以它工作正常。
【讨论】:
没有人能编译这个看看它到底做了什么吗?以上是关于当我在 NULL 对象指针上调用成员函数时会发生啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
当我对具有复制构造函数但没有赋值运算符的对象进行赋值时会发生啥?
将 NULL 指针转换为对象并调用其成员函数之一有实际好处吗?