With my friend we made a program with overriden new and new[] operators. I discovered that when I try to create string array with this code:
string* Test1 = new string[10];
Function returns invalid pointer (Usually it's value is moved 8 bits foreward, I'm compilling program to x64 platform). Our new[] function looks like that:
void* operator new[] (size_t e)
{
void* Test2 = operator new(e);
return Test2;
}
when running program with debugger before return, pointer Test2
had value 0x0000000009dfaa90, but value of Test1
became 0x0000000009dfaa98.
This situation happens only with string type. I've tried do the same with "int[10]", "string* [10]" and object of one of my classes but problem occures only when dealing with string, also, code:
string* Test1 = new string;
works perfectly fine.
Could someone explain me why it's happenning and how to make it works correctly?
PS: We are using Visual Studio 2012 Proffesional
Edit: I just tested it non-overriden new[]
and it's working this same way when creating string table (Returned pointer is other than the one that function try to return
), so it seems to not be a problem. Could someone explain me why value of pointer changes only for string arrays, and how it changes if there seems to not be any other instruction that could change it?
The answer is that new/delete
and new[]/delete[]
are different. It may not come as a surprise to you, but the other surprising news (pun not intended) is that new
operator and operator new
are different.
Here's an sample code testing the issue (you can change what tested_type
is typedef'd to):
#include <iostream>
#include <vector>
#include <string>
typedef std::string tested_type;
void* Test2;
size_t allocated_mem_size;
void* operator new[] (size_t e)
{
void* retaddr = operator new(e);
Test2 = retaddr;
allocated_mem_size = e;
return retaddr;
}
int _tmain(int argc, _TCHAR* argv[])
{
void* Test1 = new tested_type[10];
std::cout << "sizeof(tested_type)*10 is " << sizeof(tested_type)*10 << "\n"
<< "Test1 is " << Test1 << "\n"
<< "Test2 is " << Test2 << "\n"
<< "operator new[] was called with e == " << allocated_mem_size << "\n"
<< "What's in the missing bytes? " << *(size_t*)Test2 << "\n";
}
The output on my machine is:
sizeof(tested_type)*10 is 280
Test1 is 0085D64C
Test2 is 0085D648
operator new[] was called with e == 284
What's in the missing bytes? 10
(Note - I have a 32-bit compiler)
If we change tested_type
to int, we have:
sizeof(tested_type)*10 is 40
Test1 is 0070D648
Test2 is 0070D648
operator new[] was called with e == 40
What's in the missing bytes? 3452816845
Now, if we change tested_type
to std::vector<int>
, we have
sizeof(tested_type)*10 is 160
Test1 is 004AD64C
Test2 is 004AD648
operator new[] was called with e == 164
What's in the missing bytes? 10
Now we see a pattern here: the extra bytes added are equal to the number of elements allocated. Also, the only time where the bytes are added, is when the type is non-trivial...
That's it!
The reason why the address is adjusted is that new[]
wants to store number of elements. And the reason why we need to store the number of elements in some cases, but not in others, is because delete[]
calls destructors, and delete[]
(but not delete
which just calls destructor for a single element) must somehow know how many elements it must destroy. There is no need for calling destructors for basic types like int
, and so new[]
doesn't store how many there are.
(also, I recommend std::vector
- it just works)