Dynamic array perhaps having a secret data?

Hello everyone,

Here we have simple dynamic array:
1
2
3
4
int *p = new int[2]; // creating/allocating array
p[0] = 1;
p[1] = p[0] + 1;
delete [] p;


I always wondered how those the delete operator know how much to deallocate and
then I got to thinking, why won't this following code work:
1
2
3
4
5
6
int *p = new int[2];
p[0] = 1;
p[1] = p[0] + 1;

int *u = &p[1];
delete [] u;


but this does:
1
2
3
4
5
6
int *p = new int[2];
p[0] = 1;
p[1] = p[0] + 1;

int *u = &p[0];
delete [] u;


its like there's some extra data behind the address where the real variable's value starts, perhaps the allocated size?

if so, is it accessible?
if so, how could I read it? (getting the number of bytes or dynamic array size)
would it be safe to do so?

Thank you!
Last edited on
See:

http://en.cppreference.com/w/cpp/language/new
[quote=en.cppreference]Array allocation may supply unspecified overhead, which may vary from one call to new to the next.[/quote]


perhaps the allocated size?
No.
if so, is it accessible?
Yes, with point arithmetic.
if so, how could I read it?
Unknown. This is compiler specific. Maybe you find something in the compiler documentation.
would it be safe to do so?
No. It may change even within different versions of the same compiler.

why won't this following code work:
Because you provide delete with an an invalid pointer. Only the pointer returned by new is valid for delete.
> its like there's some extra data behind the address where the real variable's value starts,
> perhaps the allocated size?

This - a size_t sized 'magic cookie' which holds the number of objects) - is a common implementation technique (typically used when the type has a non-trivial destructor).

This code is certainly not portable:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>

namespace { void* array_operator_new_returned = nullptr ; }

struct A
{
    virtual ~A() = default ; // to give the compiler something to think about

    char filler[8] = "filler\n" ;

    static void* operator new ( std::size_t nbytes )
    {
        void* pv = ::operator new( nbytes ) ;
        std::cout << "operator new - request for " << nbytes << " bytes - return " << pv << '\n' ;
        return pv ;
    }

    static void* operator new[] ( std::size_t nbytes )
    {
        void* pv = ::operator new[]( nbytes ) ;
        array_operator_new_returned = pv ;
        std::cout << "operator new[] - request for " << nbytes << " bytes - return " << pv << '\n' ;
        return array_operator_new_returned ;
    }

    static void operator delete( void* pv )
    {
        std::cout << "operator delete - memory at address " << pv << '\n' ;
        ::operator delete(pv) ;
    }

    static void operator delete[]( void* pv )
    {
        std::cout << "operator delete[] - memory at address " << pv << '\n'
                  << "   first " << sizeof(std::size_t) << " bytes contain integer "
                  << *reinterpret_cast<std::size_t*>(pv) << '\n' ;

        ::operator delete[](pv) ;
    }
};

int main()
{
    std::size_t sz1 ;
    std::cin >> sz1 ;
    std::cout << "sz1 == " << sz1 << " sizeof( A[sz1] ): " << sizeof(A) * sz1 << '\n' ;

    std::cout << "\n> A* pa = new A[sz1] ;\n" ;
    A* pa = new A[sz1] ; // array of objects: request for sizeof( A[5] ) + some extra bytes
    std::cout << "\n    address of first object: " << pa << '\n'
              << "array_operator_new_returned: " << array_operator_new_returned << '\n' ;
    std::cout << "\n> delete[] pa;\n" ;
    delete[] pa ;
    std::cout << "\n----------------------------\n\n" ;

    std::size_t sz2 ;
    std::cin >> sz2 ;
    std::cout << "sz2 == " << sz2 << " sizeof( A[sz2] ): " << sizeof(A) * sz2 << '\n' ;

    std::cout << "> delete new A[sz2]\n" ;
    delete[] new A[sz2] ;
}

http://coliru.stacked-crooked.com/a/2ba22128e2289e53
http://rextester.com/DMRN77619
Topic archived. No new replies allowed.