Difference between various initializers in C++ - c++

I've only recently started learning C++ as part of my 10th Grade syllabus, and am only aware of the basics, thus simple answers (if possible) will be appreciated.
I'm rather confused between initialization and assignment.
//Case 1
int a=5; //This is initialization
a=6; //This is assignment
From what I've understood, a variable is initialized when you give it a value to hold while declaring it. Changing this later in the code will be an assignment. Right?
What about :
//Case 2
int b;
{
//Block of code which does not call variable b
.
.
.
//End of block
}
b=6; // Is this initialization as well?
While 'b' is uninitialized when we declare, we later assign the value '6'. Can we say the 'b' is initialized now? Or are the terms initialized and uninitialized not applicable to 'b' anymore?
I read the an uninitialized variable holds "garbage values" till it isn't initialized. What exactly are "garbage values"?
What is the difference between the following initializers : '()', '{}', and '='?

Okay, once you declare a variable without assigning any value, like
int b;
that means that the compiler reserves some space in the memory to hold the value (to be exact, in this case the memory is reserved on the stack). But since you didn't assign any value to the variable, it still holds the value, that the assigned space in memory had before. And that can be anything. Those are garbage values.
Initializers:
int b(1);
assigns the value 1 to be (in general, it calls a constructor of the type)
The brackets can be used to initialize arrays like this (edit):
int b[] = {1, 3, 5, 7};
And the = just assigns a value. The difference between this and the first will only become interesting when dealing with more complex types (classes), where you have constructors

Easilly spoken:
Uninitialize variable:
int a;
You are declare a variable that means you allocate memory but dont assign a value to it. So its compiler dependend if the value is set to 0 or not. So there could be anything in. Thats waht you called garbage values.
Initialized variable:
int a = 0;
You are declare a variable that means you allocate memory and assigne a value to it.
Assigne Values:
a = 10;
You assigne a rvalue (in this case 10) to a lvalue ( a). So you dont allocate new memory.

You're basically right.
Some older texts call the first assignment to an uninitialised variable an "initialisation", although this is not strictly accurate.
"Garbage values" are arbitrary values. They could look meaningful or could be totally random.

Initialization serves to initialize an uninitialized value.
It can be done my means of copy constructor, i.e. int a = 1; or int a(1);, it can be done by means of assignment, i.e. int a; a = 1;, it can be done via a function, i.e. int a; init(a);. Initialization is not a "language thing", it is just the act of specifying an unspecified value.
A "garbage value" is an arbitrary value. Some storage will be given to the uninitialized object, and attempting to read it will produce a value of whatever happened to be in that memory.

Related

C++ Pointers and C-style array initialization

Long time java programmer here, new to C++. I have been working with C-style "traditional" arrays (similar to arrays in java). I understand in C++ we can create a simple array as follows:
Person people[3];
The contents of this array is essentially uninitialized junk values. When I print out the contents of each element in the array, I (believe) am getting the memory address of each element.
for(int i = 0; i < 3; i++){std::cout << &person[i] << std::endl;}
Results:
//Note, I get different results here when I use an enhanced for loop vs an iterator for loop. Weird.
00EFFB6C
00EFFBA8
00EFFBE4
Now, here is the part I have failed to get a clear explanation on. I create a pointer to one of the elements in the array. I then ask for some value back from that pointer. In java, I would expect to get a null pointer, but in C++, that is not happening.
Instead, I get the default value, as though this element is initialized:
Person* person1Ptr = &people[0];//Points to an uninitialized value
std::cout << person1Ptr->getFirstName() << std::endl;//Output: "Default First Name", expected nullptr
When I try to get the first name of an element using a reference, this doesn't work (presumably because the value doesn't exist).
Full paste of code: https://pastebin.com/cEadfJhr
From my research, C++ does NOT fill arrays with objects of the specified type automagically.
How is my person1Ptr returning a value?
I believe the problem stems from this misconception :
From my research, C++ does NOT fill arrays with objects of the specified type automagically.
C++ objects have value semantics. Defining a local variable of type T concretely creates a unique instance of that type, it is not a handle to a potential T. The expression Foo f; is conceptually equivalent to the Java expression Foo f = new Foo();. Additionally, value semantics means assignment usually implies a copy. The C++ expression Foo f; Foo g = f; is conceptually equivalent to the Java expression Foo f = new Foo(); Foo g = f.Clone();.
In the case of an array, defining a local variable Foo f[3]; immediately creates three instances of Foo as elements of the f array. Your misconception may come from the fact that creating an object in C++ does not imply that it has been initialized. An object can exist in an uninitialized state. For example int i; create an int object identified by i but its value is indeterminate. In the case of int i[3]; you would have an array of three int each with indeterminate values.
The rules for initialization are very complicated in C++. In the case of Person people[3]; you have an array that is default initialized.
You are initializing an object of type Person[3]. According to default initialization rules :
if T is an array type, every element of the array is default-initialized;
That means each Person gets its own default initialization. To see what happens, consider that T is Person :
if T is a 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;
So each Person's default constructor will be called to initialize that element. You end up with Person people[3]; defining three default constructed Person objects with default initial values.

why is the method of assigning one to another is different from assigning two individual variables in C++?

so in the picture, it says that the problem is with the starting address of the array as we cant change it. but why does this apply only for arrays. int x = 1; we could easily say int y = x; and it would work. doesnt this change the memory address of the variable too?
[tl;dr] The first and sole reason why newValues = oldValues; is illegal ("will not work") is that the C++ standard prohibits it. Array assignment is not defined, supported or allowed in C++, and therefore any such statement is invalid code. Any other attempts to "explain" it, using memory addresses or other speculations, only obfuscates the simple truth that it is the definition of the language that decides what is legal and what is not in that language.
The following are quoted from the posted "textbook" excerpt, which I find to be both wrong and misguided for what looks to be an introduction to C++ basics.
the name of an array without the brackets and subscript stands for the array's starting memory address
Wrong. The name stands for the variable that it denotes, which has array type. While it is true that an array can decay to a pointer ("starting memory address) in certain contexts, it is certainly not true that an array name is the same with its starting address. For example, both sizeof oldValues and typeid(oldValues) are valid expressions, which would mean something very different if replacing oldValues with its memory address.
the statement will not work because you cannot change the starting memory address of an array
The statement "will not work" is correct, but the given reason is still wrong. No assignment changes the address of its left-hand side, it only changes its value. Array assignment does not work because the language does not define it, and for no other reason. Consider for example the following.
int a[4], b[4];
a = b; // error, array assignment not allowed
struct { int n[4]; } c, d;
c = d; // ok, using default copy assignment

Why compilers put zeros into arrays while they do not have to?

I'm trying to understand when compilers should value initialize arrays and when they should default initialize it. I'm trying two options: one raw array, another array aggregated in a struct:
const int N = 1000;
struct A
{
uint32_t arr[N];
A() = default;
};
void print(uint32_t* arr, const std::string& message)
{
std::cout << message << ": " <<
(std::count(arr, arr + N, 0) == N ? "all zeros" : "garbage") << std::endl;
}
int main()
{
uint32_t arrDefault[N];
print(arrDefault, "Automatic array, default initialization");
uint32_t arrValue[N] = {};
print(arrValue, "Automatic array, value initialization");
uint32_t* parrDefault = new uint32_t[N];
print(parrDefault, " Dynamic array, default initialization");
uint32_t* parrValue = new uint32_t[N]();
print(parrValue, " Dynamic array, value initialization");
A structDefault;
print(structDefault.arr, "Automatic struct, default initialization");
A structValue{};
print(structValue.arr, "Automatic struct, value initialization");
A* pstructDefault = new A;
print(pstructDefault->arr, " Dynamic struct, default initialization");
A* psstructValue = new A();
print(psstructValue->arr, " Dynamic struct, value initialization");
}
Here is what I see for clang and VC++:
Automatic array, default initialization: garbage
Automatic array, value initialization: all zeros
Dynamic array, default initialization: garbage
Dynamic array, value initialization: all zeros
Automatic struct, default initialization: all zeros
Automatic struct, value initialization: all zeros
Dynamic struct, default initialization: garbage
Dynamic struct, value initialization: all zeros
Output for gcc is different only in the first line, where it also puts "all zeros".
From my point of view they are all wrong, and what I expect is:
Automatic array, default initialization: garbage
Automatic array, value initialization: all zeros
Dynamic array, default initialization: garbage
Dynamic array, value initialization: all zeros
Automatic struct, default initialization: garbage
Automatic struct, value initialization: garbage
Dynamic struct, default initialization: garbage
Dynamic struct, value initialization: garbage
I.e. output is ok for raw arrays (except for gcc): we have garbage for default and zeros for value. Great. But for a struct I would expect to have garbage all the time. From default initialization:
Default initialization is performed in three situations:
...
...
when a base class or a non-static data member is not mentioned in a constructor initializer list and that constructor is called.
The effects of default initialization are:
if T is a non-POD (until C++11) class type, ...
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.
In my example I have non-static data member that is not mentioned in a constructor initializer list, which is an array of POD type. I expect it to be left with indeterminate values, no matter how my struct is constructed.
My questions are:
Why does compilers violate that? I mean, why they put zeros when they do not have to, wasting my runtime? Am I wrong in my readings?
How can I enforce such behavior to make sure I do not waste my runtime populating arrays with zeros?
Why gcc performs value initialization for an automatic array?
A structValue{}; is aggregate initialization, so 0 are guaranteed.
As A has no user provided constructor because explicitly defaulted constructors do not count as such, the same applies for value initialization as in A* psstructValue = new A();.
For the default initialization cases: Reading uninitialized variables is UB, and Undefined behavior is undefined. The compiler can do with that whatever it wants. Showing you 0 is just as legal as crashing. Maybe there even were 0 in the memory you read by chance. Maybe the compilers felt like 0 initializing. Both equally fine from the standard's point of view.
That being said, you have a better chance of seeing garbage when testing with Release / optimized builds. Debug builds tend to do extra stuff to help diagnosing problems, including doing some extra initialization.
(For the record: gcc and clang with -O3 appear to do no unnecessary initialization on my Linux system at first glance. Nevertheless, I got "all zeroes" for every case. That appears to be by chance.)
The other answer doesn't really address the REASON just kind of dances around with the language specification.
The actual reason is due to how the initialization process works.
Ask yourself the question how do I know if something is initialized.
That is why static data DOES need to be initialized, while data that is not, does not. If you didn't go through first and zero out all of the static data then the static dynamic initialization process (look it up) would be basically impossible.
You would constantly run into issues like two statics that obliquely reference each other in their initialization and everything falls apart.
So without this rule C++ basically is impossible to write a compiler for. Though there's other initialization schemes that don't have this requirement it would require a big overhaul of the language to implement them.

Initialization of a pointer to an array of int

I have been mucking around with C++ once again and noticed a strange behavior regarding the initialization of an array when declared as a pointer inside a class member method or inside the main() function.
int * p = new int[20];
What I would expect to happen is that the pointers will remain uninitialized with random values as they do with
int arr[20];
But instead they are all zeroed. What is going on?
Even though they're zero (this is loosely put, see below), they're not initialized.
Actually, you can't tell they're zero, because if you read the values, you run into undefined behavior. You can't read an un-initialized variable.
To have the array value-initialized, you can do:
int * p = new int[20]();
// ^^
// note parenthesis
but otherwise no, it's not initialized.
p is a pointer to an integer, and it is initialized with the result of a new[] expression. That expression returns the address of the first element of a dynamically allocated array of integers. The array itself is not initialized and contains indeterminate values. If you had said new int[100](), the array would have been zero-initialized instead.

Is setting a pointer to zero equivalent to setting it to NULL?

I searched in the web but couldn't find a reliable answer.
And what would
someclass* ptr = 1;
char* charptr = 2;
or assigning them to any other integer mean?
What happens if I don't initialize pointers (for native data pointers as well as class pointers) before using them?
Setting a pointer to 0 is equivalent to setting it to NULL. However, this is only true for a constant expression 0, i.e. for compile-time zero value. Trying to set a pointer to a run-time zero value is not guaranteed to produce a null pointer
int *pi = 0; // Initializes a null pointer
char *pc = 2 - 2; // Initializes a null pointer
short *ps = sizeof *pc - 1; // Initializes a null pointer
int x = 0;
double *pd = (double *) x;
// Implementation-defined, not guaranteed to produce a null pointer
You can explore the matter in greater detail C FAQ
To answer the second part of your question:
It is illegal to assign any other integer (besides literal/constant 0) to a pointer. Neither of your initializations (or assignments) will compile. It is illegal in both C and C++, although C compilers are historically more permitting in this regard, responding with a warning instead of refusing to compile the code.
Also, in C++ language there's no difference of how pointers to class types are treated. Pointer to class types are still considered scalar types and behave in this regard the same way as any pointer to a fundamental type does.
Yes, in C++ NULL is defined to be 0. Setting a pointer to some other small integer value would mean it pointed to a -- likely illegal -- portion of the computer's memory (but it wouldn't be considered a NULL pointer).
If you don't initialize a global pointer it will be set to zero (NULL) for you just before the program starts. If you don't initialize pointer variables declared on the stack (i.e. as within functions or methods) they will have garbage in them. Likewise, the contents of any dynamically allocated pointer or any contained in an object as a data member also will have no predefined value. Some compilers have extensions that allow requests for dynamically allocated memory to be initially zeroed, like C's calloc() function does for raw memory.