1

Suppose I have allocated some memory for storing an int value like this:

int *p=new int; 

Here I created the required memory using new operator and assigned the address of that memory block so that I can access that memory block.

Now it's my control to what I store in that memory block.

But when I write a statement like this:

delete p;

we say that I have deleted the dynamically allocated memory.

But if I really delete'd or freed up that memory, should I not be able to access that memory anymore after the delete operation? But I was able to access that memory block using the same pointer variable. So what was the purpose of deleting the memory block if we can access the block after deleting it?

Here's some sample code:

#include <iostream>
using namespace std;

int main(void)
{
    int *p;
    p=new int;
    *p=10;
    cout << *p << endl;
    delete p;

    //here look we can still access that block of memory using the same pointer variable
    //But we did not created the  memory block after deletetion
    cout << *p << endl;
    *p=20;
    cout << *p << endl;

    return 0;
}

What does the saying "deleted/ free up the dynamically allocated memory" means in this context?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    Undefined behavior is undefined behavior. – L. F. Jul 22 '19 at 10:28
  • See the FAQ: https://stackoverflow.com/q/2397984 – L. F. Jul 22 '19 at 10:29
  • 1
    Pointer of type int is declared, then this pointer is made to point to newly allocated location in memory having integer. this memory location which contains the integer was allocated dynamically as 'new' keyword used. 'delete p' statement doesn't delete the pointer itself BUT frees up the memory allocated by 'new' . Now the pointer p still exists but now its not guaranteed that it still points to same memory location. It might point to the same address OR not hence undefined behavior. pointer p is now a dangling pointer. Best practice after deleting the pointer assign it to nullptr. – dorKKnight Jul 22 '19 at 10:50
  • Why do we say you can't grab the ball and carry it in basketball? I did it and it worked – StoryTeller - Unslander Monica Jul 22 '19 at 11:11
  • @StoryTeller Not sure that analogy works - because you've no chance of breaking the other players noses; or giving the ball a small puncture that you'll realise has been causing issues only an hour later when you think it's really important – UKMonkey Jul 22 '19 at 11:20
  • @UKMonkey - UB boogieman aside, it's a proper analogy. There's a rule, if you break it you may not be caught... or you may be removed from the game entire. – StoryTeller - Unslander Monica Jul 22 '19 at 11:52
  • @L.F. is correct. And understanding `new` and `delete` is important. That said, you shouldn't be calling `new`/`delete` by hand pretty much ever. The C++17 way to write that is: `std::unique_ptr p; p = std::make_unique(); *p = 10; << *p << endl; p.reset();` (Now `p` is `nullptr`.) `cout << *p << endl; /* crash */ *p=20; /* crash */ cout << *p << endl; /* crash */`. (Strictly speaking, dereferencing `nullptr` is undefined behavior, but I've never seen it not crash.) – Ben Jul 23 '19 at 13:39

5 Answers5

6

The other answers saying that dereferencing a pointer after delete is undefined behavior are correct. But I think it's useful to get some understanding of what's going on instead of simply saying "anything can happen".

When you delete a pointer, several steps are taken by the program:

  1. Calling the destructor of the object referred to by the pointer
  2. Calling memory manager's function which will do whatever bookkeeping it needs to free the memory.

The second step, depending on implementation, may actually return the memory block to the OS, or it may simply mark that the block is available for subsequent allocation, or do something else. In your case, apparently, the block is simply marked as free but not returned to the OS. A further allocation may return an address within this block, and then the undefined behavior will likely change from "working fine" to "working strange".

But even if you still can access the object through the pointer you've deleted, the compiler can still assume that the object no longer exists and do some optimizations which rely on this. This may break your program in very unexpected and seemingly illogical ways, so you'd better keep away from undefined behavior.

Ruslan
  • 18,162
  • 8
  • 67
  • 136
  • 1
    _A further allocation may return an address within this block_. This is the most important point to remember. :) – Fareanor Jul 22 '19 at 10:58
1

When you allocated that memory for your pointer, that location in memory became yours, that is, other applications cannot override it. Now, when you freed that location up, it became usable for other applications, so your current code no longer has exclusivity, your meaningful data became random data from other app's perspective. It is quite logical that the actual value is not changed, since that would involve some extra labor from your app's perspective on a memory chunk it is not caring about. As about the address being reachable, basically you have freed that memory, so other apps, including this one will reach it, so it is not surprising at all that you can reach it.

Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
  • 2
    This explanation would only make sense in an OS without memory protection — e.g. DOS, where your app is e.g. a TSR, and "other applications" is any subsequently run application. – Ruslan Jul 22 '19 at 10:25
  • @Ruslan we are talking about undefined behavior. That address is still usable, even though the app no longer has exclusive rights on it. – Lajos Arpad Jul 22 '19 at 10:30
  • 1
    Yes, I mean that your explanation about "other applications can use this address to which you still have access after `delete`" doesn't apply to modern OSes, which 1) don't usually share heaps, and 2) make same virtual addresses refer to different physical addresses. – Ruslan Jul 22 '19 at 10:34
  • When I freed up the memory, if in the mean time another program allocates that memory can i still be able to access the memory block from my program. Will it then throw an error or will be undefined behavior –  Jul 22 '19 at 10:50
  • @S.M.TusharIbneSalam I'm not 100% sure about that, but according to my opinion that should mean that your current app will not have access to that location. – Lajos Arpad Jul 22 '19 at 12:09
0

But if we really delete or freed up that memory should not we unable to access that memory after delete operation.

No that's wrong.

Deleting the pointer frees the memory it's pointing to. That is the purpose of deleting. It's your responsibility not to access deleted memory, not the compilers. If your program does access deleted memory then according to the C++ standard it's behaviour is undefined, which means exactly what it says.

john
  • 85,011
  • 4
  • 57
  • 81
0

After you do delete p, you access *p. delete p ends the lifetime of the object, and you access an object after its lifetime has ended. This is undefined behavior. When your program contains undefined behavior, all bets are off — there is no guarantee what the program will do. "Working fine" is one example of "no guarantee". Nasal demons are another example. In fact, one version of GCC (1.17) tried to start the games NetHack, Rogue, and Towers of Hanoi when encountering certain kinds of undefined behavior. [1] So you can't count on it.

To quote the FAQ:

Undefined behavior is one of those aspects of the C and C++ language that can be surprising to programmers coming from other languages (other languages try to hide it better). Basically, it is possible to write C++ programs that do not behave in a predictable way, even though many C++ compilers will not report any errors in the program!

[...]

This is exactly the problem with undefined behavior. Basically, the standard allows anything to happen once you invoke undefined behavior (even nasal demons). If there is a "correct" behavior according to your mental model of the language, that model is simply wrong; The C++ standard has the only vote, period.

L. F.
  • 19,445
  • 8
  • 48
  • 82
0

Just after calling delete, your program writes a few characters on standard output. Possibly, just possibly, in order to perform this write() operation, the C++ I/O system had to allocate a few bytes, so it asked the C++ memory management system for a few bytes. Bad luck, the memory system gave to the I/O system precisely this little area that had just become available thanks to your delete operation.

The I/O system, acting fully within its rights, stores an useful pointer to some auxiliary structure into that little place. Then, by storing 20 into the location it had just "deleted", your program wrecks that useful pointer. After that point, the I/O system data structures are corrupt.

Note that there is nothing the hardware or operating system can do to protect the memory location from misuse. This is because memory write permissions are per process not per function, and the C++ I/O system is part of the same process as your main() function.

If, at a later point in time, the I/O system starts to delete or change files without notice, you may not complain to your C++ compiler vendor, because you wrote into memory that did not belong to you any more.

If your programming staff is prone to this sort of mistake, you have to insist that they write things like: "delete p ; p = nullptr;". That way, the crash caused by a subsequent misuse of the pointer occurs immediately and is very easy to debug, unlike a (possibly much further down the road) crash caused by a latent corruption of the I/O system data structures.

But the spirit of modern C++ is to replace "raw pointers", that is the sort of pointer you're using here, by objects called "smart pointers". So you might have to get acquainted with the std::shared_ptr and std::unique_ptr classes. Here is a small sample, where you can see the numerical value of the pointer has been reset to NULL:

#include  <memory>
#include  <iostream>


int main(void)
{
    std::unique_ptr<int> uPtr = std::make_unique<int>(0);
    *uPtr = 10;

    std::cout << *uPtr << std::endl;
    uPtr.reset();

    auto ptrValue = reinterpret_cast<unsigned long>(uPtr.get());

    std::cout << "uPtr is: " << ptrValue << std::endl;

    std::cout << "So far so good ... " << std::endl;

    // here, the program will crash :

    *uPtr = 20;
    std::cout << *uPtr << std::endl;

    return EXIT_SUCCESS;
}

If you allow me a lame attempt at programmer humour: after the main function has written 20, the status of your program can be described as "so far so good". I don't know whether you are familiar with the financial services industry.

There is a classic joke about a legendary Wall Street trader who did a number of very bad deals with subprime financial instruments. So the trader decides to jump dive into the street below from the 94th floor of the building. Reaching the level of the 5th floor, he sees a secretary, who asks him: "How is it going ?". And the trader replies: "So far so good.".

jpmarinier
  • 4,427
  • 1
  • 10
  • 23