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

ynn*_*ynn 8 c++ language-lawyer

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

但为什么?我知道这个事实是事实,但没有根据。实际上我想要的是C ++标准的相关摘录。我正在阅读,但最终没有找到证据。有人可以给我摘录或提示找我吗(因为仅在文档中搜索“ static”会导致一百多个匹配)?

#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"

}
Run Code Online (Sandbox Code Playgroud)

我看了堆栈溢出建议的所有大约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"

}
Run Code Online (Sandbox Code Playgroud)

P.W*_*P.W 4

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

\n\n
\n

6.7.1 静态存储持续时间 [basic.stc.static]
\n 1 所有没有动态存储持续时间、没有线程存储持续时间、并且不是本地的变量都具有静态存储持续时间。这些实体的存储应在程序持续期间持续(6.6.2、6.6.4)。
\n ...
\n 3 该关键字static可用于声明具有静态存储期限的局部变量。[注:9.7描述了局部静态变量的初始化;6.6.4 描述了局部静态变量的销毁。\xe2\x80\x94结束注]

\n
\n\n

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

\n\n

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

\n\n

然而,以下引用[basic.life](虽然不直接适用于您的场景)讲述了有关使用指向其存储仍然有效(已分配但未释放或重用)但其生命周期尚未开始或已结束的对象的指针的信息:

\n\n
\n

6 在对象的生命周期开始之前但在该对象将占用的存储已分配之后,或者在对象的生命周期结束之后且在重用或释放该对象所占用的存储之前,任何表示该地址的指针可以使用对象将要或曾经位于的存储位置的信息,但只能以有限的方式使用。对于正在构造或销毁的对象,请参阅 15.7。\n 否则,这样的指针引用分配的存储空间,并且使用该指针就好像该指针的类型一样void*,是明确定义的。允许通过此类指针进行间接寻址,但生成的左值只能以有限的方式使用,如下所述。如果出现以下情况,则程序具有未定义的行为:
\n (6.1) \xe2\x80\x94 对象将是或曾经是具有非平凡析构函数的类类型,并且指针用作删除表达式的操作数,\
n (6.2) \xe2\x80\x94 指针用于访问非静态数据成员或调用对象的非静态成员函数,或者
\n (6.3) \xe2\x80\x94 指针被隐式转换指向虚拟基类的指针,或
\n (6.4) \xe2\x80\x94 该指针用作 static_cast 的操作数,除非转换为指向 的指针cv void,或指向 的指针cv void,随后又指向指向的指针cv char, cv unsigned char, or cv std::byte, or
\n (6.5) \xe2\x80\x94 指针用作 的操作数dynamic_cast

\n
\n