Default variable value

C++

C++ Problem Overview


If I don't assign a value to a variable when I declare it, does it default to zero or just whatever was previously in the memory?

e.g.

float x;

C++ Solutions


Solution 1 - C++

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 - C++

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 - C++

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
}

Solution 4 - C++

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 5 - C++

This depends on where you declare it. Variables in the global scope are initialized with 0, and stack-variables are undefined.

Solution 6 - C++

I think it's undefined. I think some compilers, when compiling in debug mode, initialize it to zero. But it's also ok to have it be whatever was already there in memory. Basically - don't rely on either behavior.

UPDATE: As per the comments - global variables will be zero-initialized. Local variables will be whatever.

To answer your second question:

> Thanks - following on from this then, is there a shortcut to assign zero to all of the following?: float x1, x2, x3, x4, x5, y1, y2, y3, y4, y5

You could do

float x[5] = {0,0,0,0,0}; float y[5] = {0,0,0,0,0};

and use x[0] instead of x1.

Solution 7 - C++

Using the value of any variable prior to initialization (note that static-storage-duration objects are always initialized, so this only applies to automatic storage duration) results in undefined behavior. This is very different from containing 0 as the initial value or containing a random value. UB means it's possible that anything could happen. On implementations with trap bits it might crash your program or generate a signal. It's also possible that multiple reads result in different unpredictable values, among any other imaginable (or unimaginable) behavior. Simply do not use the value of uninitialized variables.

Note: The following was edited based on comments:

Note that code like this is invalid unless you can assure that the type foo_t does not have padding bits:

foo_t x;
int i;
for (i=0; i<N; i++) x = (x<<1) | get_bit();

Even though the intent is that the "random value" initially in x gets discarded before the loop ends, the program may invoke UB as soon as it accesses x to perform the operation x<<1 on the first iteration, and thus the entire program output is invalidated.

Solution 8 - C++

It can be compiler specific but generally release builds don't initialise variables to any particular value, so you get whatever is left in memory. Certain magic numbers are used in debug builds by some compilers to mark specific areas of memory however.

Solution 9 - C++

C++ does not instantiate variables. The value of x is whatever happened to be in the memory at the time. Never assume anything about its initial value.

Solution 10 - C++

I am just learning about this and using short example I find out that this is possible related to compiler version. In learning materials it clearly say that global variable are by default set to zero, and local variable are using current value from that memory location and it s good practice to initialize variable to zero. Then I tried this code

#include <iostream>

using namespace std;

int rezultat;

main()
{
    int localVariabla1;

    int nr,result;
    cout << nr << endl;
    cout << result << endl;

    for (int i = 0; i<3; i++)
    {
        cout << "Enter number" << endl;
        cin >> nr;
        result += nr;
    }
cout << "Result is : " << result;

} 

After I compile this code resulting values for all "printed" values are zero, before entering into a if conditions and result is correct.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCaptainProgView Question on Stackoverflow
Solution 1 - C++Alok SaveView Answer on Stackoverflow
Solution 2 - C++Kirill V. LyadvinskyView Answer on Stackoverflow
Solution 3 - C++giacomo-bView Answer on Stackoverflow
Solution 4 - C++James KanzeView Answer on Stackoverflow
Solution 5 - C++kusmaView Answer on Stackoverflow
Solution 6 - C++ClaudiuView Answer on Stackoverflow
Solution 7 - C++R.. GitHub STOP HELPING ICEView Answer on Stackoverflow
Solution 8 - C++EphphathaView Answer on Stackoverflow
Solution 9 - C++matzahboyView Answer on Stackoverflow
Solution 10 - C++explorerView Answer on Stackoverflow