Why can freed dynamically allocated memory still be accessed after a delete operation in C++?

偶尔善良 提交于 2019-12-26 06:49:13

问题


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?


回答1:


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.




回答2:


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.




回答3:


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.




回答4:


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.




回答5:


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.".



来源:https://stackoverflow.com/questions/57143681/why-can-freed-dynamically-allocated-memory-still-be-accessed-after-a-delete-oper

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!