2

I have read here How does delete[] "know" the size of the operand array? that before the allocated memory it is saved how much memory it was allocated. I allocated an array of integers, set each element to value 15 and inspected the heap. First I allocated an array with 2 elements:

x/8xw 134524932
0x804b004:    0x00000011    0x0000000f    0x0000000f    0x00000000
0x804b014:    0x00020ff1    0x00000000    0x00000000    0x00000000

and another with 4 elements:

x/8xw 134524932
0x804b004:    0x00000019    0x0000000f    0x0000000f    0x0000000f
0x804b014:    0x0000000f    0x00000000    0x00020fe9    0x00000000

There are several things I do not understand:
1) How the values 0x00000011 and 0x00000019 define the size of the allocation?
2) Is there space for an extra array element allocated?
3) Are the values 0x00020ff1 and 0x00020fe9 at the of the allocated memory related to the allocation?

I am using gcc on a 32bit Ubuntu.

Community
  • 1
  • 1
robert
  • 3,539
  • 3
  • 35
  • 56
  • How exactly did you _inspect_ the heap? Usually there's some bookkeeping in the allocation mechanism used, which blocks were allocated, and what size they are. It's not necessarily exactly the size requested. – πάντα ῥεῖ Dec 20 '14 at 16:26
  • @πάντα ῥεῖ: With gdb. – robert Dec 20 '14 at 16:27
  • 1
    `new[]` doesn't save how much memory was allocated anymore than `malloc()` does -- that is, the book-keeping is an opaque implementation detail. What `new[]` must do, though, is to allow `delete[]` to produce the effect of calling the correct number of destructors, which may require additional memory. – Kerrek SB Dec 20 '14 at 16:36
  • @franz1 And you inspected what actually? The memory that the result of `new[]` points to? As mentioned, bookkeeping is done somewhere else, and the size of the allocated memory blocks may differ from the aquired size. – πάντα ῥεῖ Dec 20 '14 at 16:36
  • First, I'm interpreting your question to be a question about C++ *implementation*. If so, I suggest that you reword your question to make that clear. Otherwise, what Kerrek said is the correct answer. That said, typically the number of elements is stored before the actual beginning of the array. So you need to look there. Also, rather than using gdb to inspect, it's probably easier to just print it out directly using some pointer arithmetic. Of course, all this is completely platform dependent and undefined behavior in the standard. – kec Dec 20 '14 at 16:45
  • Allocate a few more arrays, with e.g. 100, 200, 300 and 400 elements. Examine the space just before the first element. You will see a pattern. – n. m. could be an AI Dec 20 '14 at 17:16

1 Answers1

2

The number of elements is typically not stored for primitives, because there is no need to call destructors. Here's an example compiled on 64-bit Linux with g++. Note that most new implementations are layered on malloc(). Typically, malloc() also stores some metadata. You'd typically see that before the metadata stored by C++.

One might ask why C++ needs to store the number of elements if malloc() is also storing size information. The rationale is that coupling your C++ compiler/ABI to the underlying memory allocation implementation is a bad idea from an engineering standpoint.

Of course, this is all platform dependent, and getting at this information invokes undefined behavior. On a different platform, it could cause nasal demons or other demonic behavior, so be sure to have an exorcist with you.

#include <cstddef>
#include <iostream>
#include <cstdlib>

struct A {
    A() : data(count++) {}
    ~A() {}
    const std::size_t data;
    static std::size_t count;
};

std::size_t A::count = 1000;

int
main() {

    {
        A *aa = new A[1234];
        std::cout << "The size_t immediatly before an array of A objects: "
                  << *((std::size_t *) aa - 1) << std::endl;
        delete [] aa;
    }

    // Note that for fundamental types, there is no need to store the size, so it doesn't.
    {
        std::size_t *sa = new std::size_t[5678];
        std::cout << "The size_t before an array of std::size_t objects: "
                  << *(sa - 1) << std::endl;
        delete [] sa;
    }
}
kec
  • 2,099
  • 11
  • 17
  • I voted up your answer. You are saying: "The number of elements is typically not stored for primitives, because there is no need to call destructors". The number of elements is also needed to free up memory, isn't it? Or this is solved by the opaque implementation detail mentioned by Kerrek? – robert Dec 25 '14 at 06:21