Default variable value

Solution 1:

A declared variable can be Zero Initialized, Value Initialized or Default Initialized.

The C++03 Standard 8.5/5 aptly defines each:

To zero-initialize an object of type T means:

— if T is a scalar type (3.9), the object is set to the value of 0 (zero) converted to T;
— if T is a non-union class type, each nonstatic data member and each base-class subobject
is zero-initialized;
— if T is a union type, the object’s first named data member is zero-initialized;
— if T is an array type, each element is zero-initialized;
— if T is a reference type, no initialization is performed.

To default-initialize an object of type T means:
— if T is a non-POD class type (clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
— if T is an array type, each element is default-initialized;
— otherwise, the object is zero-initialized.

To value-initialize an object of type T means:
— if T is a class type (clause 9) with a user-declared constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
— if T is a non-union class type without a user-declared constructor, then every non-static data member and base-class component of T is value-initialized;
— if T is an array type, then each element is value-initialized;
— otherwise, the object is zero-initialized

For example:

#include<iostream>
using namespace std;

static int a; //Zero Initialized
int b; //Zero Initialized

int main()
{
    int i;  //Undefined Behavior, Might be Initialized to anything
    static int j; //Zero Initialized

    cout<<"\nLocal Uninitialized int variable [i]"<<i<<"\n";

    cout<<"\nLocal Uninitialized Static int variable [j]"<<j<<"\n";

    cout<<"\nGlobal Uninitialized Static int variable [a]"<<a<<"\n";

    cout<<"\nGlobal Uninitialized int variable [b]"<<b<<"\n";

    return 0;
}

You will notice The results for variable i will be different on different compilers. Such local uninitialized variables SHOULD NEVER be used. In fact, if you turn on strict compiler warnings, the compiler shall report an error about it. Here's how codepad reports it an error.

cc1plus: warnings being treated as errors
In function 'int main()':
Line 11: warning: 'i' is used uninitialized in this function

Edit: As rightfully pointed out by @Kirill V. Lyadvinsky in the comments, SHOULD NEVER is a rather very strong word, and there can be perfectly valid code which might use uninitialized variables as he points out an example in his comment. So, I should probably say:
You should never be using uninitialized variables unless you know exactly what you are doing.

Solution 2:

It depends. If this is a local variable (an object with automatic storage duration) it will be uninitialized, if it is a global variable (an object with static storage duration) it will be zero initialized. Check also this answer.

Solution 3:

It depends on the lifetime of the variable. Variables with static lifetime are always zero-initialized before program start-up: zero-initialization for basic types, enums and pointers is the same as if you'd assigned 0, appropriately converted to the type, to it. This occurs even if the variable has a constructor, before the constructor is called.

Solution 4:

Since the current top-answer was written in 2011 and only refers to C++03, I am providing an updated answer to keep into account changes made after C++11. Note that I am stripping any information that only held true until C++03 or C++11 and unnecessary notes that can be seen in the original sources. I am quoting the original specifications as much as I can, in order to avoid unnecessary reformulation which may lead to inexact information. Please consult the original sources I am providing if you are interested in diving deeper into a certain topic. Also, be warned that I am mainly focusing on rules regarding * Default initialization * Undefined behavior * Zero-initialization Since it seems to me that these are the most important aspects needed to understand how a variable behaves "by default", as the question is asking.

Default initialization is performed in some cases:

  1. when a variable with automatic, static, or thread-local storage duration is declared with no initializer;
  2. when an object with dynamic storage duration is created by a new-expression with no initializer;
  3. when a base class or a non-static data member is not mentioned in a constructor initializer list and that constructor is called.

and the effects of this default initialization are:

  • if T is a non-POD (until C++11) class type, the constructors are considered and subjected to overload resolution against the empty argument list. The constructor selected (which is one of the default constructors) is called to provide the initial value for the new object;

  • if T is an array type, every element of the array is default-initialized;

  • otherwise, nothing is done: the objects with automatic storage duration (and their subobjects) are initialized to indeterminate values.

Meaning that if the uninitialized variable is a local (say, an int only present in a function's scope), its value is indeterminate (undefined behavior). cppreference strongly discourages the usage of uninitialized variables.

As a side note, even though most modern compilers will issue an error (at compile-time) if they detect that an uninitialized variable is being used, they usually fail to do so in cases were you are "tricking" them to think you may be initializing the variable somehow, such as in:

int main()
{
    int myVariable;
    myFunction(myVariable);  // does not change the variable
    cout << myVariable << endl;  // compilers might think it is now initialized
}

Starting from C++14, the following holds (note that std::byte was introduced with C++17):

Use of an indeterminate value obtained by default-initializing a non-class variable of any type is undefined behavior (in particular, it may be a trap representation), except in the following cases:

  • if an indeterminate value of type unsigned char or std::byte is assigned to another variable of type (possibly cv-qualified) unsigned char or std::byte (the value of the variable becomes indeterminate, but the behavior is not undefined);

  • if an indeterminate value of type unsigned char or std::byte is used to initialize another variable of type (possibly cv-qualified) unsigned char or std::byte;

  • if an indeterminate value of type unsigned char or std::byte (since C++17) results from

    • the second or third operand of a conditional expression,
    • the right operand of the comma operator,
    • the operand of a cast or conversion to (possibly cv-qualified) unsigned char or std::byte,
    • a discarded-value expression.

Additional details about the default initialization of variables and their behavior can be found here.

To dive deeper into indeterminate values, in 2014 the following changes were made (as Shafik Yaghmour pointed out here with additional useful resources):

If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, an object with automatic or dynamic storage duration has indeterminate value. [Note: Objects with static or thread storage duration are zero-initialized]

to:

If no initializer is specified for an object, the object is default-initialized. When storage for an object with automatic or dynamic storage duration is obtained, the object has an indeterminate value, and if no initialization is performed for the object, that object retains an indeterminate value until that value is replaced. [Note: Objects with static or thread storage duration are zero-initialized] If an indeterminate value is produced by an evaluation, the behavior is undefined except in the following cases:

  • If an indeterminate value of unsigned narrow character type is produced by the evaluation of:

    • the second or third operand of a conditional expression (5.16 [expr.cond]),

    • the right operand of a comma,

    • the operand of a cast or conversion to an unsigned narrow character type, or

    • a discarded-value expression,

      then the result of the operation is an indeterminate value.

  • If an indeterminate value of unsigned narrow character type is produced by the evaluation of the right operand of a simple assignment operator whose first operand is an lvalue of unsigned narrow character type, an indeterminate value replaces the value of the object referred to by the left operand.

  • If an indeterminate value of unsigned narrow character type (3.9.1 [basic.fundamental]) is produced by the evaluation of the initialization expression when initializing an object of unsigned narrow character type, that object is initialized to an indeterminate value.

Finally, there is the subject of zero-initialization which is performed in the following situations:

  1. For every named variable with static or thread-local storage duration that is not subject to constant initialization (since C++14), before any other initialization.

  2. As part of value-initialization sequence for non-class types and for members of value-initialized class types that have no constructors, including value initialization of elements of aggregates for which no initializers are provided.

  3. When an array of any character type is initialized with a string literal that is too short, the remainder of the array is zero-initialized.

The effects of zero initialization are:

  • If T is a scalar type, the object's initial value is the integral constant zero explicitly converted to T.

  • If T is an non-union class type, all base classes and non-static data members are zero-initialized, and all padding is initialized to zero bits. The constructors, if any, are ignored.

  • If T is a union type, the first non-static named data member is zero-initialized and all padding is initialized to zero bits.

  • If T is array type, each element is zero-initialized

  • If T is reference type, nothing is done.

Following are some examples:

#include <iostream>
#include <string>

struct Coordinates {
    float x, y;
};

class WithDefaultConstructor {
    std::string s;
}

class WithCustomConstructor {
    int a, b;

public:
    WithCustomConstructor() : a(2) {}
}

int main()
{
    int a;    // Indeterminate value (non-class)

    int& b;   // Error

    std::string myString;    // Zero-initialized to indeterminate value
                             // but then default-initialized to ""
                             // (class, calls default constructor)

    double coordsArray[2];   // Both will be 0.0 (zero-initialization)

    Coordinates* pCoords;    // Zero-initialized to nullptr

    Coordinates coords = Coordinates();

    // x: 0.0
    // y: 0.0
    std::cout << "x: " << coords.x << '\n'
        "y: " << coords.y << std::endl;

    std::cout << a.a << a.b << a.c << '\n';

    WithDefaultConstructor wdc;    // Since no constructor is provided,
                                   // calls the default constructor

    WithCustomConstructor wcs;     // Calls the provided constructor
                                   // a is initialized, while b is
                                   // default-initialized to an indeterminate value
}