1

I understand pointer allocation of memory fully, but deallocation of memory only on a higher level. What I'm most curious about is how C++ keeps track of what memory has already been deallocated?

int* ptr = new int;
cout << ptr;
delete ptr;
cout << ptr;
// still pointing to the same place however it knows you can't access it or delete it again
*ptr // BAD
delete ptr // BAD

How does C++ know I deallocated that memory. If it just turns it to arbitrary garbage binary numbers, wouldn't I just be reading in that garbage number when I dereference the pointer?

Instead, of course, c++ knows that these are segfaults somehow.

trincot
  • 317,000
  • 35
  • 244
  • 286
Joshua Segal
  • 541
  • 1
  • 7
  • 19
  • C++ doesn't specify that, it's all implementation defined. You'll have to ask about a particular platform, but then that's not really the same question anymore, is it? – Etienne de Martel Feb 14 '20 at 15:29
  • 1
    Perhaps you're looking for a more detailed answer than this, but C++ does nothing with deallocated memory; it is simply returned to the operating system as free space. It might remain in the same state as you left it for a long time, or it might be overwritten instantly by another process. C++ doesn't know you've caused a segfault, that's the OS detecting it. – Reticulated Spline Feb 14 '20 at 15:29
  • The language doesn't keep track of memory allocations. The Operating System does. – Jesper Juhl Feb 14 '20 at 15:31
  • C++ doesn't know. It would make things easier if it did. It isn't going out of it's way to crash, that's a consequence of the underlying machinery for managing memory. In fact, the `*ptr` and `delete ptr;` labeled `// BAD` might not even crash, anything could happen. – François Andrieux Feb 14 '20 at 15:31
  • 1
    All that, including segfaults are OS level. – Michael Chourdakis Feb 14 '20 at 15:32
  • [Related question](https://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope). The answer has a very helpful analogy for trying to use or inspect freed memory. – François Andrieux Feb 14 '20 at 15:45
  • You may want to read up on [Undefined Behaviour](https://en.cppreference.com/w/cpp/language/ub). – Jesper Juhl Feb 14 '20 at 16:24
  • Probably you should watch this https://youtu.be/LIb3L4vKZ7U – Marek R Feb 14 '20 at 16:30
  • "Instead, of course, c++ knows that these are segfaults somehow" - that's not C++ doing/knowing *anything*, that's purely the OS doing its thing. – Jesper Juhl Feb 14 '20 at 16:53
  • It is the task of the heap manager to track those deallocated memory, and that heap manager is a part of the language runtime environment. – muaz Feb 14 '20 at 21:22

4 Answers4

5

C++ does not track memory for you. It doesn't know, it doesn't care. It is up to you: the programmer. (De)allocation is a request to the underlying OS. Or more precisely it is a call to libc++ (or possibly some other lib) which may or may not access the OS, that is an implementation detail. Either way the OS (or some other library) tracks what parts of memory are available to you.

When you try to access a memory that the OS did not assigned to you, then the OS will issue segfault (technically it is raised by the CPU, assuming it supports memory protection, it's a bit complicated). And this is a good situation. That way the OS tells you: hey, you have a bug in your code. Note that the OS doesn't care whether you use C++, C, Rust or anything else. From the OS' perspective everything is a machine code.

However what is worse is that even after delete the memory may still be owned by your process (remember those libs that track memory?). So accessing such pointer is an undefined behaviour, anything can happen, including correct execution of the code (that's why it is often hard to find such bugs).

If it just turns it to arbitrary garbage binary numbers, wouldn't I just be reading in that garbage number when I dereference the pointer?

Who says it turns into garbage? What really happens to the underlying memory (whether the OS reclaims it, or it is filled with zeros or some garbage, or maybe nothing) is none of your concern. Everything you need to know is that after delete it is no longer safe to use the pointer. Even (or especially) when it looks ok.

freakish
  • 54,167
  • 9
  • 132
  • 169
  • 1
    "Everything you need to know is that after delete it is no longer safe to use the pointer." <-- *That*! – Jesper Juhl Feb 14 '20 at 15:52
  • I actually am interested in understanding this on more than just a C++ level. So when you delete a pointer the OS marks that piece of memory as "available" space, and then when something tries to directly access that piece of memory the OS fires a segfault? – Joshua Segal Feb 14 '20 at 16:57
  • @JoshuaSegal it's a bit simplified (the marking does not always happen), but I suppose you can think about it that way. The things that happen nowadays between your code, the standard lib, OS and CPU is an insanely complex magic. So I suggest you stick to simple rules: `new` gives you memory from some place (whatever that place is), `delete` gives it back. – freakish Feb 14 '20 at 16:59
  • @JoshuaSegal Realistically, your C++ library marks that piece of memory as available space. It doesn't tell the OS that you've stopped using it. It does remember that the next time you ask for some memory, it can give you back the memory that you just marked as available. – user253751 Feb 14 '20 at 17:54
  • A segfault is raised by the CPU when your program attempts to access memory outside it's (virtual) address space. That's all the CPU and OS cares about. How that translates to addresses used by your C++ runtime is an entirely different thing. – Jesper Juhl Feb 14 '20 at 17:58
5

How does C++ know I deallocated that memory.

When you use a delete expression, "C++ knows" that you deallocated that memory.

If it just turns it to arbitrary garbage binary numbers

C++ doesn't "turn [deallocated memory] to arbitrary garbage binary numbers". C++ merely makes the memory available for other allocations. Changing the state of that memory may be a side effect of some other part of the program using that memory - which it is now free to do.

wouldn't I just be reading in that garbage number when I dereference the pointer?

When you indirect through the pointer, the behaviour of the program is undefined.

Instead, of course, c++ knows that these are segfaults somehow.

This is where your operating system helpfully stepped in. You did something that did not make sense, and the operating system killed the misbehaving process. This is one of the many things that may but might not happen when the behaviour of the program is undefined.

eerorika
  • 232,697
  • 12
  • 197
  • 326
1

I take it that you wonder what delete actually does. Here it is:

  • First of all, it destructs the object. If the object has a destructor, it is called, and does whatever it is programmed to do.

  • delete then proceeds to deallocate the memory itself. This means that the deallocator function (::operator delete() in most cases in C++) typically takes the memory object, and adds it to its own, internal data structures. I.e. it makes sure that the next call to ::operator new() can find the deallocated memory slab. The next new might then reuse that memory slab for other purposes.

The entire management of memory happens by using data structures that you do not see, or need to know that they exist. How an implementation of ::operator new() and ::operator delete() organizes its internal data is strictly and fully up to the implementation. It doesn't concern you.

What concerns you is, that the language standard defines that any access to a memory object is undefined behavior after you have passed it to the delete operator. Undefined behavior does not mean that the memory needs to vanish magically, or that it becomes inaccessible, or that it is filled with garbage. Usually none of these happens immediately, because making the memory inaccessible or filling it with garbage would require explicit action from the CPU, so implementations don't generally touch what's written in the memory. You are just forbidden to make further accesses, because it's now up to system to use the memory for any other purpose it likes.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
0

C++ still has a strong C inheritance when it comes to memory addressing. And C was invented to build an OS (first version of Unix) where it makes sense to use well known register addresses or to whatever low level operation. That means that when you address memory through a pointer, you as the programmer are supposed to know what lies there and the language just trusts you.

On common implementations, the language requests chunks of memory from the OS for new dynamic objects, and keeps track of used and unused memory block. The goal is to re-use free blocks for new dynamic objects instead of asking the OS for each and every allocation and de-allocation.

Still for common implementation, nothing changes in a freshly allocated or deallocated block, but the pointers maintaining a list of free blocks. AFAIK few return memory to the OS until the end of the process. But a free block could be later re-used, that is the reason why when a careless programmer tries to access a block of memory containing pointers that has been re-used, SEGFAULT is not far, because the program could try to use arbitrary memory addresses that could not be mapped for the process.


BTW, the only point required by the standard is that accessing an object past its end of life, specifically here using the pointer after the delete statement invokes Undefined Behaviour. Said differently anything can happen from an immediate crash to normal results, passing through later crash or abnormal result in unrelated places of the program...

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252