为什么静态本地对象可以通过指针或外部引用访问?



如您所知,局部静态变量不能通过名称在函数外部访问,但可以通过指针或对它的引用来访问。所以下面的代码格式正确。

但是为什么?我知道这个事实,但没有根据。实际上,我想要的是C++标准的相关摘录。我正在阅读它,但最终没有找到证据。谁能给我摘录或提示来找到它(因为仅在文档中搜索"静态"就会导致一百多次点击)?

#include <iostream>
using namespace std;
class Test {
public:
int * f(int i) const {
static int j;
j += i;
cout << "now j = " << j << "n";
return &j;
}
int & g(int i) const { //same as above but handle reference
static int k;
k += i;
cout << "now k = " << k << "n";
return k;
}
};
int main() {
Test t;
int *p = t.f(3); //=> "now j = 3"
*p += 10;
t.f(0); //=> "now j = 13"
int &r = t.g(3); //=> "now k = 3"
r += 10;
t.g(0); //=> "now k = 13"
}

我看了一下堆栈溢出建议的所有大约 20 个问题,但还没有答案。(只有一个相关的问题:我可以从外部访问函数内部的静态变量吗?


对于未来的读者(或只是我的笔记):

如评论中所述,这同样适用于类成员的情况,即使它是遥远的和private

#include <iostream>
using namespace std;
class Base {
private:
int i = 0;
public:
int * return_pointer() { return &i; }
void print() { cout << "i = " << i << "n"; }
};
class Derived : public Base {
public:
int * return_pointer() { return Base::return_pointer(); }
};
int main() {
Derived d;
d.print(); //=> "i = 0"
int *p = d.return_pointer();
*p = 300;
d.print(); //=> "i = 300"
}

来自 C++17 标准 (n4659) 的相关报价告诉我们static变量的存储持续时间:

6.7.1 静态存储持续时间 [basic.stc.static]1
所有没有动态存储持续时间、没有线程存储持续时间、非本地变量的变量都有静态存储持续时间。这些实体的存储应在计划(6.6.2,6.6.4)期间持续。

3 关键字static可用于声明具有静态存储持续时间的局部变量。[注:9.7描述了局部静态变量的初始化;6.6.4描述了局部静态变量的破坏。

函数局部static变量的生存期从程序流第一次遇到声明时开始,到程序终止结束。

正如评论中提到的,没有直接引用说可以通过指针或引用访问此类变量。

但是,以下引自[basic.life](虽然不直接适用于你的方案)讲述了如何使用指针指向存储仍然有效(已分配但未释放或重用)但其生存期尚未开始或已结束的对象:

6 在对象的生存期开始之前,但在分配了对象将占用的存储之后,

或者在对象的生存期结束之后,在重新使用或释放对象所占用的存储之前,可以使用表示对象将要或曾经所在的存储位置地址的任何指针,但只能以有限的方式使用。关于正在建造或销毁的物体,见15.7。否则,此类指针引用分配的存储,并且像指针类型为void*一样使用该指针是明确定义的。允许通过此类指针进行间接寻址,但生成的左值只能以有限的方式使用,如下所述。如果:
(6.1) — 对象将是或曾经是具有非平凡析构函数的类类型,并且指针用作删除表达式的操作数,(6.2) — 指针
用于访问非静态数据成员或调用对象的非静态成员函数,或
(6.3) — 指针隐式转换为指向虚拟基类的指针, or
(6.4) — 指针用作static_cast的操作数,除非转换为指向cv void的指针,或指向cv void的指针,然后转换为指向cv char, cv unsigned char的指针,或cv std::byte,或
(6.5) — 指针用作dynamic_cast的操作数。