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;
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;
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.
int x; const bool ok = get_value( x ); if ( ok ) { /* use x */ }
–
Overgrow C
coder, not a C++
one. Today, in C
, whitespace is cheap: if it isn't expensive in C++
you can use more of it and make your source code easier to parse by humans (subjective, I know). –
Goree reinterpret_cast
and reading/writing raw byte representations from and to streams. but Steve’s examples are of course just as valid. –
Metagalaxy 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.
static vs. automatic
as opposed to global vs. local
terminology –
Breen 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:
- when a variable with automatic, static, or thread-local storage duration is declared with no initializer;
- when an object with dynamic storage duration is created by a new-expression with no initializer;
- 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
orstd::byte
is assigned to another variable of type (possibly cv-qualified)unsigned char
orstd::byte
(the value of the variable becomes indeterminate, but the behavior is not undefined);if an indeterminate value of type
unsigned char
orstd::byte
is used to initialize another variable of type (possibly cv-qualified)unsigned char
orstd::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
orstd::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:
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.
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.
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
}
myString
before the default constructor is called. coordsArray
and pCoords
are not zero-initialized, so they will have indeterminate values. –
Bought 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, enum
s 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.
This depends on where you declare it. Variables in the global scope are initialized with 0, and stack-variables are undefined.
static/automatic
than global/local
. Local variable can be static too as you know –
Breen 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
.
float x[5] = {0};
–
Eustacia 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.
on implementations with trap bits it might crash your program
? can you go into more detail? what value can possibly be put into x
(one of 256) that would crash the program when it was being read? i'd understand if x
was memory that didn't belong to the program, but it does since it has been allocated on the stack. –
Claviform uintN_t
types are not allowed to have padding/trap bits. Trap bits are just one possible example of the mechanism/rationale for UB; the impossibility of trap bits with uint8_t
does not change the fact that you're invoking UB. –
Moonlight x
can contain any value. it's not that the code can do anything - it's that the value of an uninitialized variable can be anything. that being said, any possible value x
can have in your example will still result in the code working as intended. can you show me where in the spec it says code can literally have any behavior if an uninitialized variable is encountered? all i see is "If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate." –
Claviform unsigned int x
is uninitialized, then 2*x
is not guaranteed to be even and 0*x
not guaranteed to be 0 - indeterminism was contagious through arithmetic. IIRC that was out of paranoia how bad indeterminate values might be, depending how the C standard was interpreted, rather than certain knowledge that they were allowed to be, since C was one language in which that system might be implemented. –
Lattie 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.
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.
x
has a value at all. It's quite possible that the implementation puts x
on the stack, and only grows the stack when x
is assigned to. That means there's no memory allocated for x
until the first write to it. –
Holna 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.
© 2022 - 2024 — McMap. All rights reserved.
x1 = x2 = x3 = x4 = x5 = y1 = y2 = y3 = y4 = y5 = 0.0f;
– Bermudezint
or any other type/class that has a "zero"). – Readiness