2

As in the title - how does program know, that foo is already initialized when function is called second time:

int getFoo()
{
    static int foo = 30;
    return foo;
}
int main()
{
    getFoo();
    getFoo();
}

I want to know, whether the program stores some additional information about which static variable was already initialized.

Edit:
I found an answer here:
Why does initialization of local static objects use hidden guard flags?
Like I guessed - most compilers store additional "guard variable".

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
Poeta Kodu
  • 1,120
  • 8
  • 16
  • 4
    Did you already consider studying the emitted assembly code? – πάντα ῥεῖ Sep 08 '18 at 18:52
  • See e.g. [Construction of function static variables in C++ is not thread safe](https://eli.thegreenplace.net/2011/08/30/construction-of-function-static-variables-in-c-is-not-thread-safe). – dfrib Sep 08 '18 at 19:08

2 Answers2

0

Have a look at [stmt.dcl]/4:

  1. Dynamic initialization of a block-scope variable with static storage duration or thread storage duration is performed the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.94 If control re-enters the declaration recursively while the variable is being initialized, the behavior is undefined.
Swordfish
  • 12,971
  • 3
  • 21
  • 43
0

You have to be careful here. Primitive statics are initialised at compile time (as long as the initialisation value is a compile-time contant, as Peter points out), so in your example, GetFoo just, in effect, returns a constant.

HOWEVER...

statics which initialise an object (or initialise a primitive by calling a function) perform said initialisation when the scope in which they are declared is entered for the first time.

Furthermore, as of C++ 11 this has to be done in a threadsafe way, which generates a lot of extra code (although not much runtime overhead, after the first time through) and that might be an issue on, say, a micro-controller where code size often matters.

Here's a concrete example:

#include <iostream>

struct X
{
    X () { std::cout << "Initialising m\n"; m = 7; }
    int m;
};

void init_x ()
{
    static X x;
}

int main () {
    std::cout << "main called\n";
    init_x ();
    std::cout << "init_x returned\n";
}

Output:

main called
Initialising m
init_x returned

Live demo: https://wandbox.org/permlink/NZApcYYGwK36vRD4

Generated code: https://godbolt.org/z/UUcL9s

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
  • *Primitive statics are initialised at compile time* - *if* the initializer is a compile-time constant! You can do `static int x = func_arg;` as in https://godbolt.org/z/6caWKcne8 or even a function call to some flavour of `rand()`. Perhaps a PRNG seeding its state with static initializer. But yes, in this case the initializer is a constant expression, so there's no guard variable needed, it works just file-scope `static`, except for the scoping of the name which is purely a C++ thing, not asm. – Peter Cordes Mar 04 '22 at 07:37
  • Making it thread-safe is the whole point of having a guard variable for non-constant initializers. Real-world compilers did that before C++11 required it, e.g. GCC4.1 from 2006 is the earlier compiler on Godbolt. – Peter Cordes Mar 04 '22 at 07:39
  • @PeterCordes _if the initializer is a compile-time constant!_ absolutely, I have edited my post. _Making it thread-safe is the whole point of having a guard variable for non-constant initializers._ Of course, but it would be nice to have a way of saying to the compiler 'don't bother to do this' if you know for sure that the initialisation can only ever happen on a single-threaded code path. In practise, this is true more often than not. Maybe we could have `[[no_threadsafe_initializer]] static ...` or similar. Like a lot of things in C++, you just have to make damn sure you get it right. – Paul Sanders Mar 04 '22 at 15:59
  • 1
    @PeterCordes This is all about code bloat of course, and really only matters in a resource-poor environment where developers seem to care about every nibble. Most people don't need to worry about this and would much rather play it safe. **Edit:** Oh, just seen `-fno-threadsafe-statics`. Maybe that's enough. – Paul Sanders Mar 04 '22 at 16:23
  • Yup; either way the fast path still has a compare & branch for non-constant initializers. But thread-safe requires an acquire load, which on some weakly-ordered ISAs might take a memory barrier as well (like ARM32, but not x86 or ARM64 - A64 has cheap LDAR acquire-load instructions). Other than that it's just a matter of code size *not* on the fast path. With good layout, the first-call-only code (for guard==0) is at the end of the function, not hurting I-cache locality or putting a taken branch in the way of code-fetch (for superscalar CPUs). – Peter Cordes Mar 05 '22 at 01:20