When Are Static and Global Variables Initialized

When are static and global variables initialized?

By static and global objects, I presume you mean objects with
static lifetime defined at namespace scope. When such objects
are defined with local scope, the rules are slightly different.

Formally, C++ initializes such variables in three phases:
1. Zero initialization
2. Static initialization
3. Dynamic initialization
The language also distinguishes between variables which require
dynamic initialization, and those which require static
initialization: all static objects (objects with static
lifetime) are first zero initialized, then objects with static
initialization are initialized, and then dynamic initialization
occurs.

As a simple first approximation, dynamic initialization means
that some code must be executed; typically, static
initialization doesn't. Thus:

extern int f();

int g1 = 42; // static initialization
int g2 = f(); // dynamic initialization

Another approximization would be that static initialization is
what C supports (for variables with static lifetime), dynamic
everything else.

How the compiler does this depends, of course, on the
initialization, but on disk based systems, where the executable
is loaded into memory from disk, the values for static
initialization are part of the image on disk, and loaded
directly by the system from the disk. On a classical Unix
system, global variables would be divided into three "segments":


text:


The code, loaded into a write protected area. Static
variables with `const` types would also be placed here.

data:


Static variables with static initializers.

bss:


Static variables with no-initializer (C and C++) or with dynamic
initialization (C++). The executable contains no image for this
segment, and the system simply sets it all to `0` before
starting your code.

I suspect that a lot of modern systems still use something
similar.

EDIT:

One additional remark: the above refers to C++03. For existing
programs, C++11 probably doesn't change anything, but it does
add constexpr (which means that some user defined functions
can still be static initialization) and thread local variables,
which opens up a whole new can of worms.

static global variables initialization order


6.6.3 Dynamic initialization of non-local variables [basic.start.dynamic]


  1. Dynamic initialization of a non-local
    variable with static storage duration is unordered if the variable is
    an implicitly or explicitly instantiated specialization, is
    partially-ordered if the variable is an inline variable that is not an
    implicitly or explicitly instantiated specialization, and otherwise is
    ordered. [ Note: An explicitly specialized non-inline static data
    member or variable template specialization has ordered initialization.
    — end note ]
  2. Dynamic initialization of non-local variables V and W
    with static storage duration are ordered as follows:

    • If V and
      W have ordered initialization and V is defined before W within a
      single translation unit, the initialization of V is sequenced before
      the initialization of W.
    • If V
      has partially-ordered initialization, W does not have unordered
      initialization, and V is defined before W in every translation unit in
      which W is defined, then

      • if the program starts a thread
        (4.7) other than the main thread (6.6.1), the initialization of V
        strongly happens before the initialization of W;
      • otherwise,
        the initialization of V is sequenced before the initialization of W.
    • Otherwise, if the program starts a thread other than the main
      thread before either V or W is initialized, it is unspecified in which
      threads the initializations of V and W occur; the initializations are
      unsequenced if they occur in the same thread.
    • Otherwise, the
      initializations of V and W are indeterminately sequenced.

Quoted from N4659, formatting adjusted to work with the markdown supported here.

For the exact definition of dynamic initialization, see the preceding subsesction 6.6.2 [basic.start.static].

Are global variables always initialized to zero in C?

Yes, all members of a are guaranteed to be initialised to 0.

From section 3.5.7 of the C89 standard

If an object that has static storage duration is not initialized
explicitly, it is initialized implicitly as if every member that has
arithmetic type were assigned 0 and every member that has pointer type
were assigned a null pointer constant.

Why are global and static variables initialized to their default values?


  1. Security: leaving memory alone would leak information from other processes or the kernel.

  2. Efficiency: the values are useless until initialized to something, and it's more efficient to zero them in a block with unrolled loops. The OS can even zero freelist pages when the system is otherwise idle, rather than when some client or user is waiting for the program to start.

  3. Reproducibility: leaving the values alone would make program behavior non-repeatable, making bugs really hard to find.

  4. Elegance: it's cleaner if programs can start from 0 without having to clutter the code with default initializers.

One might then wonder why the auto storage class does start as garbage. The answer is two-fold:

  1. It doesn't, in a sense. The very first stack frame page at each level (i.e., every new page added to the stack) does receive zero values. The "garbage", or "uninitialized" values that subsequent function instances at the same stack level see are really the previous values left by other method instances of your own program and its library.

  2. There might be a quadratic (or whatever) runtime performance penalty associated with initializing auto (function locals) to anything. A function might not use any or all of a large array, say, on any given call, and it could be invoked thousands or millions of times. The initialization of statics and globals, OTOH, only needs to happen once.

When does initialisation of global variables happen?

Since you didn't define the language you're talking about, I assumed it to be C++.

In computer programming, a global variable is a variable that is accessible in every scope (unless shadowed). Interaction mechanisms with global variables are called global environment (see also global state) mechanisms. The global environment paradigm is contrasted with the local environment paradigm, where all variables are local with no shared memory (and therefore all interactions can be reconducted to message passing). Wikipedia.


In principle, a variable defined outside any function (that is, global, namespace, and class static variables) is initialized before main() is invoked. Such nonlocal variables in a translation unit are initialized in their declaration order (§10.4.9). If such a variable has no explicit initializer, it is by default initialized to the default for its type (§10.4.2). The default initializer value for built-in types and enumerations is 0. [...] There is no guaranteed order of initialization of global variables in different translation units. Consequently, it is unwise to create order dependencies between initializers of global variables in different compilation units. In addition, it is not possible to catch an exception thrown by the initializer of a global variable (§14.7). It is generally best to minimize the use of global variables and in particular to limit the use of global variables requiring complicated initialization. See.

C++ static variables initialization order

The first scenario is well-defined in [basic.start.init]/2:

Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5) before any other initialization takes place.

Constant initialization is performed:

  • if each full-expression (including implicit conversions) that appears in the initializer of a reference with static or thread storage duration is a constant expression (5.19) and the reference is bound to an lvalue designating an object with static storage duration or to a temporary (see 12.2);
  • if an object with static or thread storage duration is initialized by a constructor call, if the constructor is a constexpr constructor, if all constructor arguments are constant expressions (including conversions), and if, after function invocation substitution (7.1.5), every constructor call and full-expression in the mem-initializers and in the brace-or-equal-initializers for non-static data members is a constant expression;
  • if an object with static or thread storage duration is not initialized by a constructor call and if every full-expression that appears in its initializer is a constant expression.

Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. Static initialization shall be performed before any dynamic initialization takes place. (...)

(Emphasis mine)

The upshot of this fairly lengthy paragraph is that

int n = 2;

is static initialization, while

int k = n;

is dynamic initialization (because n is not a constant expression), and therefore n is initialized before k even if it appears later in the code.

The same logic applies in the case of the Base::static_constructor example -- because the constructor of Base::static_constructor is not constexpr, Base::constructor is dynamically initialized, whereas Base::i is statically initialized. The initialization of Base::i therefore takes place before the initialization of Base::constructor.

On the other hand, the second case with

int n = func();

puts you squarely in the territory of unspecified behavior, and it is quite explicitly mentioned in [basic.start.init]/3:

An implementation is permitted to perform the initialization of a non-local variable with static storage duration as a static initialization even if such initialization is not required to be done statically, provided that

  • the dynamic version of the initialization does not change the value of any other object of namespace scope prior to its initialization, and
  • the static version of the initialization produces the same value in the initialized variable as would be produced by the dynamic initialization if all variables not required to be initialized statically were initialized dynamically.

[Note: As a consequence, if the initialization of an object obj1 refers to an object obj2 of namespace scope potentially requiring dynamic initialization and defined later in the same translation unit, it is unspecified whether the value of obj2 used will be the value of the fully initialized obj2 (because obj2 was statically initialized) or will be the value of obj2 merely zero-initialized. For example,

inline double fd() { return 1.0; }
extern double d1;
double d2 = d1; // unspecified:
// may be statically initialized to 0.0 or
// dynamically initialized to 0.0 if d1 is
// dynamically initialized, or 1.0 otherwise
double d1 = fd(); // may be initialized statically or dynamically to 1.0

-- end note]



Related Topics



Leave a reply



Submit