Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

As you may know, a local static variable cannot be accessed outside the function by name but can be accessed via pointer or reference to it. So the code below is well-formed.

But why? I know this fact as a fact, but have no ground. Actually what I want is the related excerpt from C++ standard. I'm reading it but not end up finding the evidence. Could anyone please give me the excerpt or a hint to find that (because just searching "static" in the document results in over a hundred of hits)?

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

}

I took a look at all of the about 20 questions suggested by stack overflow, but have no answer yet. (There was only one related question: Can I access static variables inside a function from outside.)


For the future readers (or just my note):

As indicated in the comment, the same applies to the case of a class member even if it is distant and 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"

}
like image 341
ynn Avatar asked Mar 18 '19 02:03

ynn


1 Answers

The relevant quotes from the C++17 standard (n4659) tell us about the storage duration of static variables:

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 ]

The lifetime of static variables local to a function begins the first time the program flow encounters the declaration and it ends at program termination.

As mentioned in the comments there is no direct quote which says that such variables can be accessed via pointer or reference.

However the following quote from [basic.life] (while not directly applicable to your scenario) tells something about using pointers to objects whose storage is still valid (allocated but not released or reused) but whose lifetime has not started or already ended:

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.

like image 63
P.W Avatar answered Nov 06 '22 05:11

P.W