Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why file scope static variables have to be zero-initialized?

C++ default initialization doesn't zero out variables with auto storage, why the special treatment for static storage variables?

Was it something defined by C and C++ just have to be compatible with? If that's the case why C decides to do zero-initialization?

If a file scope static variables is provided with a initializer, they will be zero-initialized first and then constant/dynamic initialized again. Isn't that redundant? For example the following code is from cppreference:http://en.cppreference.com/w/cpp/language/zero_initialization

#include <string>

double f[3]; // zero-initialized to three 0.0's
int* p;   // zero-initialized to null pointer value
std::string s; // zero-initialized to indeterminate value
               // then default-initialized to ""
int main(int argc, char* argv[])
{
    static int n = argc; // zero-initialized to 0
                         // then copy-initialized to argc
    delete p; // safe to delete a null pointer
}

In this case, why n can't be initialized to argc directly?

EDIT: Part of this question has been answered by the question here: Static variable initialization? But I don't think it's a duplicate because the answers in the other question didn't answer my second question, ie. why the 2 staged initialization. Besides, the title of the other post doesn't really say what exactly the question is.

like image 303
swang Avatar asked Sep 09 '14 10:09

swang


2 Answers

The behaviour on the Operating Systems where C was developed has shaped these Standard stipulations. As applications load, the OS loader provides some memory for the BSS. It's desirable to clear it to zeros because if some other process had been using that memory earlier, the program you're starting could snoop on the prior process's memory contents, potentially seeing passwords, conversations or other data. Not every early or simple OS cares about this, but most do, so on most the initialisation is effectively "free" as it's a task the OS will do anyway.

Having this default of 0 makes it easy for the implementation to see refer to flags set during dynamic initialisation, as there will be no uninitialised memory read and consequent undefined behaviour. For example, given...

void f() { static int n = g(); }

...the compiler/implementation may implicitly add something like a static bool __f_statics_initialised variable too - which "luckily" defaults to 0 / false due to the zeroing behaviour - along with initialisation code akin to (a possibly thread safe version of)...

if (!__f_statics_initialised)
{
    n = g();
    __f_statics_initialised = true;
}

For the above scenario the initialisation is done on first call, but for global variables it's done in an unspecified per-object ordering, sometime before main() is invoked. In that scenario, having some object-specific initialisation code and dynamic initialisation able to differentiate statics in uninitialised state from those they know need to be set to non-zero values makes it easier to write robust start-up code. For example, functions can check if a non-local static pointer is still 0, and new an object for it if so.

It's also noteworthy that many CPUs have highly efficient instructions to zero out large swathes of memory.

like image 77
Tony Delroy Avatar answered Nov 12 '22 07:11

Tony Delroy


Zero-initialization of globals comes "for free" because the storage for them is allocated in the "BSS" segment before main() starts. That is, when you access your pointer p, the pointer itself must be stored somewhere, and that somewhere is actually a specific chunk of bits in BSS. Since it must be initialized to something, why not zero?

Now, why don't automatic/stack variables do this? Because that would cost time: allocation on the stack is nothing more than incrementing (or decrementing, a matter of perspective) the stack pointer. Whatever garbage was there can be left there (according to C). Since we can't get zero-init for free, we don't get it at all (because again, it's C, where we don't like to pay for things we don't use).

Default-initializing a std::string or other class type is a bit more complex: C++ requires that it is initialized somehow, and the default constructor is of course the one that gets used, and yes, technically it is zero-initialized first, but as discussed that zero-init happened "for free." It might be permissible for an implementation which can sufficiently analyze std::string to determine at build time how to initialize its bits as if the default constructor were called, but I don't know if any implementation does that.

like image 21
John Zwinck Avatar answered Nov 12 '22 07:11

John Zwinck