Im trying to understand the delete operator in c++.
I could understand the logic behind using pointers and new operator, yet I understood that "The delete operator eliminates a dynamic variable and returns the memory that the dynamic variable occupied to the freestone." p517, Problem solving with C++ 9th edition.
which, I think doesn't align with the third cout statement. and I expected that the third cout statement to be sth similar to the first one.
int main() {
int *p1;
cout << "p1 address: " << &p1 << " and points to the address "<< p1 << " which has the value: " << *p1<< endl;
p1 = new int;
cout << "p1 address: " << &p1 << " and points to the address "<< p1 << " which has the value: " << *p1<< endl;
delete p1;
cout << "p1 address: " << &p1 << " and points to the address "<< p1 << " which has the value: " << *p1<< endl;
cout << endl;
return 0;
}
I would appreciate any explanation :))
delete doesn't have to change the memory your pointer points to. What it actually does is implementation specific.
What delete needs to do is to deconstruct any object at the given address and return the associated memory to the allocation pool. Some debugger might overwrite the value of a variable when freed, but for trivial types no special deconstruction needs to take place - the memory can be returned to the pool as-is. The pointer isn't changed either: after delete p we call p a dangling pointer that holds an address to freed memory. All access through that pointer is undefined behaviour.
Since handling raw pointers and especially dangling pointers is error prone, it's good to know the C++ smartpointers, e.g. unique_ptr:
std::unique_ptr<int> p; // initialised to nullptr
p = std::make_unique<int>(13); // new int with value 13
p = std::make_unique<int>(37); // deleted previous int, assigned new int with value 37
// optional (when p goes out of scope its pointee is deleted automatically)
p.reset(); // deleted the int and reset p to nullptr
Related
I ask you questions because there is something I don't understand while studying dynamic allocation in C++.
Can I delete "St" and use "ptr" instead if I move the address stored in St to another pointer "ptr" when the dynamically allocated memory is pointed by the St pointer?
If I delete the "St" dynamic allocation, can I move the address to another pointer and delete the "St" immediately because the allocated space does not disappear but disconnects the pointer "St" from the space?
Below is the code I was writing.
Student is a structure.
int main()
{
case 1:
{
Student* ptr = NULL;
Student* St = new Student[10];
ptr = St;
delete[] St;
St = NULL;
break;
}
case 2:`enter code here`
{
printdata(ptr);break;
}
}
Once you've called delete[] on the pointer you get back from new Student[10], the value of that pointer is indeterminate. Both St and ptr are exactly that, but you reassign St. (Since C++11, use nullptr rather than NULL.)
The behaviour of dereferencing the pointer you get back from new Student[10] following the delete[] is undefined.
You can’t use memory you just freed. Memory at that address might still be usable (event till program closes), but there is no guarantee and program might use it for something else.
Can I delete "St" and use "ptr" instead if I move the address stored
in St to another pointer "ptr" when the dynamically allocated memory
is pointed by the St pointer?
If St points toward your array, you can make ptr point toward it as well, but it is not a transfer. What you actually transfer is the adress of the object you are pointing to. The 2 pointers will point toward the same object.
If you delete the object with delete[] (you delete the object, not the pointer), then the 2 pointers will point toward nothing. So what you actually want to do here is make ptr point toward the same object, and then make St point toward null.
Student* ptr = NULL;
Student* St = new Student[10];
ptr = St;
St = NULL;
Edit : If it may help you understand, here is what you can display ...
int x = 4;
int* p = &x;
cout << "x = " << x << " value of x." << endl;
cout << "&x = " << &x << " adress of x." << endl;
cout << "*p = " << *p << " the value of what p points to." << endl;
cout << "p = " << p << " the actual value of p which is the adress of x." << endl;
I thought the following code snippets would cause double free, and the program would core dump. But the truth is that there is no error when I run the code?
Similar problem shows that it caused double free!
My Question is why does there have no error show that there is a double free? And why does there have no core dump?
#include <iostream>
using namespace std;
int main()
{
int *p = new int(5);
cout << "The value that p points to: " << (*p) << endl;
cout << "The address that p points to: " << &(*p) << endl;
delete p;
cout << "The value that p points to: " << (*p) << endl;
cout << "The address that p points to: " << &(*p) << endl;
delete p;
cout << "The value that p points to: " << (*p) << endl;
cout << "The address that p points to: " << &(*p) << endl;
delete p;
}
The program's output when I ran this program is shown as followed:
After modifying the code snippet like the following, the core dump occured:
#include <iostream>
using namespace std;
int main()
{
int *p = new int(5);
for (;;)
{
cout << "The value that p points to: " << (*p) << endl;
cout << "The address that p points to: " << &(*p) << endl;
delete p;
}
return 0;
}
And the program output is :
So there is another question that why this program will core dump every time?
Yes, it is a double free (well, triple, really) which puts it into undefined behaviour territory.
But that's the insidious thing about undefined behaviour, it's not required to crash or complain, it's not required to do anything at all(a). It may even work.
I can envisage an implementation that stores the free state of a block in the control information for it so that freeing it twice would have no effect. However, that would be inefficient, and also wouldn't cover the case where it had been reallocated for another purpose (it would prevent double frees, but not a piece of code freeing the block when some other piece still thinks it still has it).
So, given it's not required to work, you would be well advised to steer clear of it since it may also download maniacal_laughter.ogg and play it while erasing your primary drive.
As an aside, modern C++ has smart pointers that are able to manage their own lifetime, and you would be doing yourself a big favour if you started using those instead of raw pointers.And, although the removal of raw pointer from C++ was a joke, there are some that think it's not such a bad idea :-)
(a) The C++20 standard has this to say when describing undefined behaviour in [defns.undefined] (my emphasis):
Behavior for which this document imposes **NO** requirements.
why does there have no error show that there is a double free? And why does there have no core dump?
delete p;
cout << "The value that p points to: " << (*p) << endl;
The moment you referenced to a deleted pointer is when the program entered an undefined behaviour, and then there is no guarantee that there would be an error or a crash.
It's not entirely the same, but the analogy between memory and a hotel room is applicable, which explains well what an undefined behaviour means. Highly recommended reading:
Can a local variable's memory be accessed outside its scope?
After a pointer is initialized, do you have to use the * dereference operator to call the pointer in a condition?
Example:
int main()
{
int var = 10;
int *ptr = &var;
if(ptr) // does this need to be if(*ptr) ???
{.......}
}
And can I have a short explanation as to why?
Thank you.
if (ptr)
check if the pointer is not Null but
if (*ptr)
check if the value it points to is not zero (in this example is 10)
So for checking the value you shoud add *.
It depends on what you want to do.
if(ptr) checks if the pointer value is nullptr or not. Note that this is shorthand for if(ptr != nullptr).
if(*ptr) checks if what the pointer points to is nullptr (or 0) - and in that case, since you dereference (follow) the pointer to answer the question, the pointer itself had better not be nullptr in that case.
First of all, a pointer is only a variable. However, there are different contexts in which you can use it.
As any other variable you can access the pointers content (which is the adress of the underlying memory) as follows:
int i = 1;
int * p = &i;
std::cout << p << std::endl
this would output the adress of i since this is what is stored in p
If you however want to access the content of the underlying memory (the value of i), you need to dereference the pointer first by using the * operator:
std::cout << *p << std::endl;
This would print the value of iso 1.
of course you can also access the pointer's adress (since the adress of i is a numeric value as well and needs to be stored somewhere too):
std::cout << &p << std::endl;
That would output the adress of p so the adress where the adress of i is stored :)
As a little example try to run this code:
#include <iostream>
int main() {
int i = 1;
int * p = &i;
std::cout << "Value of i(i): " << i << std::endl
<< "Adress of i(&i): " << &i << std::endl
<< "Value of p(p): " << p << std::endl
<< "Dereferenced p(*p): " << *p << std::endl
<< "Adress of p(&p): " << &p << std::endl
<< "Dereferenced adress of p(*(&p)): " << *(&p) << std::endl;
}
#include <iostream>
using namespace std;
int main() {
int* z = new int(9);
cout << "address: " << z << endl;
cout << "value: " << *z << endl;
cout << "referance: " << &z << endl;
return 0;
}
Looking at the cout values, I was expecting the address and reference to give the same address, but heres what the output is:
address: 0x7fc452c032a0
value: 9
referance: 0x7fff5191b8d8
Just curious about the reason for this, is the plain value(z) the address of the variable in the heap with a value of 9, where var(&z) is address of the pointer variable which is located in the stack?
Here is a visualization:
Is the
&z designates the adress of the pointer int * z where you store the allocated adress new int(9).
The pointer z and the value 9 are stored at two different locations in memory.
There is not any notion of reference here, only adresses.
Let me go through some of the basics first.
A variable is a name that is used to refer to some location in the memory, a location that holds a value with which we are working.
Using '&' in C/C++ we can get the address of the variable.
A pointer is a variable that stores the address of a variable. For instance, in the example you are referring to
int* z = new int(9);
variable z stores the address of the value 9 [new int(9)].
Now, finally this variable has to be stored at some location in the memory and this can be accessed using ampersand (&).
&z //gives the address of the pointer to value 9 (address of variable z).
This is the same way the pointers and pointers to a pointer (multi level pointers) works.
int* z;
Above statement implies a pointer variable of int type declaration.
int* z = new int();
Above statement implies an address is allocated to pointer variable of int type dynamically.
int* z = new int(9);
Above statement implies value 9 is stored in a dynamically allocated.
cout << "address: " << z << endl;
Above line of code tells the address of pointer variable z.
cout << "value: " << *z << endl;
Above line of code tells the value stored in the variable z.
cout << "referance: " << &z << endl;
Above line of code tells the dynamically created variable's address.
I just learned the difference between the stack and the heap. After creating a function which will dynamically allocate memory on the heap for me, I return the pointer and display (in and out of the function) the address and value of each pointer.
The values are the same, which I expected, but the addresses to the same chunk of memory on the heap are different, which I did NOT expect.
Why? Shouldn't pHeap2 and pTemp point to the same address?
#include <iostream>
using namespace std;
int* intOnHeap(); // returns an int on the heap
int main()
{
int* pHeap = new int; // new operator allocates memory on the heap and returns its address
// 'new int' allocates enough memory on heap for one int and returns the address on the heap for that chunk of memory
// 'int* pHeap' is a local pointer which points to the newly allocated chunk of memory
*pHeap = 10;
cout << "*pHeap: " << *pHeap << "\n\n";
int* pHeap2 = intOnHeap();
cout << "pHeap2:\n-----------" << endl;
cout << "Address:\t" << &pHeap2 << "\n";
cout << "Value:\t\t" << *pHeap2 << "\n\n";
cout << "Freeing memory pointed to by pHeap.\n\n";
delete pHeap;
cout << "Freeing memory pointed to by pHeap2.\n\n";
delete pHeap2;
// get rid of dangling pointers
pHeap = 0;
pHeap2 = 0;
system("pause");
return 0;
}
int* intOnHeap()
{
int* pTemp = new int(20);
cout << "pTemp:\n-----------" << endl;
cout << "Address:\t" << &pTemp << "\n";
cout << "Value:\t\t" << *pTemp << "\n\n";
return pTemp;
}
Output:
*pHeap: 10
pTemp:
-----------
Address: 0042FBB0
Value: 20
pHeap2:
-----------
Address: 0042FCB4
Value: 20
Freeing memory pointed to by pHeap.
Freeing memory pointed to by pHeap2.
Press any key to continue . . .
You are reporting the address of the pointers, not the address that the pointer is pointing to. Of course the address of the pointer will be different for pTemp and pHeap2; these are different pointers that happen to be pointing to the same address in memory. Remove the & prefixing pTemp and pHeap2 to see the results that you are expecting.
The picture is something like this:
0042FBB0 0042FBC0 0042FCB4
------------ ------ ------------
| 0042FBC0 |------>| 20 |<------| 0042FBC0 |
------------ ------ ------------
pTemp pHeap2
Here you have that &pTemp is 0042FBB0 and &pHeap2 is 0042FCB4. I made up an address for the address that pTemp and pHeap2 are pointing to (of course, the results could vary from run to run anyway) and if you remove the & prefixing pTemp and pHeap2 then you would see 0042FBC0 printed instead in each case.
Yes, pTemp and pHeap2 should be the same. But &pTemp and &pHeap2 are different. This is because the & operator returns a pointer to it's operand. So pTemp is a pointer to an int, while &pTemp is a pointer to a pointer to an int.
&pHeap2 doesn't return the value of the pointer, it returns the address of the pointer. That's because of the &, which in this context means "the address of".
i.e. at 0042FCB4 in memory, there's a pointer pointing to 0042FBB0 (i.e. in a big-endian environment, you'd see 00 42 FB B0 at 0042FCB4).