3

In "The C++ Programming Language" book Stroustrup says:

"To deallocate space allocated by new, delete and delete[] must be able to determine the size of the object allocated. This implies that an object allocated using the standard implementation of new will occupy slightly more space than a static object. Typically, one word is used to hold the object’s size.

That means every object allocated by new has its size located somewhere in the heap. Is the location known and if it is how can I access it?

johnsyweb
  • 136,902
  • 23
  • 188
  • 247
slaviber
  • 358
  • 2
  • 10

3 Answers3

7

In actual fact, the typical implementation of the memory allocators store some other information too.

There is no standard way to access this information, in fact there is nothing in the standard saying WHAT information is stored either (the size in bytes, number of elements and their size, a pointer to the last element, etc).

Edit: If you have the base-address of the object and the correct type, I suspect the size of the allocation could be relatively easily found (not necessarily "at no cost at all"). However, there are several problems:

  1. It assumes you have the original pointer.
  2. It assumes the memory is allocated exactly with that runtime library's allocation code.
  3. It assumes the allocator doesn't "round" the allocation address in some way.

To illustrate how this could go wrong, let's say we do this:

size_t get_len_array(int *mem)
{
   return allcoated_length(mem);
}

... 
void func()
{
    int *p = new int[100];
    cout << get_len_array(p); 
    delete [] p;
}

void func2()
{
    int buf[100];
    cout << get_len_array(buf); // Ouch!
}
Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • Is it true that *any* implementation can technically supply a `len()` function, at no cost at all? – Elazar Sep 02 '13 at 18:12
  • @Elazar: See my edit above. No, it's not entirely trivial do this. – Mats Petersson Sep 02 '13 at 18:22
  • This is the same problem as with `delete` or `free()`. – Elazar Sep 02 '13 at 18:28
  • In general, it looks as if a-pointer-to-the-heap should be subtype of a-pointer-to-the-stack. – Elazar Sep 02 '13 at 18:28
  • @Elazar not sure what that means "subtype of"... In general, code shouldn't have to care where a pointer comes from. If you need "pointers" that know how large the thing is they are pointing at, then use `vector` or some such. – Mats Petersson Sep 02 '13 at 18:30
  • `subtype of` means that I should be able to ask for a "pointer" or for a "pointer to the heap". Substitutability. The rule you are stating is a result of the fact that there is no such distinction in the language. – Elazar Sep 02 '13 at 18:32
  • No, and I don't see why there should be a differentiation. It would just make the language more complicated, and you'd have to write TWO functions that do essentially the same thing. – Mats Petersson Sep 02 '13 at 18:34
  • There will be only one function. If this thing is common to heap and stack pointers, it will get a pointer. Otherwise the operations in the functions are applicable only to heap pointers anyway (e.g. `delete`, `len()`. When I come to think about it, C has this already in `static` parameters). About the complication issue - you are right. – Elazar Sep 02 '13 at 18:38
  • But that would mean that `int values[3];` would have to havbe a bunch of extra values stored on the stack, because the compiler can't know that you aren't going to pass it to `len`. According to the principle in C++ of "only pay for what you actually use", that wouldn't work. – Mats Petersson Sep 02 '13 at 18:45
  • The compiler will know: `values` is an array-on-the-stack - decays into a normal pointer, on which a call to `len` will not compile anywhere... But I got the idea. Thank you, and +1 :) – Elazar Sep 02 '13 at 18:51
  • That would still mean that the compiler has to have some extra information to know the difference between different types of pointers... And what if `get_len_array()` is in a different source file? – Mats Petersson Sep 02 '13 at 18:53
  • I just wanted to ask/comment. My understanding, in reality, the size of array only stored in the block before the actual array only for `new[]/delete[]`, not for `malloc/free`, not for static arrays. This is because it's C++ feature of `delete[]` to have to call destructors without compiler knowing when this might happen. For stack compiler knows when it's gonna happen and how many destructors to call. `free` doesn't call destructors at all. All heap functions (essentially, `malloc/free` for both C/C++), though, use heap memory management via manager to allocate/free blocks. Am I correct? – lapk Sep 02 '13 at 18:54
  • P.S. `new/delete` (non-array) don't need this extra block either, because only one destructor to call. – lapk Sep 02 '13 at 18:55
  • @PetrBudnik: that is correct. In practice, there is nearly always a length (or something that can be used to find the length, e.g. a pointer to the end) in most memory allocators anyway. Whether `delete []` uses that or it's own information is another matter. – Mats Petersson Sep 02 '13 at 18:58
  • @MatsPetersson Thanks, Mats. +1 for confirming this extra bit. – lapk Sep 02 '13 at 18:58
1

That means every object allocated by new has its size located somewhere in the heap. Is the location known and if it is how can I access it?

Not really, that is not needed for all cases. To simplify the reasoning, there are two levels at which the sizes could be needed. At the language level, the compiler needs to know what to destroy. At the allocator level, the allocator needs to know how to release the memory given only a pointer.

At the language level, only the array versions new[] and delete[] need to handle any size. When you allocate with new, you get a pointer with the type of the object, and that type has a given size.

To destroy the object the size is not needed. When you delete, either the pointer is to the correct type, or the static type of the pointer is a base and the destructor is virtual. All other cases are undefined behavior, and thus can be ignored (anything can happen). If it is the correct type, then the size is known. If it is a base with a virtual destructor, the dynamic dispatch will find the final overrider, and at that point the type is known.

There could be different strategies to manage this, the one used in the Itanium C++ ABI (used by multiple compilers in multiple platforms, although not Visual Studio) for example generates up to 3 different destructors per type, one of them being a version that takes care of releasing the memory, so although delete ptr is defined in terms of calling the appropriate destructor and then releasing the memory, in this particular ABI delete ptr call a special destructor that both destroys and releases the memory.

When you use new[] the type of the pointer is the same regardless of the number of elements in the dynamic array, so the type cannot be used to retrieve that information back. A common implementation is allocating an extra integral value and storing the size there, followed by the real objects, then returning a pointer to the first object. delete[] would then move the received pointer one integer back, read the number of elements, call the destructor for all of them and then release the memory (pointer retrieved by the allocator, not the pointer given to the program). This is really only needed if the type has a non-trivial destructor, if the type has a trivial destructor, the implementation does not need to store the size and you can avoid storing that number.

Out of the language level, the real memory allocator (think of malloc) needs to know how much memory was allocated so that the same amount can be released. In some cases that can be done by attaching the metadata to the memory buffer in the same way that new[] stores the size of the array, by acquiring a larger block, storing the metadata there and returning a pointer beyond it. The deallocator would then undo the transformation to get to the metadata.

This is, on the other hand, not always needed. A common implementation for allocators of small size is to allocate pages of memory to form pools from which the small allocations are then obtained. To make this efficient, the allocator considers only a few different sizes, and allocations that don't fit one of the sizes exactly are bumped to the next size. If you request, for example, 65 bytes, the allocator might actually give you 128 bytes (assuming pools of 64 and 128 bytes). Thus given one of the larger blocks managed by the allocator, all pointers that were allocated from it have the same size. The allocator can then find the block from which pointer was allocated and infer the size from it.

Of course, this is all implementation details that are not accessible to the C++ program in a standard portable way, and the exact implementation can differ not just based on the program, but also de execution environment. If you are interested in knowing how the information is really kept in your environment, you might be able to find the information, but I would think twice before trying to use it for anything other than learning purposes.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
0

Your are not deleting a object directly, instead you send a pointer to delete operator. Reference C++
You use delete by following it with a pointer to a block of memory originally allocated with new:

int * ps = new int; // allocate memory with new
           . . .  // use the memory
delete ps;          // free memory with delete when done

This removes the memory to which ps points; it doesn’t remove the pointer ps itself. You can reuse ps, for example, to point to another new allocation

user2713461
  • 387
  • 2
  • 5
  • 16