As far as I know, static or const objects are initialized by default. Is it true? And is there any other type of object which is by default initialized?
PS: I don't think primitive type are initialized by default.
The links' content is confusing but the examples are clearer. From the first link I got that, built-in types (types with no default ctor) are not initialized by default whether they're variables, const or even static, if there're inside a function.
For instance:
1 2 3 4 5 6 7 8 9
int f(bool b)
{
int x; // OK: the value of x is indeterminate
int y = x; // undefined behavior
unsignedchar c; // OK: the value of c is indeterminate
unsignedchar d = c; // OK: the value of d is indeterminate
int e = d; // undefined behavior
return b ? d : 0; // undefined behavior if b is true
}
1 2 3
int n; // static non-class, a two-phase initialization is done:
// 1) zero initialization initializes n to zero
// 2) default initialization does nothing, leaving n being zero
But n above as a global variable and mem as a class data member (in both structs below) are all initialized to zero:
1 2 3 4 5 6 7
struct T1 { int mem; };
struct T2
{
int mem;
T2() { } // "mem" is not in the initializer list
};
But in the second link I got:
1 2 3
T () ;
T t = {} ; // (2)
T {} ; (since C++11)
Zero-initialization is performed in the following situations:
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.
The effects of zero-initialization are:
If T is a scalar type, the object is initialized to the value obtained by explicitly converting the integer literal 0 (zero) to T.
So in this case some expression like int i; should always be default initialized since int is of type scalar.
I'm still rather confused which comprehension from either of the links is right!
IMO, one of the things that makes C++ tricky to learn well is the variety of ways to do things, with a bunch of rules. In cppreference there is a whole section on initialisation, with like 9 ways of doing it:
The rules are all there for a reason, they makes sense, but I guess they could be seen as being confusing if taken in isolation.
There is a school of thought that: instead of worrying about every detail of whether something is automatically initialised for you, just explicitly initialise each variable once you have a sensible value.
frek wrote:
So in this case some expression like int i; should always be default initialized since int is of type scalar.
In the particular case where zero initialisation is happening, yes. But not for every expression of int i; For example variable x in your first example has an indeterminate value.
frek wrote:
I'm still rather confused which comprehension from either of the links is right!
Any reference on initialization is quite a battle between correctness and readibility. Both "static" and "const" mean different things in different contexts, and so does "by default initialized".
To stick to objects of "built-in types" without initializers, that are not subobjects or array elements, and the role of const and static, which I think you're mainly asking about,
1 2 3 4 5 6 7 8 9 10 11
int gn; // initialized to 0
staticint gsn; // initialized to 0
constint gcn; // compile-time error
staticconstint gscn; // compile-time error
int main()
{
int ln; // contains garbage
staticint lsn; // initialized to 0
constint lcn; // compile-time error
staticconstint lscn; // compile-time error
}
If you add {} or () or =0 after any of those, they will all initialize to zero (as in constint lcn{}; or constint lcn(); (oh right, forgot about MVP) or constint lcn = 0; will all do the same thing)
static variables are initialised if no initial value is provided.
Static initialization
There are two forms of static initialization:
1) If possible, constant initialization is applied.
2) Otherwise, non-local static and thread-local variables are zero-initialized.
In practice:
Constant initialization is usually applied at compile time. Pre-calculated object representations are stored as part of the program image. If the compiler doesn't do that, it must still guarantee that the initialization happens before any dynamic initialization.
Variables 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.
const objects are not. They need to be initialised (even if just using {} ).
So I think there are only two ways to assure us about the built-in types to be default initialized:
Initialized them, or use empty curly braces.
If you disagree, please tell me.
Honestly, my worthless 2 cents... objects should be fine without anything at all, as their ctor and internal variable initializations should take care of it. Only simple types like int should need an init unless you have a value in mind.
Note that default initialisation of an object of type int would result in an object with an indeterminate value;
an attempt to use this indeterminate value would engender undefined behaviour.
@JLBorges
I got your points, they're right. But I'm still not able to wrap things up. Could you write what you've gathered, for example, on a conclusion list, regarding built-in types default initialization so that they can be used in various situations without undefined behaviour.
There is a very big difference between
T t();
which I alluded to, and
T();
which you have just mentioned.
The first tries (unsuccessfully) to declare a function called t that returns an entity of type T. (It would compile - albeit not do what you presumably intended - if you declared it outside of any other function, including main()). The second (successfully if there is one) invokes a constructor of T with no parameters.
The following will invoke compiler warnings, but not errors. It will produce garbage, obviously, but will run.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
#include <iostream>
struct T {
float f;
int gn;
char ch;
};
T t(); // <==== Move it here. Note the (). This is the declaration of a function, NOT a variable of type T
int main() {
T t; // <==== Unfortunate use of the same name, but not an error. Note: no (). This is a variable of type T.
std::cout << t.gn << ' ' << t.f << ' ' << int(t.ch); // garbage, obviously, but a separate issue
}
So I think there are only two ways to assure us about the built-in types to be default initialized:
Initialized them, or use empty curly braces. If you disagree, please tell me.
I disagree.
"default initialized" means that they would be initialised without any action on your part.
If you initialise them by explicitly stating a value then clearly you aren't default-initialising!
If you use empty curly braces then you ... are initialising them with a type equivalent of a zero. "Uniform initialisation" or "brace initialisation" or "initialising to a default value", not "default initialisation". You are assigning a value, even if you don't state that value explicitly.
Could you write what you've gathered, for example, on a conclusion list, regarding built-in types default initialization so that they can be used in various situations without undefined behaviour.
It's already been said:
If you add {} or () or =0 after any of those, they will all initialize to zero (as in const int lcn{}; or const int lcn = 0; will all do the same thing)
> there are only two ways to assure us about the built-in types to be default initialized:
> Initialized them, or use empty curly braces.
As I said earlier, just initialise everything, It's been a golden rule in programming from the very beginning. Don't forget to validate all the data as well.
The rules that will allow the compiler to initialise for you, are a small convenience IMO, as long as one can remember the rules. If one is having trouble with that, then initialise yourself.
The compiler should be able to warn about possible use of uninitialised values.