无论直接还是间接,永远不要返回指向局部对象的指针或引用。
To avoid the crashes and data corruption that can result from the use of such a dangling pointer.
为了避免使用这个悬空指针而引起崩溃和数据破坏。
After the return from a function its local objects no longer exist:
当程序从函数退出之后,函数中的局部变量就不再存在了。
int* f()
{
int fx = 9;
return &fx; // BAD
}
void g(int* p) // looks innocent enough
{
int gx;
cout << "*p == " << *p << '\n';
*p = 999;
cout << "gx == " << gx << '\n';
}
void h()
{
int* p = f();
int z = *p; // read from abandoned stack frame (bad)
g(p); // pass pointer to abandoned stack frame to function (bad)
}
Here on one popular implementation I got the output:
这是一段很一般代码,我得到了以下输出:
*p == 999
gx == 999
I expected that because the call of g()
reuses the stack space abandoned by the call of f()
so *p
refers to the space now occupied by gx
.
我预期如此是因为对g()的调用再次使用了调用f()之后放弃的堆栈空间,因此*p访问的是现在被gx占用的空间。
fx
and gx
were of different types.
想象一下如果fx和gx是不同类型时会发生什么。
译者注:变量会被修改为完全无关的值。
fx
or gx
was a type with an invariant.
想象一下如果fx和gx是一种包含不变式的类型时会发生什么。
译者注:不变式会被莫名其妙地破坏。
Fortunately, most (all?) modern compilers catch and warn against this simple case.
幸运的是,大部分(所有?)现代编译器都可以捕捉并对这个简单的情况报警。
This applies to references as well:
这一问题也适用于引用的情况。
int& f()
{
int x = 7;
// ...
return x; // Bad: returns reference to object that is about to be destroyed
}
This applies only to non-static
local variables. All static
variables are (as their name indicates) statically allocated, so that pointers to them cannot dangle.
这个问题只适用于非静态全局变量。所有的静态变量(就像名称所表示的)都是静态分配内存,因此指向它们的指针不会悬空。
Not all examples of leaking a pointer to a local variable are that obvious:
不是所有泄漏指向局部变量指针的示例都是明显的。
int* glob; // global variables are bad in so many ways
template<class T>
void steal(T x)
{
glob = x(); // BAD
}
void f()
{
int i = 99;
steal([&] { return &i; });
}
int main()
{
f();
cout << *glob << '\n';
}
Here I managed to read the location abandoned by the call of f
. The pointer stored in glob
could be used much later and cause trouble in unpredictable ways.
这段代码中我设法读取函数f被调用后放弃的局部变量。保存在glob中的指针可以在很长时间之后被使用并以无法预期的方式带来麻烦。
The address of a local variable can be "returned"/leaked by a return statement, by a T&
out-parameter, as a member of a returned object, as an element of a returned array, and more.
局部变量的地址以多种方式被“返回”或者说被泄漏。具体的方式可以是通过返回语句,T&类型的输出参数,返回值对象的成员,返回值数组的元素或者是其它方式。
Similar examples can be constructed "leaking" a pointer from an inner scope to an outer one; such examples are handled equivalently to leaks of pointers out of a function.
类似地,也可以构造出从内部作用域向外部作用域“泄漏”指针的例子。这样的例子等价于向函数外部泄漏(指向局部变量的)指针。
A slightly different variant of the problem is placing pointers in a container that outlives the objects pointed to.
这个问题的稍微不同的版本是将指针放到生命周期超过指针所指向对象的容器中的情况。
See also: Another way of getting dangling pointers is pointer invalidation. It can be detected/prevented with similar techniques.
参见:产生悬空指针的另一种情况是指针无效化。它可以通过类似的技术检查或防止。
译者注:指针无效化应该是指针本来指向的是一个有效对象,但后来对象被销毁而指针没有被同时清空的情况。
觉得本文有帮助?请分享给更多人。
关注【面向对象思考】,轻松学习每一天!
有任何疑问,欢迎留言提问或讨论。
面向对象设计,面向对象编程,面向对象思考!