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.
Related
For example, can you explain what would happen in the following code?
class Vector{
int v[3];
Vector(int *x);//parameterized constructor created
};
Vector::Vector(int *x)//definition of the parameterized constructor
{
for (int i=0;i<size;i++)
v[i]=x[i];//what happens here?? why did we take pointer as arguement?
}
From my understanding, by putting v[i]=x[i] we created a new array in which all elements of v are now in x. Why did this require a pointer argument? Couldn't it have been done with a reference &?
This goes back to older style C habits, when you can use a pointer as an array, by "indexing" it's elements.
Taken from: https://en.wikibooks.org/wiki/C_Programming/Pointers_and_arrays
A variable declared as an array of some type acts as a pointer to that type. When used by itself, it points to the first element of the array.
A pointer can be indexed like an array name.
However, a few notes:
v[i]=x[i] we created a new array
No, you did not create a new array here, the array was already created when the body of the constructor got executed. What happens here is that TO the value v[i] we will assign the value of: *(x + i) ie. the ith. element from the address x points to. Unless you know how x was created and initialized this is pretty dangerous code. Just imagine you can call this method with the address of a single int. I suppose, size is 3 or less, otherwise this code has serious security issues.
You always should check for null pointers, before trying to access the value they point to.
You can pass in the array by reference if you know the size of x at compile time:
Vector(int (&x)[3]);
If you don't know the size at compile time then what you're doing goes from being unsafe code, to blatantly wrong code.
Another option is to use std::array if you know the size at compile time, and std::vector if you don't.
Just to add a bit to previous answers, the indexing operator [] actually dereferences a pointer and shifts it by index*sizeof(type) at the same time. The same relates to declaration of an array. Say, if you declare int a[1]; this means that a is now a pointer to int, i.e. int*. So if you wanted to pass it to a function as an argument, you would need to specify its type as int*.
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.
I read Are negative array indexes allowed in C? and found it interesting that negative values can be used for the index of an array. I tried it again with the c++11 unique_ptr and it works there as well! Of course the deleter must be replaced with something which can delete the original array. Here is what it looks like:
#include <iostream>
#include <memory>
int main()
{
const int min = -23; // the smaller valid index
const int max = -21; // the highest valid index
const auto deleter = [min](char* p)
{
delete [](p+min);
};
std::unique_ptr<char[],decltype(deleter)> up(new char[max-min+1] - min, deleter);
// this works as expected
up[-23] = 'h'; up[-22] = 'i'; up[-21] = 0;
std::cout << (up.get()-23) << '\n'; // outputs:hi
}
I'm wondering if there is a very, very small chance that there is a memory leak. The address of the memory created on the heap (new char[max-min+1]) could overflow when adding 23 to it and become a null pointer. Subtracting 23 still yields the array's original address, but the unique_ptr may recognize it as a null pointer. The unique_ptr may not delete it because it's null.
So, is there a chance that the previous code will leak memory or does the smart pointer behave in a way which makes it safe?
Note: I wouldn't actually use this in actual code; I'm just interested in how it would behave.
Edit: icepack brings up an interesting point, namely that there are only two valid pointer values that are allowed in pointer arithmetic:
§5.7 [expr.add] p5
If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.
As such, the new char[N] - min of your code already invokes UB.
Now, on most implementations, this will not cause problems. The destructor of std::unique_ptr, however, will (pre-edit answer from here on out):
§20.7.1.2.2 [unique.ptr.single.dtor] p2
Effects: If get() == nullptr there are no effects. Otherwise get_deleter()(get()).
So yes, there is a chance that you will leak memory here if it indeed maps to whatever value represents the null pointer value (most likely 0, but not necessarily). And yes, I know this is the one for single objects, but the array one behaves exactly the same:
§20.7.1.3 [unique.ptr.runtime] p2
Descriptions are provided below only for member functions that have behavior different from the primary template.
And there is no description for the destructor.
new char[max-min+1] doesn't allocate memory on the stack but rather on heap - that's how standard operator new behaves. The expression max-min+1 is evaluated by the compiler and results in 3, so eventually this expression is equal to allocating 3 bytes on the heap. No problem here.
However, subtracting min results in pointer which is 23 bytes beyond the beginning of the allocated memory returned by new and since in new you allocated only 3 bytes, this will definitely point to a location not owned by you --> anything following will result in undefined behavior.
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.
I know this might be a common question but I have tried to search but still cannot find a clear answer.
I have the following code:
int* f() {
int a[] = {1,2,3};
return a;
}
int main() {
int a[] = f(); // Error here
getch();
return 0;
}
This code produces the error message: "Cannot convert from 'int *' to 'int []'"
I found this quite strange because I have read that pointer and array are similar. For example, we can use a[i] instead of *(a + i).
Can anyone give me a clear explanation, please?
There are actually two errors in this code.
Firstly, you are returning the address of a temporary (the int array within f), so its contents are undefined after the function returns. Any attempt to access the memory pointed to by the returned pointer will cause undefined behaviour.
Secondly, there is no implicit conversion from pointers to array types in C++. They are similar, but not identical. Arrays can decay to pointers, but it doesn't work the other way round as information is lost on the way - a pointer just represents a memory address, while an array represents the address of a continuous region, typically with a particular size. Also you can't assign to arrays.
For example, we can use a[i] instead of *(a + i)
This, however, has little to do with the differences between arrays and pointers, it's just a syntactic rule for pointer types. As arrays decay to pointers, it works for arrays as well.
The type int[] doesn't actually exist.
When you define and initialize an array like
int a[] = {1,2,3};
the compiler counts the elements in the initializer and creates an array of the right size; in that case, it magically becomes:
int a[3] = {1,2,3};
int[] used as a parameter to a function, instead, it's just plain int *, i.e. a pointer to the first element of the array. No other information is carried with it, in particular nothing about the size is preserved. The same holds when you return a pointer
Notice that an array is not a pointer: a pointer can be changed to point to other stuff, while an array refers always to the same memory; a pointer does not know anything about how big is the space of memory it points to, while the size of an array is always known at compile time. The confusion arises from the fact that an array decays to a pointer to its first element in many circumstances, and passing it to a function/returning it from a function are some of these circumstances.
So, why doesn't your code work? There are two big errors:
You are trying to initialize an array with a pointer. We said that an int * doesn't carry any information about the size of the array. It's just a pointer to the first element. So the compiler cannot know how big a should be made to accomodate the stuff returned by f().
In f you are returning a pointer to a variable that is local to that function. This is wrong, because a pointer does not actually store the data, it only points to where the data is stored, i.e. in your case to the a local to f. Because that array is local to the function, it ceases to exist when the function exits (i.e. at the return).
This means that the pointer you are returning points to stuff that does not exist anymore; consider the code:
int * a = f();
This initialization works, and you can try to use a later in the function, but a will be pointing to the no-longer existent array of f; in the best case your program will crash (and you'll notice immediately that you've done something wrong), in the worst it will seem to work for some time, and then start giving strange results.
int * and int [] are similar but different.
int * is a real pointer, meanwhile int[] is an array reference ( a sort of "constant pointer" to the begin of the data) wich cannot be modified. So, a int * can be threated like a int [] but not viceversa.
You can use a[b] and*(a+b) interchangeably because that is exactly how a[b] is defined when one of a or b is a pointer and the other is of integer or enumeration type.
Note: This also means that expressions like 42[a] are perfectly legal. Human readers might object strongly, but the compiler won't bat an eye at this.