关于c ++:为什么可以通过外部的指针或引用访问静态本地对象?

Why can static local object be accessed via pointer or reference from outside?

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

但是为什么呢?我知道这是事实,但没有根据。实际上,我想要的是C++标准的相关摘录。我在读,但最终还是没有找到证据。有人能给我一个摘录或提示来找到它吗(因为只要在文档中搜索"static"就可以得到超过100个点击量)?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
using namespace std;

class Test {

    public:

        int * f(int i) const {
            static int j;
            j += i;
            cout <<"now j =" << j <<"
"
;
            return &j;
        }

        int & g(int i) const { //same as above but handle reference
            static int k;
            k += i;
            cout <<"now k =" << k <<"
"
;
            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,也同样适用于该类成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
using namespace std;

class Base {
    private:
        int i = 0;
    public:
        int * return_pointer() { return &i; }
        void print() { cout <<"i =" << i <<"
"
; }
};

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标准(N4665)的相关引述告诉我们EDOCX1×0变量的存储持续时间:

6.7.1 Static storage duration [basic.stc.static]
1 All variables which do not have dynamic storage duration, do not have thread storage duration, and are not local have static storage duration. The storage for these entities shall last for the duration of the program (6.6.2, 6.6.4).
...
3 The keyword static can be used to declare a local variable with static storage duration. [ Note: 9.7 describes the initialization of local static variables; 6.6.4 describes the destruction of local static variables. —end note ]

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

正如注释中所提到的,没有直接引述可以通过指针或引用访问这些变量。

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

6 Before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that represents the address of the storage location where the object will be or was located may be used but only in limited ways. For an object under construction or destruction, see 15.7.
Otherwise, such a pointer refers to allocated storage, and using the pointer as if the pointer were of type void*, is well-defined. Indirection through such a pointer is permitted but the resulting lvalue may only be used in limited ways, as described below. The program has undefined behavior if:
(6.1) — the object will be or was of a class type with a non-trivial destructor and the pointer is used as the operand of a delete-expression,
(6.2) — the pointer is used to access a non-static data member or call a non-static member function of the object, or
(6.3) — the pointer is implicitly converted to a pointer to a virtual base class, or
(6.4) — the pointer is used as the operand of a static_cast, except when the conversion is to pointer to cv void, or to pointer to cv void and subsequently to pointer to cv char, cv unsigned char, or cv std::byte, or
(6.5) — the pointer is used as the operand of a dynamic_cast.