Initialization

< cpp‎ | language

Initialization of a variable provides its initial value at the time of construction.

The initial value may be provided in the initializer section of a declarator or a new expression. It also takes place during function calls: function parameters and the function return values are also initialized.

For each declarator, the initializer may be one of the following:

( expression-list ) (1)
= expression (2)
{ initializer-list } (3)
1) comma-separated list of arbitrary expressions and braced-init-lists in parentheses
2) the equals sign followed by an expression
3) braced-init-list: possibly empty, comma-separated list of expressions and other braced-init-lists

Depending on context, the initializer may invoke:

If no initializer is provided, the rules of default initialization apply.

Non-local variables

All non-local variables with static storage duration are initialized as part of program startup, before the execution of the main function begins (unless deferred, see below). All variables with thread-local storage duration are initialized as part of thread launch, sequenced-before the execution of the thread function begins. For both of these classes of variables, initialization occurs in two distinct stages:

Static initialization

1) If permitted, Constant initialization takes place first (see Constant initialization for the list of those situations). In practice, constant initialization is usually performed at compile time, and pre-calculated object representations are stored as part of the program image. If the compiler doesn't do that, it still has to guarantee that this initialization happens before any dynamic initialization.
2) For all other non-local static and thread-local variables, Zero initialization takes place. In practice, variables that are going to be zero-initialized are placed in the .bss segment of the program image, which occupies no space on disk, and is zeroed out by the OS when loading the program.

Dynamic initialization

After all static initialization is completed, dynamic initialization of non-local variables occurs in the following situations:

1) Unordered dynamic initialization, which applies only to (static/thread-local) class template static data members and variable templates (since C++14) that aren't explicitly specialized. Initialization of such static variables is indeterminately sequenced with respect to all other dynamic initialization except if the program starts a thread before a variable is initialized, in which case its initialization is unsequenced (since C++17). Initialization of such thread-local variables is unsequenced with respect to all other dynamic initialization.
2) Partially-ordered dynamic initialization, which applies to all inline variables that are not an implicitly or explicitly instantiated specialization. If a partially-ordered V is defined before ordered or partially-ordered W in every translation unit, the initialization of V is sequenced before the initialization of W (or happens-before, if the program starts a thread).
(since C++17)
3) Ordered dynamic initialization, which applies to all other non-local variables: within a single translation unit, initialization of these variables is always sequenced in exact order their definitions appear in the source code. Initialization of static variables in different translation units is indeterminately sequenced. Initialization of thread-local variables in different translation units is unsequenced.

If the initialization of a non-local variable with static or thread storage duration exits via an exception, std::terminate is called.

Early dynamic initialization

The compilers are allowed to initialize dynamically-initialized variables as part of static initialization (essentially, at compile time), if the following conditions are both true:

1) the dynamic version of the initialization does not change the value of any other object of namespace scope prior to its initialization
2) 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.

Because of the rule above, if initialization of some object o1 refers to an namespace-scope object o2, which potentially requires dynamic initialization, but is defined later in the same translation unit, it is unspecified whether the value of o2 used will be the value of the fully initialized o2 (because the compiler promoted initialization of o2 to compile time) or will be the value of o2 merely zero-initialized.

inline double fd() { return 1.0; }
extern double d1;
double d2 = d1;   // unspecified:
                  // dynamically initialized to 0.0 if d1 is dynamically initialized, or
                  // dynamically initialized to 1.0 if d1 is statically initialized, or
                  // statically initialized to 0.0 (because that would be its value
                  // if both variables were dynamically initialized)
double d1 = fd(); // may be initialized statically or dynamically to 1.0

Deferred dynamic initialization

It is implementation-defined whether dynamic initialization happens-before the first statement of the main function (for statics) or the initial function of the thread (for thread-locals), or deferred to happen after.

If the initialization of a non-inline variable (since C++17) is deferred to happen after the first statement of main/thread function, it happens before the first odr-use of any variable with static/thread storage duration defined in the same translation unit as the variable to be initialized. If no variable or function is odr-used from a given translation unit, the non-local variables defined in that translation unit may never be initialized (this models the behavior of an on-demand dynamic library). However, as long as anything from a translation unit is odr-used, all non-local variables whose initialization or destruction has side effects will be initialized even if they are not used in the program.

If the initialization of an inline variable is deferred, it happens before the first odr-use of that specific variable.

(since C++17)
// - File 1 -
#include "a.h"
#include "b.h"
B b;
A::A(){ b.Use(); }
 
// - File 2 -
#include "a.h"
A a;
 
// - File 3 -
#include "a.h"
#include "b.h"
extern A a;
extern B b;
 
int main() {
    a.Use();
    b.Use();
}
 
// If a is initialized before main is entered, b may still be uninitialized
// at the point where A::A() uses it (because dynamic init is indeterminately sequenced
// across translation units)
 
// If a is initialized at some point after the first statement of main (which odr-uses
// a function defined in File 1, forcing its dynamic initialization to run),
// then b will be initialized prior to its use in A::A

Static local variables

For initialization of locals (that is, block scope) static and thread-local variables, see static local variables.

Class members

Non-static data members can be initialized with member initializer list or with a default member initializer.

Notes

The order of destruction of non-local variables is described in std::exit.

Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
CWG 2026 C++14 zero-init was specified to always occur first, even before constant-init no zero-init if constant init applies

See also