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

时间:2019-03-18 02:22:20

标签: c++ language-lawyer

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

但是为什么呢?我知道这个事实是事实,但没有根据。实际上,我想要的是 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个问题,但还没有答案。 (只有一个相关的问题:Can I access static variables inside a function from outside。)


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

comment中所述,即使类成员与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"

}

1 个答案:

答案 0 :(得分:3)

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)—该对象将是或曾经是具有非平凡析构函数的类类型,并且该指针用作delete-expression的操作数,
  (6.2)—指针用于访问对象的非静态数据成员或调用对象的非静态成员函数,或者
  (6.3)—将该指针隐式转换为指向虚拟基类的指针,或者
  (6.4)—指针用作static_cast的操作数,除非转换是指向cv void的指针或指向cv void的指针,然后是指向cv char, cv unsigned char的指针,或者cv std::byte
  (6.5)—指针用作dynamic_cast的操作数。