分享免费的编程资源和教程

网站首页 > 技术教程 正文

C++核心准则编译边学-F.43 永远不要返回局部对象的指针或引用

goqiw 2024-10-04 22:07:49 技术教程 13 ℃ 0 评论


F.43: Never (directly or indirectly) return a pointer or a reference to a local object

无论直接还是间接,永远不要返回指向局部对象的指针或引用。

Reason(原因)

To avoid the crashes and data corruption that can result from the use of such a dangling pointer.

为了避免使用这个悬空指针而引起崩溃和数据破坏。

Example, bad(反面示例)

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占用的空间。

  • Imagine what would happen if fx and gx were of different types.想象一下如果fx和gx是不同类型时会发生什么。译者注:变量会被修改为完全无关的值。
  • Imagine what would happen if fx or gx was a type with an invariant.想象一下如果fx和gx是一种包含不变式的类型时会发生什么。译者注:不变式会被莫名其妙地破坏。
  • Imagine what would happen if more that dangling pointer was passed around among a larger set of functions.进一步想象一下如果悬空指针在更多函数之间传递是会发生什么。译者注:会有跟多的函数和局部变量受到影响。
  • Imagine what a cracker could do with that dangling pointer.想象一下破密者会如果处理悬空指针。译者注:破密者可以通过这个人变量改变函数的行为。

Fortunately, most (all?) modern compilers catch and warn against this simple case.

幸运的是,大部分(所有?)现代编译器都可以捕捉并对这个简单的情况报警。

Note(注意)

This applies to references as well:

这一问题也适用于引用的情况。

int& f()
{
 int x = 7;
 // ...
 return x; // Bad: returns reference to object that is about to be destroyed
}

Note(注意)

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.

这个问题只适用于非静态全局变量。所有的静态变量(就像名称所表示的)都是静态分配内存,因此指向它们的指针不会悬空。

Example, bad(反面示例)

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中的指针可以在很长时间之后被使用并以无法预期的方式带来麻烦。

Note(注意)

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&类型的输出参数,返回值对象的成员,返回值数组的元素或者是其它方式。

Note(注意)

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.

参见:产生悬空指针的另一种情况是指针无效化。它可以通过类似的技术检查或防止。

译者注:指针无效化应该是指针本来指向的是一个有效对象,但后来对象被销毁而指针没有被同时清空的情况。

Enforcement(实施建议)

  • Compilers tend to catch return of reference to locals and could in many cases catch return of pointers to locals.编译器倾向于捕捉返回指向局部变量的引用的情况,也可以在很多情况下捕捉返回指向局部变量的指针的情况。
  • Static analysis can catch many common patterns of the use of pointers indicating positions (thus eliminating dangling pointers)静态分析可以发现许多使用指针表示位置的通常形式,这样就可以排除悬空指针。

觉得本文有帮助?请分享给更多人。

更多更新文章,欢迎关注微信公众号【面向对象思考】

面向对象设计,面向对象编程,面向对象思考!

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表