Rich
Rich

Reputation: 1851

Does delete (non array form) know the total amount of memory allocated by either new or new[]

This question was asked as part of Does delete[] deallocate memory in one shot after invoking destructors? but moved out as a separate question.

It seems (Correct me if wrong) that the only difference between delete and delete[] is that delete[] will get the array size information and invoke destructors on all of them, while delete will destruct the only first one. In particular, delete also has access to the info on how much total memory is allocated by new[].

If one doesn't care about destructing the dynamically allocated array elements, and only care that the memory allocated either by new or new[] be deallocated, delete seems to be able to do the same job.

This How does delete[] "know" the size of the operand array? question's accepted answer has one comment from @AnT and I quote

Also note that the array element counter is only needed for types with non-trivial destructor. For types with trivial destructor the counter is not stored by new[] and, of course, not retrieved by delete[]

This comment suggests that in general delete expression knows the amount of the entire memory allocated and therefore knows how much memory to deallocate in one shot in the end, even if the memory hold an array of elements. So if one writes

auto pi = new int[10];
...
delete pi;

Even though the standard deems this as UB, on most implementations, this should not leak memory (albeit it is not portable), right?

Upvotes: 2

Views: 231

Answers (3)

zwol
zwol

Reputation: 140688

A concrete reason to avoid all constructs provoking undefined behavior, even if you cannot see how they could possibly go wrong, is that the compiler is entitled to assume that undefined behavior never happens. For instance, given this program...

#include <iostream>
#include <cstring>
int main(int argc, char **argv)
{
    if (argc > 0) {
        size_t *x = new size_t[argc];
        for (int i = 0; i < argc; i++)
            x[i] = std::strlen(argv[i]);
        std::cout << x[0] << '\n';
        delete x;
    }
    return 0;
}

... the compiler might emit the same machine code as it would for ...

int main(void) { return 0; }

... because the undefined behavior on the argc > 0 control path means the compiler may assume that path is never taken.

Upvotes: 0

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275600

Under the C++ standard, calling delete on something allocated with new[] is simply undefined behavior, as is calling delete[] on something allocated with new.

In practice, new[] will allocate the memory through something like malloc as will new. delete will destroy the pointed-to object, then send the memory to something like free. delete[] will destroy all of the objects in the array, then send the memory to something like free. Some extra memory may be allocated by new[] to pass to delete[] to give delete[] the number of elements to be destroyed, or not.

If actual malloc/free is used, then some implementations will allow a pointer to anywhere in the malloc'd block to be used. Others won't. The exact same value is required to be passed to free as you got from malloc for this to be defined. There is an issue here in that if new[] malloced some extra room for the array size/element stride and stuck it before the block, then delete is passed the pointer-to-the-first element, then delete will pass free a different pointer than new[] got from malloc. (I think there is an architecture where something like this happens.)

Like most undefined behavior, you can no longer rely on auditing the code you write, but instead you are now committed to auditing both the produced assembly, and the C/C++ standard libraries you interact with, before you can determine if the behavior you want to do is correct. In practice, that is a burden that will not be fulfilled, so your code ends up having negative value, even if you check that things work the way you expect the one time you actually checked. How will you ensure that an identical check (of the resulting binary and its behavior) will occur every time the compiler version, standard library version, OS version, system libraries, or compiler is changed?

Upvotes: 3

SergeyA
SergeyA

Reputation: 62583

This is correct. Difference between delete and delete[] is that the latter knows the number of items allocated in the array and calls destructor on every object on them. To be 100% correct, both actually 'know' it - the number of items allocated for an array is equal to the allocated memory size (which both know) divided by the size of the object.

One might ask, why do we need delete[] and delete than - why can't delete perform the same calculations? The answer is polymorphism. The size of the allocated memory will not be equal to the sizeof static objec when deletion is done through the pointer to the base class.

On the other hand, delete[] does not take into account a possibility of object being polymorphed, and this is why dynamic arrays should never be treated as polymorphic objects (i.e. allocated and stored as a pointer to the base class).

As for leaking memory, delete will not leak memory in case of POD types when used on arrays.

Upvotes: 0

Related Questions