Why is the allocated memory still usable after a delete expression?
Asked Answered
R

9

22

I'm playing a little with memory dynamic allocation, but I don't get a point. When allocating some memory with the new statement, I'm supposed to be able to destroy the memory the pointer points to using delete.

But when I try, this delete command doesn't seem to work since the space the pointer is pointing at doesn't seem to have been emptied.

Let's take this truly basic piece of code as an example:

#include <iostream>  

using namespace std;

int main()  
{  
    //I create a pointer-to-integer pTest, make it point to some new space,  
    // and fulfill this free space with a number;  
    int* pTest;  
    pTest = new int;  
    *(pTest) = 3;  
    cout << *(pTest) << endl; 

    // things are working well so far. Let's destroy this
    // dynamically allocated space!
    delete pTest;

    //OK, now I guess the data pTest pointed to has been destroyed 
    cout << *(pTest) << endl; // Oh... Well, I was mistaking.  

    return 0;  
}  

Any clue ?

Rookie answered 19/7, 2010 at 10:55 Comment(4)
Yes, a noob quieston, but not that a trivial one.Devilish
"Destruction" as defined by the Standard is concerned with when objects become invalid to use, after running any cleanup code the class author wrote. It does not refer to scrubbing the memory formerly occupied by the object, which the class author/user did not request and so would be a waste of time.Worker
If you want to destroy memory, use a screwdriver to lever the chips off the motherboard. All that delete will do is allow that memory to be allocated to something else. It may or may not change the values stored in the actual bits. But reading those bits using a pointer to the deleted object causes undefined behaviorSuperstition
If you want to erase memory, for example password data that you already used, overwrite it a couple of times with random patterns before deleting. This prevents the data 'leaking' into free memory, ready to be scraped by other software. I bet there are libraries out there. I found some useful info with "c++ erase password memory". Also, stackoverflow.com/questions/5698002Pegasus
O
62

It's time to learn what undefined behavior is. :)

In C++, when you do something illegal/nonsensical/bad/etc. the standard often says that "it leads to undefined behavior." This means that from that point forward, the state of your program is completely non-guaranteed, and anything could happen.

At the point where you do your last *(pTest), you get undefined behavior. This is because pTest does not point to a valid object, and dereferencing such a pointer is undefined. So what you're seeing is totally allowed: undefined output.

All you've done is said "I'm finished with this allocation." Once you've said that, you shouldn't (and indeed, cannot) inspect or care about that memory any longer. It doesn't even make conceptual sense to deallocate something then try to use it; you've said you were done!

Your output is somewhat predictable though: likely, your OS simply says "okay, thanks for the memory" and that's it. It has no reason to actually "reset" the memory, or do anything special. That would indeed be a waste of time, when nobody (including your own program) is not using it.

But remember, this output is completely undefined. Don't try to use objects that don't exist. Perhaps a better test would have been:

#include <iostream>

struct foo
{
    ~foo()
    {
        std::cout << "foo is gone :(" << std::endl;
    }
};

int main(void)
{
    foo* f = new foo();
    delete f; // you'll see that the object is destroyed.
}

Although it seems you were looking to see what happens with the memory itself. Just remember that it makes no sense to get rid of memory then try to use it, so the answer is: who knows. It's up to your specific platform, which C++ doesn't care about.

Oskar answered 19/7, 2010 at 11:2 Comment(4)
The ugly thing about undefined behavior is that it most of the time looks good.Wolfram
Note that some implementations will alter the contents of the memory, to help in debugging. For example, you might see byte patterns like 0xDEADBEEF, 0xFEEEFEEE, or 0xDDDDDDDD depending on how the memory was freed.Superstition
"from that point forward" not just forward from the undefined behaviour, backward along all paths that end up at the undefined behaviourFaille
Note that the statement "It doesn't even make conceptual sense to deallocate something then try to use it" is technically wrong. It makes sense in the area of cybersecurity, because the so-called "use-after-free bug" is a popular entry point for malware attacks. The malicious party will find it makes very much sense indeed.Pegasus
I
11

Calling delete will mark the memory area as free. It won't necessary reset its old value.

You are advised to set your pointer to 0, after calling delete:

delete pTest;
pTest = 0;
Illuminance answered 19/7, 2010 at 11:1 Comment(2)
I would instead advise to not use the variable at all after calling delete (...unless, of course, it is re-assigned to something else).Besiege
A better solution is to make sure you variable goes out of scope soon after deletion and thus can not be re-used.Tupiguarani
P
6

The answer is performance.

It's a great debug aid to fill all freed memory with an invalid value (0xCCCCCCCC, 0xDEADDEAD, etc.) to catch attempts to use stale pointers to already-freed memory.

But modifying a freed memory costs CPU time, so for performance reasons, the OS will just add the freed memory block to its "free" list, and leave the contents intact.

Phony answered 19/7, 2010 at 11:27 Comment(0)
P
5

delete operator calls the destructor of the object and deallocates the memory previously allocated to the object. It doesn't affect the pointer variable that points to the deleted object.

So when dereferencing a pointer pointing to a destroyed object, you'll get trouble.

Purlieu answered 19/7, 2010 at 10:57 Comment(1)
I think the question is really why the memory pointed-to still has the same value as it did before the object was destroyed, not why the pointer still points to such.Worker
S
4

Dereferencing a pointer that points to deallocated memory is undefined behavior.

Many times it will just work, because the memory provided by new is usually part of a bigger chunk of allocated memory that the allocator manages. When you call delete, it will call the relevant destructors and mark the memory as free, which usually means "ready for reuse". So, looking in that memory you'll find the same data that was there before the call to delete, or some other data if that chunk of memory has been reassigned after a new call.

Note that nothing forbids that the new/delete allocator works as a thin wrapper around the OS virtual memory functions, so when all the allocated blocks relative to a page has been deallocated, the whole page is freed and any attempt to access it results in an address violation.

TL,DR version: don't deference pointers which point to deallocated memory: it may work sometimes, sometimes will give you back garbage, sometimes it will trigger an access violation.

A good way to notice immediately if you're doing this kind of mistake is to set your pointers to NULL after deleting the memory they point to: if your code tries to dereference a NULL pointer, on almost any system this will make the application crash, so faults like these won't go unnoticed.

Swedish answered 19/7, 2010 at 11:7 Comment(0)
M
1

It could have referred to any piece of mapped memory. Or maybe unmapped memory, depending upon how long your program has been executing, details of memory allocations, and if the libraries are returning memory to the OS later on...

If delete actually cleared all the memory that is being deleted, programs would spend a significantly longer time running, because they'd waste a lot of time scrubbing memory that will probably be overwritten sooner or later anyway. It might be good for debugging, but in production use, there's just not much call for actually scrubbing memory contents. (Crypto keys are a good exception, of course; scrubbing those before calling delete or free is a good idea.)

Mansour answered 19/7, 2010 at 11:4 Comment(0)
P
1

What would destroying the data mean? I suppose it could zero it out, but why bother? It's assumed dirty when we get it from the environment, so why clean it before we return it? We don't care what's in it, because we are relinquishing our right to read it. And as to why delete doesn't zero out the pointer itself:

http://www2.research.att.com/~bs/bs_faq2.html#delete-zero

Pandorapandour answered 19/7, 2010 at 11:5 Comment(0)
B
1

Just a simple example to illustrate what might happen, and what the undefined behaviour that some people mentioned means.

If we add two extra lines of code before the print:

delete pTest;

int *foo = new int;
*foo = 42;

cout << *pTest << endl;

The printed value of pTest could very well be 3, as it was in your case. However, the printed value could also be 42. As the pTest pointer was deleted, its memory was freed. Because of this, it is possible that the foo pointer will point to the same location in memory that pTest used to point to before it was deleted.

Bascule answered 19/7, 2010 at 11:25 Comment(0)
N
0

Calling delete for a pointer marks the memory block pointed by it to 'free memory'. It doesn't destroy the items in the memory right away. The 'items' get destroyed only after the process has returned given that 'delete' was used for the pointer pointing to that memory block. In case 'delete' isn't used, it stays there forever resulting in a memory leak.

To solve your problem, just:

delete pTest;
pTest=NULL;
Nance answered 20/4, 2021 at 12:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.