24 June 2022

C++ 核心指南目录

理由

悬空指针会导致程序崩溃,数据破坏。

坏例子

从函数返回后,本地对象已经没了。

// -*- compile-command: "g++ -std=c++20 code.cpp && ./a"; -*-
#include <iostream>
#include <gsl/gsl>
using namespace std;
using namespace gsl;
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();
    cout << *p;
    int z = *p;  // read from abandoned stack frame (bad)
    g(p);        // pass pointer to abandoned stack frame to function (bad)
}
int main()
{
    h();
    return 0;
}

可能的结果:

*p == 999
gx == 999

可能的原因是 g() 重用了 f() 函数不用了的堆栈空间。所有 *p 指向了 gx 的堆栈空间。

以下情况都可能发生,想象下:

  • fxgx 不是同一类型
  • fxgx 是同一类型的变体
  • 一组函数调用过程中有更多悬挂指针
  • 黑客可以利用悬挂指针做什么事情

幸运的是,大部分现代编译器都会对简单的情况进行警告

注意

引用有一样的情况

// -*- compile-command: "g++ -std=c++20 code.cpp && ./a"; -*-
#include <iostream>
#include <gsl/gsl>
using namespace std;
using namespace gsl;
int& f()
{
    int x = 7;
    // ...
    return x;  // Bad: returns reference to object that is about to be destroyed
}
int main()
{
    cout << f();
    return 0;
}

注意

只针对非静态局部变量会。静态变量静态分配存储空间,指向他们的指针不会悬空。

坏例子

有些泄漏局部变量指针的情况不太明显

// -*- compile-command: "g++ -std=c++20 code.cpp && ./a"; -*-
#include <iostream>
#include <gsl/gsl>
using namespace std;
using namespace gsl;
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;
    cout << "address of i    = " << &i << endl;
    steal([&] { return &i; });
}

int main()
{
    cout << "address of glob = " << glob << endl;
    f();
    cout << *glob << '\n';
    cout << "address of glob = " << glob << endl;
}
address of glob = 0
address of i    = 0xbdc97ffb0c
99
address of glob = 0xbdc97ffb0c

这里我们在函数 f() 中把本地变量的引用赋值给了 glob ,后续代码使用 glob 会产生不可预测的结果。

注意

本地变量地址会通过返回、 T& 输出参数、返回对象的成员、返回数组的元素等方式泄漏出来。

注意

内部作用域的变量,也可能通过类似方式泄漏到外层作用域。

此问题的另一个变体是将指针放在一个容器内,此容器的生存周期比指针指向的对象生存周期长。

强化

  • 编译器捕捉返回值为指向局部对象的引用和指针。
  • 静态代码分析确认用指针表示对象位置的情况。