tags:

views:

167

answers:

4

E.g

 foo1() {
     static const char* str = foo2();

 }

 const char * foo2 () {

     ...
 }

How does the compiler makes sure it calls foo2 just once.

+4  A: 

foo2 is called at the initialisation of your program, just before main().

Edit: this is wrong! I assumed this as this is how normally static initialisation works. But in this case, they are called once at the start of the function.

It must work with some kind of static boolean. Yep. At least in gcc, this:

int test2()
{
    static int bla = test();
}

Compiles to:

 8048616: b8 30 a0 04 08        mov    $0x804a030,%eax
 804861b: 0f b6 00              movzbl (%eax),%eax
 804861e: 84 c0                 test   %al,%al
 8048620: 75 52                 jne    8048674 <_Z5test2v+0x67>
 ...
 804863c: e8 b3 ff ff ff        call   80485f4 <_Z4testv>
 ...
 8048674: 83 c4 1c              add    $0x1c,%esp
 8048677: 5b                    pop    %ebx
 8048678: 5e                    pop    %esi
 8048679: 5f                    pop    %edi
 804867a: 5d                    pop    %ebp
 804867b: c3                    ret    

So it uses a hidden, function specific boolean (at $0x804a030) + some magic to protect against exceptions and multiple threads calling it at once.

wump
are you sure about that for statics declared inside of functions. You are certainly right for file scope. Are they guaranteed to be run even if the function is never called?
bmargulies
Is that really true? I thought statics weren't initialized until you entered the body of the function - that's how you get around initialization order problems.
Mark Ransom
Local statics (those insides a function body) are initialized exactly once, the first time control passes through the declaration. If control never passes through the declaration, then it is never initialized.
MtnViewMark
Note that GCC also puts some mutex protection in place -- you can see this in the asm. I think the main purpose of that is to catch multiple tasks trying to initialize the static at the same time. However, I have also run into situations where a static initialization causes another static initialization, and another, and eventually recurses back to the original function (don't ask, just lousy code). Anyway, you get interesting runtime errors when that occurs, can't remember specifically what happened but it wasn't good. The mutex protection helped catch it.
Dan
Static variables declared inside a function are indeed initialized only when the function is actually called for the first time.
Remy Lebeau - TeamB
Again, local statics are **NOT** initialized when the function is called the first time. Local statics are initialized when the control passes over the definition.
AndreyT
@AndreyT, that's a mighty fine distinction, sorry that I missed it. If the static is declared in an `if` statement for example and the condition is false, the static won't be initialized.
Mark Ransom
You are right, the "when the function is called for the first time" only applies for the example in the question, not in general.
wump
@Dan: lovely, yet another source of "hidden" locks ;-)
Chris O
+4  A: 

There isn't one compiler. gcc might do this one way, visual studio might do it another.

Conceptually, there is a hidden static boolean, and the compiler is generating an if statement.

bmargulies
As a side note: it is a bit more than that. The compiler also has to make sure the statics are destructed in the reverse order of their construction (if the destructor is non-trivial). This in general case requires registering all constructed statics (with non-trivial destructors) in a run-time list, so that the end of the code it can traverse the list and destruct everything in proper order.
AndreyT
Yup, i was focused on the plain old type case in the question.
bmargulies
A: 

A static in a function is called the first time it is hit. For example:

#include <stdio.h>

class Thing
{
    public:
    Thing()
    {
        printf ("initializing thing\n");
    }
};

int foo()
{
    static Thing *thing = new Thing;
    printf ("done\n");
}

int main()
{
    printf ("calling foo:\n");
    foo();
    printf ("foo returned\n");

    printf ("calling foo:\n");
    foo();
    printf ("foo returned\n");
}

Gives this:

calling foo:
initializing thing
done
foo returned
calling foo:
done
foo returned
Too much scripting this year. I'm reverting to printf. =]
I always thought you should be reverting to puts :)
UncleBens
A: 

A compiler might compile the following:

void foo1() {
    static const char* str = foo2();
}

as if it were written as:

void foo1() {
    static int __str_initialized = 0;
    static const char* str;

    if (__str_initialized == 0) {
        str = foo2();
        __str_initialized = 1;
    }
}

Note that the initialization of __str_initialized can occur as a normal part of initializing the data segment to 0, so nothing special needs to happen there.

Also note that this is not thread-safe, and generally speaking the static variable initialization that compilers perform will not be thread safe (nor is it required to beby the standard - I'm not sure what compilers might make this thread-safe).

Michael Burr
Recent versions of gcc (i.e. those that support the "Itanium ABI" - http://www.codesourcery.com/public/cxx-abi/abi.html#once-ctor) make initialization of local statics thread safe by default. This behavior can be disabled using the "-fno-threadsafe-statics" (http://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Dialect-Options.html) command line option. However, this is indeed a compiler-specific extension.
Void
@Void: thanks. Also I seem to recall C++0x possibly adding this, but I'll need to look it up to make certain.
Michael Burr
If `foo2` throws, the Standard says that the next time `foo1` is called, initialization should be tried again, until it completed. So it needs a try/catch around that stuff.
Johannes Schaub - litb