这是C ++中的未定义行为从悬空指针调用函数
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了这是C ++中的未定义行为从悬空指针调用函数相关的知识,希望对你有一定的参考价值。
这里出现了一个问题,当一个指针变得晃来晃去时,问“为什么这个工作”。答案是它是UB,这意味着它可能起作用。
我在一个教程中学到了:
#include <iostream>
struct Foo
{
int member;
void function() { std::cout << "hello";}
};
int main()
{
Foo* fooObj = nullptr;
fooObj->member = 5; // This will cause a read access violation but...
fooObj->function(); // Because this doesn't refer to any memory specific to
// the Foo object, and doesn't touch any of its members
// It will work.
}
这相当于:
static void function(Foo* fooObj) // Foo* essentially being the "this" pointer
{
std::cout << "Hello";
// Foo pointer, even though dangling or null, isn't touched. And so should
// run fine.
}
我错了吗?是UB,即使我解释只是调用一个函数而不是访问无效的Foo指针?
你在推理实践中会发生什么。允许未定义的行为执行您期望的操作...但不保证。
对于非静态情况,使用[class.mfct.non-static]
中的规则证明这很简单:
如果为
X
类型的对象或从X
派生的类型调用类X
的非静态成员函数,则行为未定义。
请注意,没有考虑非静态成员函数是否访问*this
。该对象只需要具有正确的动态类型,而*(Foo*)nullptr
肯定不会。
特别是,即使在使用您描述的实现的平台上,也可以使用该调用
fooObj->func();
转换为
__assume(fooObj); Foo_func(fooObj);
并且是优化不稳定的。
这是一个与您的期望相反的例子:
int main()
{
Foo* fooObj = nullptr;
fooObj->func();
if (fooObj) {
fooObj->member = 5; // This will cause a read access violation!
}
}
在实际系统上,这很可能最终会在注释行上出现访问冲突,因为编译器使用了fooObj
中fooObj->func()
不能为空的事实来消除它后面的if
测试。
即使您认为自己了解平台的功能,也不要做UB的事情。 Optimization instability is real.
此外,您可能会认为标准更具限制性。这也会导致UB:
struct Foo
{
int member;
void func() { std::cout << "hello";}
static void s_func() { std::cout << "greetings";}
};
int main()
{
Foo* fooObj = nullptr;
fooObj->s_func(); // well-formed call to static member,
// but unlike Foo::s_func(), it requires *fooObj to be a valid object of type Foo
}
标准的相关部分可在[expr.ref]
找到:
表达式
E1->E2
被转换为等价形式(*(E1)).E2
以及随附的脚注
如果计算了类成员访问表达式,则即使不需要结果来确定整个后缀表达式的值,也会发生子表达式求值,例如,如果id-expression表示静态成员。
这意味着有问题的代码肯定会评估(*fooObj)
,尝试创建对不存在的对象的引用。有几个提议允许这样做,并且只禁止在这样的引用上允许lvalue-> rvalue转换,但是到目前为止这些已经被拒绝了;甚至形成参考文献在迄今为止的所有版本的标准中都是非法的。
在实践中,这通常是主要编译器如何实现成员函数,是的。这意味着您的测试程序可能看起来运行“很好”。
话虽如此,取消引用指向nullptr
的指针是未定义的行为,这意味着所有的赌注都关闭,整个程序和它的输出都没有意义,任何事情都可能发生。
你永远不能依赖这种行为,特别是优化器可能会破坏所有这些代码,因为他们可以假设fooObj
永远不会是nullptr
。
编译器没有义务通过向它传递指向类实例的指针来实现成员函数。是的,有伪指针“this”,但它是无关的元素,保证被“理解”。
nullptr
指针不指向任何现有对象, - >()调用该对象的成员。从标准的角度来看,这是无稽之谈,并且这种操作的结果是不确定的(并且可能是灾难性的)。
如果function()
是虚拟的,则允许调用失败,因为函数的地址将不可用(vtable可能作为对象的一部分实现,如果对象没有则不存在)。
如果成员函数(方法)的行为与此类似,并且意味着要调用它应该是静态成员函数(方法)。静态方法不访问非静态字段,也不调用类的非静态方法。如果它是静态的,那么调用也可能如下所示:
Foo::function();
以上是关于这是C ++中的未定义行为从悬空指针调用函数的主要内容,如果未能解决你的问题,请参考以下文章
何时在空实例上调用成员函数会导致 C++11 中的未定义行为? [复制]