I am relatively new to programming so this may well sound like a stupid question to you seasoned pros out there. Here goes:
In C++, when I use the delete operator on arrays, I have noticed that the data contained in the released memory locations is preserved. For example:
int* testArray=new int[5];
testArray[3]=24;
cout<<testArray[3]; //prints 24
delete [] testArray;
cout<<testArray[3]; // still prints 24
Subsequently, am I right in assuming that since testArray[3] still prints 42 , the data in the deleted memory location is still preserved? If so, does this notion hold true for other languages, and is there any particular reason for this?
Shouldn't "freed" memory locations have null data, or is "free memory" just a synonym for memory that can be used by other applications, irrespective of whether the locations contain data or not?
I've noticed this is not the case when it comes to non array types such as int, double etc. Dereferencing and outputting the deleted variable prints 0 rather than the data. I also have a sneaking suspicion that I might be using wrong syntax to delete testArray, which will probably make this question all the more stupid. I'd love to hear your thoughts nonetheless.
Once you deallocate the memory by calling delete and try to access the memory at that address again it is an Undefined Behavior.
The Standard does not mandate the compilers to do anything special in this regard. It does not ask the compilers to mark the de-allocated memory with 0 or some special magic numbers.It is left out as the implementation detail for the compilers. Some compiler implementations do mark such memory with some special magic numbers but it is left up to each compiler implementation.
In your case, the data, still exists at the deallocated addresses because perhaps there is no other memory requirement which needed that memory to be re-utilized and the compiler didn't clear the contents of previous allocation(since it is not needed to).
However, You should not rely on this at all as this might not be the case always. It still is and will be an Undefined Behavior.
EDIT: To answer the Q in comment.
The delete operator does not return any value so you cannot check the return status however the Standard guarantees that the delete operator will sucessfully do it's job.
Relevant quote from the C++03 Standard:
Section ยง3.7.3.2.4:
If the argument given to a deallocation function in the standard library is a pointer that is not the null pointer value (4.10), the deallocation function shall deallocate the storage referenced by the pointer, render-ing invalid all pointers referring to any part of the deallocated storage.
The data is still there, because when you free, it frees it in the allocation table -- the system would be very slow if it had to zero over all the memory each time free() or delete is called.
This is the same in any language.
I think the non-array types were set to zero because they were in fact statically allocated rather than dynamically allocated.
non-POD data will be altered in a destructor (which might appear as being null-ed in a debugger).
Freed data is just usable, indeed.
You can NOT depend on the data being unaltered after delete. On a related note, debugging malloc's or runtime libraries will frequently reset the data to a specific signature (0xdeadbeef, 0xdcdcdcdc etc) so you can easily spot accesses to deleted memory in a debugger.
Related
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.
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.
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.
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.
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...
Considering the following:
tbModelHFrame = new TbModelHeaderFrame(this, storage->getDataBase());
I guess the correct way to delete tbModelHFrame memory will be
delete tbModelHFrame;
Right?
How do I check that the memory was really released?
How do I check that the memory was really released?
You don't.
C++ has no means of telling whether a pointer points to a valid object or a random region in memory. The latter includes a region that was valid at some point, but has been deleted since.
It is up to the developer to organize their code in a way that this cannot happen.
The only guarantee that the language gives you to help you out here, is that a delete call never fails. So if you call delete once on the object, you can be reasonably sure that the object destroyed properly and the memory was released. Just don't attempt to access it again afterwards, or you'll be in trouble.
Yes, what is allocated with new should be freed with delete.
A way to check if every dinamically allocated memory has been freed is to use Valgrind's Memcheck
Anyway, it is usually safer to use smart pointers (See here).
According to the delete operator reference:
[..] In all cases, if ptr is a null pointer, the standard library
deallocation functions do nothing.
If the pointer passed to the
standard library deallocation function was not obtained from the
corresponding standard library allocation function, the behavior is
undefined.
After the standard library deallocation function returns,
all pointers referring to any part of the deallocated storage become
invalid.
Any use of a pointer that became invalid in this manner, even
copying the pointer value into another variable, is undefined
behavior. (until C++14)
Indirection through a pointer that became
invalid in this manner and passing it to a deallocation function
(double-delete) is undefined behavior. Any other use is
implementation-defined.
Thus, in case of a problems of deleting the pointer, it is undefined behavior.
The premise of the question is wrong: if delete doesn't release the memory, your heap is corrupted and your application can already do anything, including formatting your hard drive. So you've got bigger problems than a mere delete being a no-op if it comes to that. So, don't worry about it. As long as haven't messed up your heap due to memory errors in your own code, you'll be fine.
In any case, you should not use naked pointers as owning pointers. This is C++, not C.
Use a smart pointer:
QScopedPointer<TbModelHeaderFrame> tbModelHFrame(
new TbModelHeaderFrame(this, storage->getDataBase())
);
...
tbModelHFrame->something(); // do something with it
And that's it. The memory will be released when the pointer goes out of scope. You don't have to worry about it.
The pointer can also be a class member:
class Foo {
QScopedPointer<TbModelHeaderFrame> m_modelHFrame;
...
};
Foo::Foo() :
m_modelHFrame(new TbModelHeaderFrame(this, storage->getDataBase())) {
...
}
or
Foo::Foo() : ... {
m_modelHFrame.reset(new TbModelHeaderFrame(this, storage->getDataBase()));
...
}
Modern C++ code should be designed to not to use manual memory management except where absolutely necessary for well understood reasons. In most cases, naked pointers and manual memory management in modern C++ are a sign of bad design, not necessity.
TL;DR: Modern C++/Qt code can and should read a bit like Python :)
I would like to know what happens to memory that is destroyed by "delete" operator in C++.
Is 'destroying' memory in such way means setting given pieces of memory to 0 or something else?
It is destroying (as in calling the relevant destructor for) the object instance passed to delete, and then "frees" the memory so that it can be used for other purposes.
The C++ standard states nothing about what the contents of the memory will be after delete, and it is certainly not guaranteed to be zero or any other value - nor is it guaranteed that it is NOT zero - it may be zerod, it may retain all the values it had before, or some parts of it may be altered and others remain the same.
The goal of C and C++ as languages is to "only do the minimum necessary", so a typical memory free will not overwrite the "old" memory.
You could of course use code in the destructor to set the memory to zero before it is freed.
Since you are not supposed to use memory after it has been deleted, it shouldn't really matter.
delete just releases the memory (previously allocated by new) and in case that some object has been stored within this memory, the destructor is also invoked.
delete doesn't change the value of the pointer and neither it modifies the memory that has been released, thus you'll notice that many people are used to assign NULL to this pointer after calling delete just to make sure they will not end up with dereferencing invalid (dangling) pointer, which produces undefined behavior.
Worth to have a look at: Is it good practice to NULL a pointer after deleting it?
No, it does not mean setting the memory to any particular value+. The memory simply gets back into the heap of values that can be reused. The runtime often use several bytes of the returned chunk to store "bookkeeping" information, but they do not set the entire chunk to a particular value. Once a memory chunk is reused, it is your program that sets its new values.
+ There are memory profiling tools that let you set released memory to some "junk" values to make sure that you get a crash faster.
I am learning about pointers in C++ currently, in college. I have coded a program that is a binary tree of objects that points to a linked list of sub-objects. IF I am even wording that correctly. Anyways, my program seems to work correctly, but I am having trouble wrapping my head around how to test pointer deletion.
For instance, my delete function for single object of the binary tree is:
void EmployeeRecord::destroyCustomerList()
{
if(m_oCustomerList != NULL)
{
delete m_oCustomerList;
m_oCustomerList = NULL;
}
}
When printing my tree, everything populates and is taken off correctly (meaning the tree is kept intact through every removal of a node)...but how do I confirm what happens to the deallocated memory? I know that since I am setting the pointer *m_oCustomerList to NULL, that I can test for a NULL value on a previously populated object, but what happens to the actual memory?
I am using Visual Studio/C++ and have read that the debugger will use a code starting at 0xCC for deallocated memory...but I can't seem to figure out how to use that information.
Note that your code
void EmployeeRecord::destroyCustomerList()
{
if(m_oCustomerList != NULL)
{
delete m_oCustomerList;
m_oCustomerList = NULL;
}
}
Simplifies to:
void EmployeeRecord::destroyCustomerList()
{
delete m_oCustomerList;
m_oCustomerList = NULL;
}
It is safe to invoke the delete operator on a null pointer in C++. It does nothing. In other words, the check for null is already "built in".
Once you delete an object, it no longer exists, and the pointer to that object becomes and indeterminate value (so it's not a bad idea to null out all copies of that pointer).
What really happens to the memory in actual C++ implementations, rather than in the abstract sense, is that it continues to exist at the same address, but is marked as free, so that it can be allocated for another purpose. An allocation request coming from the program (possibly a completely unrelated module) or possibly from another program in the system, could obtain that memory for its own use.
Any uses of a pointer to an object which no longer exists are "undefined behavior". Functions for safely verifying such a pointer do exist, but they are very platform-specific and rarely perfect.
The problem is that whereas it is not particularly hard for an implementation to confirm that a pointer is bad, it is not possible to confirm that a pointer is good. We can walk the internal memory data structures of the memory allocator to determine that some pointer refers to free storage. But what if the storage is subsequently allocated? Then the pointer no longer refers to free storage. But it does not refer to the original object which was allocated, either! This is known as an "ABA ambiguity": because some A changed into a B, but then back into A, indistinguishable from the original A.
Approaches exist to solve the ABA ambiguity (if not completely than at least partially). For instance, pointers be made "fat" so the they have an extra field in addition to the address bits. The field could contain a sequence number which is used to stamp the pointer that are returned from the allocator. Now when an object is deleted and reallocated, the new pointer to the same location has a different sequence number: we have ABA'. The pointer A has gone bad, making it B, but the when it is resurrected it comes back as A'. If we ask the system to validate A, it will correctly determine that A is bad, because it does not have the expected sequence number. The correct, valid pointer to the object is A', which does not match A.
However, sequence number fields are only so many bits wide and they will wrap around eventually. So the ABA problem has not really been solved. The validation of good versus bad pointers has only been made substantially more reliable. To absolutely deal with the ABA problem, the system must always hand out new pointers which are not equal to any pointers which could still be in use. This means never actually freeing anything (thereby running out of memory) or implementing garbage collection. (Meaning that delete actually does nothing: deleted objects are destructed, but stick around in memory until they are garbage-collected, which happens when the program no longer remembers any copies of the pointer. At that point, the program no longer remembers A, and so A can be re-introduced, and there is no ABA problem.)
To make all pointers "fat", you have to change the entire toolchain and runtime: compilers, libraries, et cetera. There are further difficulties because large programs tend to have multiple memory allocators. If you ask the wrong allocator "is this pointer valid", all it can say is "this pointer is not from my arena". Another approach you can do is to invent your own pointers and implement them as smart pointers in C++. Your pointers can support an is_valid method which tries to be as reliable as possible (dealing with the ABA problem somehow: either partially with some sequence numbers and such, or by implementing your own garbage collection scheme.)
Accessing deleted memory is undefined behaviour by the standard. For instance, if this was a multithreaded application (or some other process had injected a thread into your application) then a new allocation could allocate the memory you just deallocated before you are able to "verify" it.
Once you delete your memory and set your pointer to NULL you no longer have access to that memory even if you want it. So, there is no way to verify that it really gone. However, if you did something wrong and the memory was never deleted it would consist of a memory leak which would cause your program to increase the amount of ram it uses, you could see this as a symptom of a pointer not properly disposed of.
You will probably learn later that you will not have to worry about the deletion of your pointers because of std::shared_ptr which will delete your object when the pointer goes out of scope. Which will be safer later on because you will probably will learn that exceptions can cause your destructor to never fire leaving a memory leak.
...
...
delete m_oCustomerList;
// Try using the deleted pointer here
// This should cause a runtime exception
// which means you did free the pointer
m_oCustomerList->someStrMemberVariable = "This will fail"
...
...
Needless to say, don't do this in the actual code. Hope this helps.
I know free() won't call the destructor, but what else will this cause besides that the member variable won't be destructed properly?
Also, what if we delete a pointer that is allocated by malloc?
It is implementation defined whether new uses malloc under the hood. Mixing new with free and malloc with delete could cause a catastrophic failure at runtime if the code was ported to a new machine, a new compiler, or even a new version of the same compiler.
I know free() won't call the destructor
And that is reason enough not to do it.
In addition, there's no requirement for a C++ implementation to even use the same memory areas for malloc and new so it may be that you're trying to free memory from a totally different arena, something which will almost certainly be fatal.
Many points:
It's undefined behaviour, and hence inherently risky and subject to change or breakage at any time and for no reason at all.
(As you know) delete calls the destructor and free doesn't... you may have some POD type and not care, but it's easy for someone else to add say a string to that type without realising there are weird limitations on its content.
If you malloc and forget to use placement new to construct an object in it, then invoke a member function as if the object existed (including delete which calls the destructor), the member function may attempt operations using pointers with garbage values
new and malloc may get memory from different heaps.
Even if new calls malloc to get its memory, there may not be a 1:1 correspondence between the new/delete and underlying malloc/free behaviour.
e.g. new may have extra logic such as small-object optimisations that have proven beneficial to typical C++ programs but harmful to typical C programs.
Someone may overload new, or link in a debug version of malloc/realloc/free, either of which could break if you're not using the functions properly.
Tools like ValGrind, Purify and Insure won't be able to differentiate between the deliberately dubious and the accidentally.
In the case of arrays, delete[] invokes all the destructors and free() won't, but also the heap memory typically has a counter of the array size (for 32-bit VC++2005 Release builds for example, the array size is in the 4 bytes immediately before the pointer value visibly returned by new[]. This extra value may or may not be be there for POD types (not for VC++2005), but if it is free() certainly won't expect it. Not all heap implementations allow you to free a pointer that's been shifted from the value returned by malloc().
An important difference is that new and delete also call the constructor and destructor of the object. Thus, you may get unexpected behavior. That is the most important thing i think.
Because it might not be the same allocator, which could lead to weird, unpredictable behaviour. Plus, you shouldn't be using malloc/free at all, and avoid using new/delete where it's not necessary.
It totally depends on the implementation -- it's possible to write an implementation where this actually works fine. But there's no guarantee that the pool of memory new allocates from is the same pool that free() wants to return the memory to. Imagine that both malloc() and new use a few bytes of extra memory at the beginning of each allocated block to specify how large the block is. Further, imagine that malloc() and new use different formats for this info -- for example, malloc() uses the number of bytes, but new uses the number of 4-byte long words (just an example). Now, if you allocate with malloc() and free with delete, the info delete expects won't be valid, and you'll end up with a corrupted heap.