5

Just started learning c++ and came across this example where the function is returning a reference to a local static variable.

int& fun() {
    static int x = 10;
    return x;
}

int main() {
    int &z = fun();
    cout << fun() << " ";
    z = 30;
    cout << fun();
    return 0;
}

What does the line int &z = fun(); do? Are we storing a reference inside another reference? I'm new to the language and all I know is that reference variables refer to a variable just like an alias. Can anyone explain how this works?

Ken White
  • 123,280
  • 14
  • 225
  • 444
dev.sh
  • 59
  • 2
  • 3
    Yes `z` is an alias of `static` variable `x`. – songyuanyao Aug 13 '22 at 02:15
  • 1
    There are no references to references. The syntax `int&&` is valid but means something totally different. `z` and the return value of `fun` are each of type `int&` – Silvio Mayolo Aug 13 '22 at 02:20
  • [Dupe1](https://stackoverflow.com/questions/959951/returning-reference-to-static-local-variable-in-c), [Dupe2](https://stackoverflow.com/questions/55213960/why-can-static-local-object-be-accessed-via-pointer-or-reference-from-outside), [Dupe3](https://stackoverflow.com/questions/13424564/returning-static-local-variables-as-references), [Dupe4](https://stackoverflow.com/questions/9133965/returning-a-static-local-reference) – Jason Aug 13 '22 at 07:40

3 Answers3

2

Are we storing a reference inside another reference?

No, references aren't even required to have "storage". A reference is something to simplify programming. auto& thing = foo.get_value_reference(); and then using thing makes code easier to write and debug. thing doesn't even have to exist as a separate entity when you look at the final assembly code.

int orig;
int& a = orig;
int& b = a;

b is now a reference to orig - nothing else. You can't reference a reference.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
1

Are we storing a reference inside another reference?

No, there is no reference to reference, at least in C\C++.

For me, a reference is just a different name for another variable, at the end, they are all referring to the same, single object. In more detail, abstractly, whenever you write int& a = b, all you have is still b, and there is no such thing called a, that ever exist. (a is just an alias of b)

Because of that, we can't have a different name of a name, that would sound a bit weird, since it does not actually refer to anything that exist.

In your case above, what int& fun() does is returning the actual static int x = 10;. And int &z = fun();, once again, refer directly the the actual static int x = 10;. Whatever z or anything, afterall, it is just static int x = 10, under different names.

This would be different if you remove the amphersand-& to int fun(), which returns a copied version of int x = 10;, which means now existed two different things: int x = 10 and a copy of int x = 10.

That's why C\C++ is memory-efficient, isn't it? You know when things get copied and when it does not, which helps optimization a lot!

Hope this helps!

Khoa LT
  • 89
  • 1
  • 6
1

First of all, a variable declared static inside a function is allocated when the program begins and deallocated when the program ends. Unlike normal local variables, it is safe to keep a reference to a static variable after returning from the function in which it is declared. It continues to exist and will keep its value.

Let's consider this function:

int& fun() {
    static int x = 10;
    return x;
}

Returning a reference to the static variable x is like returning the variable itself. We can increment the variable through that reference, for instance:

  cout << fun()++ << endl;
  cout << fun()++ << endl;  // output: 11
  cout << fun() << endl;    // output: 12

This would not be possible if fun() returned the value of x (the integer 10) instead of a reference to variable x itself (whose value we can update).

int &z = fun() lets us refer to that same static variable through the name z in the same way:

  int &z = fun();
  cout << z++ << endl;
  cout << z++ << endl;       // output: 11
  cout << z++ << endl;       // output: 12
  cout << fun() << endl;     // output: 13

Both the function return type and z have to be references for the above to work.

If z were not a reference but an int z variable, we would be making a copy of the original value and incrementing that in place of the static variable x itself.

If the function return type were a value (instead of a reference), it would return the value of x, not a reference to x itself. In this case, int f(); int &z = f(); would try to take a reference to the temporary return value of the function. In fact, this code doesn't even compile.

Functions that return static variables (by reference or otherwise) have their uses. One of which is that a static variable inside a function is initialized at runtime, the first time we run through its declaration.

In the code below, init_x() is called when initializing the static variable x. This happens the first time fun() is called to retrieve the value of x.

int& fun() {
    static int x = init_x();
    return x;
}

int main() {
    do_other_stuff();
    fun()++;  // init_x() is called here
    fun()++;
    fun()++;
}
Alex Jasmin
  • 39,094
  • 7
  • 77
  • 67
  • Is the static variable (within a function scope) initialized, when the program begins or when the function is first called? Or is the time of initialization different from the time of allocation. – Sebastian Aug 13 '22 at 07:36
  • Indeed, allocation is different from initialization. *Allocation* here happens when the OS loads the program binary, The compiler & linker have computed in advance how large a chunk of space it needs for static and global variables and write this in the executable. Technically, some static variables can be initialized when the program starts, but only if their initialization is zero or constant. In which case, you can't observe the difference because there's no side-effect. – Alex Jasmin Aug 13 '22 at 16:13