14
SomeObj<unsigned int>* Buffer;
char* BufferPtr = MemoryManager::giveMeSomeBytes(resX*resY*sizeof(SomeObj<unsigned int>));
Buffer = new(BufferPtr) SomeObj<unsigned int>[resX*resY];

when I step past these lines with the debugger, it shows me the values for the variables Buffer and BufferPtr:

BufferPtr: 0x0d7f004c
Buffer:    0x0d7f0050

I don't really understand why those values differ. The way I understand it, placement new should use the memory starting at address 'BufferPtr' to initialize the array elements using theyr default constructors on the allocated memory and return a pointer to the first byte of the first element in the array, which should be exactly the same byte as passed to the placement new operator.

Did I understand something wrong or can someone tell me why the values differ?

thanks!

//edit: ok - i investigated the issue further and got more confusing results:

    int size = sizeof(matth_ptr<int>);

    char* testPtr1 = (char*)malloc(a_resX*a_resY*sizeof(int));
    int* test1 = new(testPtr1) int[a_resX*a_resY];

    char* testPtr2 = mmgr::requestMemory(a_resX*a_resY*sizeof(int));
    int* test2 = new(testPtr2) int[a_resX*a_resY];

    char* testPtr3 = (char*)malloc(a_resX*a_resY*sizeof(matth_ptr<int>));
    matth_ptr<int>* test3 = new(testPtr3)matth_ptr<int>[a_resX*a_resY];

    char* testPtr4 = mmgr::requestMemory(a_resX*a_resY*sizeof(matth_ptr<int>));
    matth_ptr<int>* test4 = new(testPtr4)matth_ptr<int>[a_resX*a_resY];

the debugger returns me the following values for my variables:

size: 4

testPtr1:0x05100418
test1:   0x05100418
testPtr2:0x0da80050
test2:   0x0da80050

testPtr3:0x05101458
test3:   0x0510145c
testPtr4:0x0da81050
test4:   0x0da81054

so it clearly must have something to do with my generic smartpointer class matth_ptr so here it is:

template <class X> class matth_ptr
{
public:
    typedef X element_type;

    matth_ptr(){
        memoryOfst = 0xFFFFFFFF;
    } 

    matth_ptr(X* p) 
    {
        unsigned char idx = mmgr::getCurrentChunkIdx();
        memoryOfst = (int)p-(int)mmgr::getBaseAddress(idx);
        assert(memoryOfst<=0x00FFFFFF || p==0);//NULL pointer is not yet handled
        chunkIdx = idx;
    }
    ~matth_ptr()                {}
    X& operator*()              {return *((X*)(mmgr::getBaseAddress(chunkIdx)+(memoryOfst&0x00FFFFFF)));}
    X* operator->()             {return  ((X*)(mmgr::getBaseAddress(chunkIdx)+(memoryOfst&0x00FFFFFF)));}
    X* get()                    {return  ((X*)(mmgr::getBaseAddress(chunkIdx)+(memoryOfst&0x00FFFFFF)));}


    template<typename T>
    matth_ptr(const matth_ptr<T>& other) {memoryOfst=other.memoryOfst;}//put these two operators into the private part in order to prevent copying of the smartpointers
    template<typename T>
    matth_ptr& operator=(const matth_ptr<T>& other) {memoryOfst = other.memoryOfst; return *this;}
    template<typename T>
    friend class matth_ptr;
private:

    union //4GB adressable in chunks of 16 MB
    {
        struct{
            unsigned char padding[3]; //3 bytes padding
            unsigned char chunkIdx; //8 bit chunk index
        };
        unsigned int memoryOfst; //24bit address ofst
    };

};

can anyone explain me what's going on? thanks!

Mat
  • 2,713
  • 5
  • 25
  • 25
  • Off the top of my head, might be alignment or VTable space. – Michael Aaron Safyan Oct 25 '10 at 02:47
  • does that mean placement new creates an object simply at a higher memory address if it doesn't like the alignment of the passed address? – Mat Oct 25 '10 at 02:50
  • I would suspect so, alhough don't know for certain. One way to test would be to see if this happens at the selected memory location (i.e. force giveMeSomeBytes to give an aligned address). – Michael Aaron Safyan Oct 25 '10 at 02:51
  • i changed giveMeSomeBytes to be 16byte aligned-> `BufferPtr: 0x0da50050` and `Buffer: 0x0da50054` so alignment can't be the issue here – Mat Oct 25 '10 at 04:03

4 Answers4

15

Be careful with placement new on arrays. In the current standard look to section 5.3.4.12, you'll find this:

new(2,f) T[5] results in a call of operator new[](sizeof(T)*5+y,2,f)

It is clear that it will expect the placement new operator to allocate it additional space beyond what the array contents need. "y" is specified only as a non-negative integral value. It will then offset the result of the new function by this amount.

Also look to 18.4.1.3.4 where it says the placement new operator simply returns the provided pointer. This is obviously the expected part.

Based on 5.3.4.12, since that offset may be different for every invocation of the array, the standard basically means there is no way to allocate the exact amount of size needed. In practice that value is probably constant and you could just add it to the allocation, but his amount may change per platform, and again, per invocation as the standard says.

edA-qa mort-ora-y
  • 30,295
  • 39
  • 137
  • 267
  • 12
    so basically this means - in regards of the standard - placement new on arrays is not usable – Mat Oct 25 '10 at 18:13
  • In terms of pre-allocated arrays of the exact size: yes. Remember you can still create a custom placement allocator that works more like a dynamic allocator on a larger block of memory. – edA-qa mort-ora-y Oct 25 '10 at 19:15
  • Keep in mind that `operator new[](sizeof(T)*5+y,2,f)` is a **user-defined placement ALLOCATION function**, while `operator new[](sizeof(T)*5, ptr)` is a **NON-ALLOCATING placement allocation function**. What you see here is another f**k up resulting from careless generalization by "The Committee". I hope this gets cleared no later than 2020. – bit2shift Feb 16 '17 at 06:21
7

You're using the array version of the new operator which in your implementation is storing information about the array size in the first few bytes of the memory allocation.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • and when is the array version of new operator storing those bytes and when is it not doing it? please check my extended test in the original question. turns out that with type 'int' the adress is not ofset, but with my own object it is. – Mat Oct 25 '10 at 04:30
  • Whether the array size is stored depends on whether the object type has a destructor, because delete[] operator will have to call the destructor for each object. – Timo Oct 25 '10 at 04:38
  • so - for objects having a destructor one has to actually allocate 4 more extrabytes to not run into troubles? why do the tutorials discussing array allocation with placement new not mention this? sounds significant to me... – Mat Oct 25 '10 at 04:41
  • @Mat - which tutorials? I've never used array placement new, and right now I'm no sure it can be used portably - there's no specification on how (or even when) array new will store array size information. I'd probably perform a series of non-array placement new calls over the memory area, constructing each object in its own slot individually. – Michael Burr Oct 25 '10 at 05:42
  • i've seen a few ones online.. this one for example I was using so far: http://www.devx.com/cplus/10MinuteSolution/30508/1954 - after I removed the deconstructor from my class it worked. however, the tutorial for example calls the destructors explicitely - so in the tutorial there ARE destructors - but those 4 bytes are not mentioned... – Mat Oct 25 '10 at 06:37
3

@Mat, This is actually a great question. When I've used placement new[], I've had trouble deleting the storage. Even if I call my own symmetrical placement delete[], the pointer address is not the same as was returned by my own placement new[]. This makes placement new[] completely useless, as you've suggested in the comments.

The only solution I've found was suggested by Jonathan@: Instead of placement new[], use placement new (non-array) on each of the elements of the array. This is fine for me as I store the size myself. The problem is that I have to worry about pointer alignments for elements, which new[] is supposed to do for me.

cdunn2001
  • 17,657
  • 8
  • 55
  • 45
  • 3
    There's no such thing as placement delete operator. The placement delete *function* gets called when the constructor throws inside the placement new operator, and it does absolutely nothing. Please be advised, the `void*` placement new operators are meant to construct objects in a given memory region, not to allocate memory from the heap. As such, you *CANNOT* use operators `delete` or `delete[]` on a region constructed with placement new array, you need to keep the array length (sounds familiar?) and loop through the array and call each destructor. – bit2shift Mar 11 '16 at 01:34
1

As others have said, this is due to your C++ implementation storing the size of the array at the start of the buffer you pass to array placement new.

An easy fix for this is to simply assign your array pointer to the buffer, then loop over the array and use regular (non-array) placement new to construct each object in the buffer.

Jonathan
  • 41
  • 1
  • 2