How does delete[] know it's an array?

132,642

Solution 1

The compiler doesn't know it's an array, it's trusting the programmer. Deleting a pointer to a single int with delete [] would result in undefined behavior. Your second main() example is unsafe, even if it doesn't immediately crash.

The compiler does have to keep track of how many objects need to be deleted somehow. It may do this by over-allocating enough to store the array size. For more details, see the C++ Super FAQ.

Solution 2

One question that the answers given so far don't seem to address: if the runtime libraries (not the OS, really) can keep track of the number of things in the array, then why do we need the delete[] syntax at all? Why can't a single delete form be used to handle all deletes?

The answer to this goes back to C++'s roots as a C-compatible language (which it no longer really strives to be.) Stroustrup's philosophy was that the programmer should not have to pay for any features that they aren't using. If they're not using arrays, then they should not have to carry the cost of object arrays for every allocated chunk of memory.

That is, if your code simply does

Foo* foo = new Foo;

then the memory space that's allocated for foo shouldn't include any extra overhead that would be needed to support arrays of Foo.

Since only array allocations are set up to carry the extra array size information, you then need to tell the runtime libraries to look for that information when you delete the objects. That's why we need to use

delete[] bar;

instead of just

delete bar;

if bar is a pointer to an array.

For most of us (myself included), that fussiness about a few extra bytes of memory seems quaint these days. But there are still some situations where saving a few bytes (from what could be a very high number of memory blocks) can be important.

Solution 3

Yes, the OS keeps some things in the 'background.' For example, if you run

int* num = new int[5];

the OS can allocate 4 extra bytes, store the size of the allocation in the first 4 bytes of the allocated memory and return an offset pointer (ie, it allocates memory spaces 1000 to 1024 but the pointer returned points to 1004, with locations 1000-1003 storing the size of the allocation). Then, when delete is called, it can look at 4 bytes before the pointer passed to it to find the size of the allocation.

I am sure that there are other ways of tracking the size of an allocation, but that's one option.

Solution 4

This is very similar to this question and it has many of the details your are looking for.

But suffice to say, it is not the job of the OS to track any of this. It's actually the runtime libraries or the underlying memory manager that will track the size of the array. This is usually done by allocating extra memory up front and storing the size of the array in that location (most use a head node).

This is viewable on some implementations by executing the following code

int* pArray = new int[5];
int size = *(pArray-1);

Solution 5

delete or delete[] would probably both free the memory allocated (memory pointed), but the big difference is that delete on an array won't call the destructor of each element of the array.

Anyway, mixing new/new[] and delete/delete[] is probably UB.

Share:
132,642
GRB
Author by

GRB

Updated on November 17, 2020

Comments

  • GRB
    GRB over 3 years

    Alright, I think we all agree that what happens with the following code is undefined, depending on what is passed,

    void deleteForMe(int* pointer)
    {
         delete[] pointer;
    }
    

    The pointer could be all sorts of different things, and so performing an unconditional delete[] on it is undefined. However, let's assume that we are indeed passing an array pointer,

    int main()
    {
         int* arr = new int[5];
         deleteForMe(arr);
         return 0;
    }
    

    My question is, in this case where the pointer is an array, who is it that knows this? I mean, from the language/compiler's point of view, it has no idea whether or not arr is an array pointer versus a pointer to a single int. Heck, it doesn't even know whether arr was dynamically created. Yet, if I do the following instead,

    int main()
    {
         int* num = new int(1);
         deleteForMe(num);
         return 0;
    }
    

    The OS is smart enough to only delete one int and not go on some type of 'killing spree' by deleting the rest of the memory beyond that point (contrast that with strlen and a non-\0-terminated string -- it will keep going until it hits 0).

    So whose job is it to remember these things? Does the OS keep some type of record in the background? (I mean, I realise that I started this post by saying that what happens is undefined, but the fact is, the 'killing spree' scenario doesn't happen, so therefore in the practical world someone is remembering.)

  • Steve Jessop
    Steve Jessop about 15 years
    "fussiness about a few extra bytes of memory seems quaint these days". Fortunately, to such people bare arrays are also starting to look quaint, so they can just use a vector or boost::array, and forget about delete[] forever :-)
  • sharptooth
    sharptooth about 15 years
    +1 - valid point in general except that usually the language runtime is responsible for storing this metadata, not the OS.
  • Don Wakefield
    Don Wakefield about 13 years
    I only wish every compiler observed some documented ABI for C++. +1 for the link, which I've visited before. Thanks.
  • Shree
    Shree almost 13 years
    What happens to the size of the array or size of an object that has the array defined? Does it show the additional 4 bytes when you do a sizeof on that object?
  • bsdfish
    bsdfish almost 13 years
    No, sizeof shows just the size of the array. If the runtime chooses to implement it w/ the method I described, that's strictly an implementation detail and from a user's perspective, that should be masked. The memory before the pointer does not 'belong' to the user, and doesn't get counted.
  • bdonlan
    bdonlan almost 13 years
    More importantly, sizeof will not return the true size of a dynamically allocated array in any case. It can only return sizes known at compile time.
  • David Gardner
    David Gardner almost 12 years
    @Rodrigo The link in your comment is broken, but thankfully the wayback machine has a copy of it at replay.web.archive.org/20080703153358/http://taossa.com/…
  • buddy
    buddy almost 12 years
    will this work? In windows & linux we didnt get this working.
  • Sam
    Sam almost 10 years
    Is it possible to use this metadata in a for loop to accurately loop over the array? e.g. for(int i = 0; i < *(arrayPointer - 1); i++){ }
  • Aidiakapi
    Aidiakapi over 9 years
    @Sam While it may work in some environments, it's an implementation detail, and it shouldn't be relied on. I doubt the actual array length will be stored anyways, it's most likely the size of memory block allocated to support the array.
  • Lineesh K Mohan
    Lineesh K Mohan over 7 years
    use delete [] when you new an array type. for example int* a = new int; int* b = new int[5]; delete a; delete[] b;
  • Admin
    Admin almost 7 years
    try size_t size = *(reinterpret_cast<size_t *>(pArray) - 1) instead
  • GntS
    GntS over 6 years
    Clear, short and the most useful answer!
  • Andrew
    Andrew almost 3 years
    I don't think this is a non-issue for standard types, as it's not just about deconstruction but about freeing up the memory. You'd end up with memory leaks for elements 1+.