C++核心准则ES.65:不要解引用无效指针

面向对象思考

共 4604字,需浏览 10分钟

 ·

2020-05-27 23:22

ffa7f479f3d2c41be448efbdcaad0f13.webp

ES.65: Don't dereference an invalid pointer

ES.65:不要解引用无效指针


Reason(原因)

Dereferencing an invalid pointer, such as nullptr, is undefined behavior, typically leading to immediate crashes, wrong results, or memory corruption.

解引用例如null等无效指针,是无定义的行为,通常会立即导致程序崩溃,错误的结果,或者内存破坏。


Note(注意)

This rule is an obvious and well-known language rule, but can be hard to follow. It takes good coding style, library support, and static analysis to eliminate violations without major overhead. This is a major part of the discussion of C++'s model for type- and resource-safety.

本规则显而易见而且众所周知,但却很难遵守。它会带来好的代码风格,更充分的库支持,不需要很大代价但可以排除违反的静态解析。这是关于C++类型和资源安全模型的论述的重要组成部分。


See also:(参见)

  • Use RAII to avoid lifetime problems.

  • 使用RAII避免生命周期问题。

  • Use unique_ptr to avoid lifetime problems.

  • 使用unique_ptr避免生命周期问题

  • Use shared_ptr to avoid lifetime problems.

  • 使用shared_ptr避免生命周期问题

  • Use references when nullptr isn't a possibility.

  • 如果不可能出现空指针,使用引用

  • Use not_null to catch unexpected nullptr early.

  • 使用not_null尽早捕获意外的空指针。

  • Use the bounds profile to avoid range errors.

  • 使用边界规则群组避免范围错误。


Example(示例)

void f()
{
int x = 0;
int* p = &x;

if (condition()) {
int y = 0;
p = &y;
} // invalidates p

*p = 42; // BAD, p might be invalid if the branch was taken
}

To resolve the problem, either extend the lifetime of the object the pointer is intended to refer to, or shorten the lifetime of the pointer (move the dereference to before the pointed-to object's lifetime ends).

为了解决这个问题,要么扩展对象指针意图指向的对象的生命周期,要么缩短指针的生命周期(将解引用操作移到所指向对象的生命周期结束之前。)

void f1()
{
int x = 0;
int* p = &x;

int y = 0;
if (condition()) {
p = &y;
}

*p = 42; // OK, p points to x or y and both are still in scope
}

Unfortunately, most invalid pointer problems are harder to spot and harder to fix.

不幸的是,大多数无效指针问题难于发现,也难于修改。


Example(示例)

void f(int* p)
{
int x = *p; // BAD: how do we know that p is valid?
}

There is a huge amount of such code. Most works -- after lots of testing -- but in isolation it is impossible to tell whether p could be the nullptr. Consequently, this is also a major source of errors. There are many approaches to dealing with this potential problem:

这样的代码大量存在。在经历了大量测试之后,大部分情况下可以动作,但是如果只看局部很难判断一个指针有没有可能为空。因此,空指针也是错误的主要来源之一。存在很多方法可以处理这个潜在问题:

void f1(int* p) // deal with nullptr
{
if (!p) {
// deal with nullptr (allocate, return, throw, make p point to something, whatever
}
int x = *p;
}

There are two potential problems with testing for nullptr:

检查指针是否为空会有两个潜在问题:

  • it is not always obvious what to do what to do if we find nullptr

  • 在发现了空指针时应该做什么并不总是很明确。

  • the test can be redundant and/or relatively expensive

  • 检查可能是多余的而且/或者代价相当高。

  • it is not obvious if the test is to protect against a violation or part of the required logic.

  • 很难判断这个检查只是为了防止违反还是必要逻辑的一部分。

void f2(int* p) // state that p is not supposed to be nullptr
{
assert(p);
int x = *p;
}

This would carry a cost only when the assertion checking was enabled and would give a compiler/analyzer useful information. This would work even better if/when C++ gets direct support for contracts:

这种做法只在断言检查有效时需要付出一定的代价,同时可以为编译器/解析器提供有用信息。如果C++得到协议(contracts)的直接支持的话,效果会更好:

void f3(int* p) // state that p is not supposed to be nullptr
[[expects: p]]
{
int x = *p;
}

Alternatively, we could use gsl::not_null to ensure that p is not the nullptr.

另外,我们可以使用gsl::not_null来保证p不是空指针。

void f(not_null p)
{
int x = *p;
}

These remedies take care of nullptr only. Remember that there are other ways of getting an invalid pointer.

这个改进只处理空指针。别忘了还有其他形式的无效指针。


Example(示例)

void f(int* p)  // old code, doesn't use owner
{
delete p;
}

void g() // old code: uses naked new
{
auto q = new int{7};
f(q);
int x = *q; // BAD: dereferences invalid pointer
}

Example(示例)
void f()
{
vector v(10);
int* p = &v[5];
v.push_back(99); // could reallocate v's elements
int x = *p; // BAD: dereferences potentially invalid pointer
}

Enforcement(实施建议)

This rule is part of the lifetime safety profile

本规则是生命周期规则群组的一部分

  • Flag a dereference of a pointer that points to an object that has gone out of scope

  • 如果指针指向的对象已经处于生命周期之外,标记它的解引用操作。

  • Flag a dereference of a pointer that may have been invalidated by assigning a nullptr

  • 如果指针由于被设为空指针而无效时,标记它的解引用操作。

  • Flag a dereference of a pointer that may have been invalidated by a delete

  • 如果由于指针指向的对象被销毁而无效时,标记它的解引用操作。

  • Flag a dereference to a pointer to a container element that may have been invalidated by dereference

  • 如果指针指向的容器元素由于解引用而无效时,标记它的解引用操作。


原文链接

https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es65-dont-dereference-an-invalid-pointer




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

关注微信公众号【面向对象思考】轻松学习每一天!

面向对象开发,面向对象思考!


浏览 45
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报