Will such code cause a memory leak?
unsigned char *ptr = new unsigned char[2];
ptr++; //pointer now points to the second member of array
delete [] ptr;
According to the standard (e.g. this online draft version), the behaviour of delete-ing a pointer, which has not been obtained previously by new, is undefined:
3.7.4.2 Deallocation functions
(3) If a deallocation function terminates by throwing an exception, the
behavior is undefined. The value of the first argument supplied to a
deallocation function may be a null pointer value; if so, and if the
deallocation function is one supplied in the standard library, the
call has no effect. Otherwise, the behavior is undefined if the value
supplied to operator delete(void*) in the standard library is not one
of the values returned by a previous invocation of either operator
new(std::size_t) or operator new(std::size_t, const std::nothrow_t&)
in the standard library, and the behavior is undefined if the value
supplied to operator delete in the standard library is not
one of the values returned by a previous invocation of either operator
new or operator new[](std::size_t, const
std::nothrow_t&) in the standard library.
The value of ptr+1 is not the one returned by new[], and hence the call to delete[] (ptr+1) is UB. Anything can happen, including that it seems to work correctly; But it is for sure not guaranteed to work correctly.
It is undefined behavior as mentioned here
delete [] expression
expression must be a null pointer value or a pointer value previously
obtained by an array form of new-expression. If expression is anything
else, including if it's a pointer obtained by the non-array form of
new-expression, the behavior is undefined.
This makes sense as delete[] would expect to get some information about number of elements to be deleted from the pointer supplied, which it would not found if a different pointer is passed.
Related
It is considered undefined behavior to invoke SL operator delete on a non-null pointer that was not returned by the SL operator new, as described here for (1) and (2):
https://en.cppreference.com/w/cpp/memory/new/operator_delete
The behavior of the standard library implementation of this function is undefined unless ptr is a null pointer or is a pointer previously obtained from the standard library implementation of operator new(size_t) or operator new(size_t, std::nothrow_t).
It is therefore also undefined behavior to mix usages of operator new, operator delete and operator new[], operator delete[]. I can't find anything in the standard stating if this also holds for replacement operator new and operator delete that invoke a user's allocation method. As an example:
void* operator new(std::size_t p_size)
{
void *ptr = UserImplementedAlloc(p_size);
return ptr;
}
void* operator new[](std::size_t p_size)
{
void *ptr = UserImplementedAlloc(p_size);
return ptr;
}
void operator delete(void* p_ptr)
{
UserImplementedFree(p_ptr);
}
void operator delete[](void* p_ptr)
{
UserImplementedFree(p_ptr);
}
Would the following be undefined? Assuming UserImplementedAlloc always returns a correct address and never a nullptr.
struct Simple
{
explicit Simple(); //Allocates m_bytes
~Simple(); //Frees m_bytes
char * m_bytes;
};
/*Placement new is not replaced or overridden for these examples.*/
//Example A
{
//Allocates and invokes constructor
Simple* a = new Simple();
//Invokes destructor
a->~Simple();
//Deallocates
UserImplementedFree(static_cast<void*>(a));
}
//Example B
{
//Allocates
void* addr = UserImplementedAlloc(sizeof(Simple));
//Invokes constructor
Simple* b = new (addr) Simple();
//Invokes destructor and deallocates
delete b;
}
I'm not looking for lectures about if this is bad practice, I am simply trying to determine if this is defined behavior or not.
Your compiler's version of delete might know something, hidden in the implementation, about the deleted pointer that relies on the type.
First calling the destructor manually and then deleting a void* (because otherwise you would call the destructor twice) is not safe. You are not deleting the same pointer in C++ semantics. It is the same address at the assembly level and it might free the same amount of memory - or, do you really know that? You have fooled the compiler to delete a void* instead of the actual type.
Both examples are undefined behavior. Now that I've taken the time to look through the C++17 standard final draft, I've found the evidence I need.
Example A
With regards to operator new:
Allocation functions - § 6.7.4.1.2
If the request succeeds, the value returned shall be a
non-null pointer value (7.11) p0 different from any previously returned value p1, unless that value p1 was
subsequently passed to an operator delete
In example A we call a new-expression, Simple* a = new Simple(), which internally will call the appropriate operator new. We bypass operator delete when we call UserImplementedFree(static_cast<void*>(a)). Even though operator delete would invoke this function, and presumably do the same deallocation, the catch is that any subsequent calls to operator new can now potentially return a pointer that matches the address that a had. And a was never passed to operator delete. So we've broken the rule stated above.
Example B
delete-expression - § 8.3.5.2
...the value of the operand of delete may be a null pointer
value, a pointer to a non-array object created by a previous new-expression, or a pointer to a subobject (4.5)
representing a base class of such an object (Clause 13). If not, the behavior is undefined. In the second
alternative (delete array), the value of the operand of delete may be a null pointer value or a pointer
value that resulted from a previous array new-expression.
83 If not, the behavior is undefined.
In example B, we do not allocate addr through a new-expression. And then we attempt to use a delete-expression to deallocate it. Which violates the rule above.
What would defined behavior look like?
The main feature of these examples is the separation of construction from allocation, and separation of destruction from deallocation. The standard states the following:
new-expression - § 8.3.4.11
For arrays of char, unsigned char,
and std::byte, the difference between the result of the new-expression and the address returned by the
allocation function shall be an integral multiple of the strictest fundamental alignment requirement (6.11) of
any object type whose size is no greater than the size of the array being created. [ Note: Because allocation
functions are assumed to return pointers to storage that is appropriately aligned for objects of any type with
fundamental alignment, this constraint on array allocation overhead permits the common idiom of allocating
character arrays into which objects of other types will later be placed. — end note ]
So defined behavior could potentially look like this:
{
//Allocates bytes
char* bytes = new char[sizeof(Simple)];
//Invokes constructor
Simple* a = new ((void *)bytes) Simple();
//Invokes destructor
a->~Simple();
//Deallocates
delete[] bytes;
}
Once again, not necessarily good practice, but defined behaviour.
The question is as above; if I have, for example, int* ptr = new int[10] and after some operations with data ptr is set to, say, 2nd, 3rd 5th or last element, will the call delete[] ptr be the correct one? Can't find it in the manual.
No, the argument passed to the delete[] operator must point to the same address that was returned by new[]
(Assuming you haven't overloaded delete[] and new[] to do something crazy)
From the C++ reference manual delete[]: Notice that an invalid value of ptr causes undefined behaviour.
Because of this it is not recommended to manipulate the pointer to your allocated memory directly!
Can't find it in the manual
C++11 3.7.4.2/3 says
behavior is undefined if the value supplied to operator delete[](void*) in the standard library is not one of the values returned by a previous invocation of either operator new[](std::size_t) or operator new[](std::size_t, const std::nothrow_t&) in the standard library
I got the the following code:
T *p = new T[10];
delete p;
It should be delete [] p, I think. I tried it on T=int type, no error. Then I created a simple class, exception. Any clear explanation?
T *p = 0;
delete p;
What's wrong with them? [I looked it up, it seems that although delete p will lead to error, but program resume because compiler thinks p==0 in the first place, so this error does not matter.]
Yes,
You should call delete [] p;
Any clear explanation?
It is undefined behavior to:
Call delete if allocation was through new []
Call delete on non dynamically allocated pointer.
Note that Undefined Behavior means that anything can happen, it does not mandate a crash. It simply means your program can show any behavior(including working as expected).
It is perfectly valid to call delete on a NULL pointer. The standard allows that. The delete operator internally takes core of that, the caller does not have to bother about the NULL check.
References:
C++03 standard §3.7.4.2-3:
If a deallocation function terminates by throwing an exception, the behavior is undefined. The value of the first argument supplied to a deallocation function may be a null pointer value; if so, and if the deallocation function is one supplied in the standard library, the call has no effect. Otherwise, the value supplied
to operator delete(void*) in the standard library shall be one of the values returned by a previous invocation of either operator new(std::size_t) or operator new(std::size_t, const std::nothrow_-t&) in the standard library, and the value supplied to operator delete[](void*) in the standard library shall be one of the values returned by a previous invocation of either operator new[](std::size_t) or
operator new[](std::size_t, const std::nothrow_t&) in the standard library.
When you allocate an array of objects with new[], you must delete it with delete[]. Failing to do so results in Undefined Behavior.
The main behavioral difference between delete and delete[] is that the latter, apart from deallocating the memory, also invokes the destructor of each object in the array.
Considering your second point:
T *p = 0;
delete p;
This will not cause an error, but rather do nothing, because that's the expected behavior of delete when the pointer is null.
The code:
int *ptr = new int[10];
int *q = ptr;
delete q;
works fine without any issues (no run-time error).
However, the following code:
int *ptr = new int[10];
int *q = ptr;
q++;
delete q;
results in run-time error.
I am using Microsoft Visual Studio-8 and Win-7 as platform.
I am not able to figure out why there is a run-time error in the second case?
Your code is causing an Undefined Behavior. An Undefined Behavior means anything can happen, the behavior cannot be defined. The program works just by pure luck its behavior cannot be explained.
Basically,
If you are allocating dynamic memory with new you MUST use delete to deallocate it.
If you are allocating dynamic memory with new[] you MUST use delete[] to deallocate it.
It is undefined behavior to pass any address to delete which was not returned by new.
Here is the quote from the Standard.
As per C++03 Standard § 3.7.4.2-3:
If a deallocation function terminates by throwing an exception, the behavior is undefined. The value of the first argument supplied to a deallocation function may be a null pointer value; if so, and if the deallocation function is one supplied in the standard library, the call has no effect. Otherwise, the value supplied
to operator delete(void*) in the standard library shall be one of the values returned by a previous invocation of either operator new(std::size_t) or operator new(std::size_t, const std::nothrow_-t&) in the standard library, and the value supplied to operator delete[](void*) in the standard library shall be one of the values returned by a previous invocation of either operator new[](std::size_t) or
operator new[](std::size_t, const std::nothrow_t&) in the standard library.
In C++ it is better to use RAII(SBRM) by using Smart pointers instead of raw pointers, which automatically take care of the memory deallocations.
There are two errors here:
You can't delete a pointer that wasn't returned by new.
You can't delete a pointer that was returned by new[] — you need to use delete[].
So, the first snippet works only by coincidence, and there is no error only because you're using a primitive type. If it were UDT, the destructors wouldn't run.
Also, you should be using containers and smart pointers, instead of raw new and delete.
Because you have to pass the unaltered result of new to delete. That is just how things work, i.e. the API contract of new/delete.
Because you changed what address q points at, and then tried to delete it.
You should only ever try to delete whatever new returns to you. Anything else is undefined behavior.
I knew that when we allocate memory by using new/new[], then we should release the memory by using the delete/delete[] respectively.
Here is the question,
Can I use delete[] to release the memory allocated by new?
For example,
int *pInt3 = new int;
delete [] pInt3;
pInt3 = NULL;
T *pT3 = new T;
delete [] pT3;
pT3 = NULL;
Thank you
No, you should match the non-array form of new with non-array delete, and likewise for the array forms.
See the C++ FAQ Lite section on Freestore Management
This is especially good: http://www.parashift.com/c%2B%2B-faq-lite/freestore-mgmt.html#faq-16.12
[16.12] What if I forget the [] when deleteing array allocated via new T[n]?
All life comes to a catastrophic end.
It is the programmer's —not the compiler's— responsibility to get the connection between new T[n] and delete[] p correct. If you get it wrong, neither a compile-time nor a run-time error message will be generated by the compiler. Heap corruption is a likely result. Or worse. Your program will probably die.
I guess that you are not satisfied with anything but a reference to the standard, so here it is.
You can find it under 3.7.3.2 in the Standard for C++03.
3 The value of the first argument supplied to one of the deallocation functions provided in the standard library may be a null pointer value; if so, the call to the deallocation function has no effect. Otherwise, the value supplied to operator delete(void*) in the standard library shall be one of the values returned by a previous invocation of either operator new(size_t) or operator new(size_t, const std::nothrow_t&) in the standard library, and the value supplied to operator delete[] (void*) in the standard library shall be one of the values returned by a previous invocation of either operator new[] (size_t) or operator new[] (size_t, const std::nothrow_t&) in the standard library.
So the answer to your question is no.
Can I use delete[] to release the
memory allocated by new?
Yes you could, but you will be sorry if you do.