问题描述
当指针悬空时,SO出现了一个问题,问这为什么起作用".答案是它是UB,这意味着它可能行不通.
A question came up here on SO asking "Why is this working" when a pointer became dangling. The answers were that it's UB, which means it may work or not.
我在一个教程中了解到:
I learned in a tutorial that:
#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.
}
我对此有误吗?就像我解释的那样,只是调用一个函数而不访问无效的Foo指针,它是UB吗?
Am I wrong about this? Is it UB even though as I explained just calling a function and not accessing the invalid Foo pointer?
推荐答案
您正在思考实际发生的情况.可以执行未定义的行为来完成您期望的事情...但是不能保证.
You're reasoning about what happens in practice. Undefined behavior is allowed to do the thing you expect... but it is not guaranteed.
对于非静态情况,可以直接使用[class.mfct.non-static]
中的规则进行证明:
For the non-static case, this is straightforward to prove using the rule found in [class.mfct.non-static]
:
请注意,不考虑非静态成员函数是否访问*this
.仅要求对象具有正确的动态类型,而*(Foo*)nullptr
当然不是.
Note that there's no consideration about whether the non-static member function accesses *this
. The object is simply required to have the correct dynamic type, and *(Foo*)nullptr
certainly does not.
尤其是,即使在使用您描述的实现的平台上,调用
In particular, even on platforms which use the implementation you describe, the call
fooObj->func();
被转换为
__assume(fooObj); Foo_func(fooObj);
并且优化不稳定.
这是一个与您的预期相反的示例:
Here's an example which will work contrary to your expectations:
int main()
{
Foo* fooObj = nullptr;
fooObj->func();
if (fooObj) {
fooObj->member = 5; // This will cause a read access violation!
}
}
在实际系统上,这很可能导致注释行出现访问冲突,因为编译器使用fooObj
不能在fooObj->func()
中为null的事实来消除紧随其后的if
测试.
On real systems, this is likely to end up with an access violation on the commented line, because the compiler used the fact that fooObj
can't be null in fooObj->func()
to eliminate the if
test following it.
即使您认为自己知道平台的功能,也不要做UB的事情. 优化不稳定是真实的.
Don't do things that are UB even if you think you know what your platform does. Optimization instability is real.
此外,您可能会认为标准更加严格.这也会也导致UB:
Also, the Standard is even more restrictive that you might think. This will also cause 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]
中找到:
The relevant portions of the Standard are found in [expr.ref]
:
和随附的脚注
这意味着所讨论的代码肯定会评估(*fooObj)
,尝试创建对不存在对象的引用.有一些提议使这种允许成为可能,并且仅禁止在这样的引用上允许进行lvalue-> rvalue转换,但是到目前为止,这些提议都被拒绝了.到目前为止,即使构成参考也属于非法.
This means that the code in question definitely evaluates (*fooObj)
, attempting to create a reference to a non-existent object. There have been several proposals to make this allowed and only forbid allowing lvalue->rvalue conversion on such a reference, but those have been rejected this far; even forming the reference is illegal in all versions of the Standard to date.
这篇关于C ++中的这种未定义行为是从悬空指针调用函数吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!