In a legacy project I am maintaining in my freetime, operators delete
/new
and delete[]
/new[]
were overloaded to spot mismatches (new[]
allocated object delete
d and vice versa).
The original prefix had a length of 9 bytes. It has not led to issues since at least VS2010, and possibly even since VS6.
I have recently tackled rewriting this piece of code and to that end asked a question at codereview.stackexchange. The old prefixes had ended with one identical character which I removed, so my prefix was only 8 bytes long. Two people noted that this might break alignment and one referred to the C++ standard paragraph 6.11 Alignment... Unfortunately I fail to grasp the issue from reading it.
The second sentence there reads as follows:
An alignment is an implementation-defined integer value representing the number of bytes between successive addresses at which a given object can be allocated.
... And as far as I understand this definition means that all is well:
- If I allocate a single object, the OS has to handle distance to the previous and next objects in dynamic memory. Distance to the previous object will only increase by length of the prefix. Such distances are presumably not part of alignment. So, okay.
- If I allocate an array, alignment between its elements has been handled before
operator new[]
gets itssize
parameter. I do not change this, so okay.- For begin and end of the array, considerations at 1) apply.
All seems to be perfectly fine. Yet, questions such as this one clearly signal that special alignment handling must be necessary in some cases.
- What characterises these cases?
- How can I generate such cases?
- What invalidates my assumption that all is fine?
Or am I right and this is a perfectly harmless state of affairs?
Here is a code snippet illustrating the principle behind the overloads in question. Refer to my original question for a complete (and safer) example.
constexpr char PREFIX[] = "str1med";
constexpr std::size_t OFFSET = sizeof(PREFIX);
void * operator new(std::size_t size)
{
void * pointer = std::malloc(size + OFFSET);
std::memcpy(pointer, PREFIX, OFFSET);
return reinterpret_cast<std::byte*>(pointer) + OFFSET;
}
void operator delete(void * untypedPointer)
{
std::byte * pointer = reinterpret_cast<std::byte*>(untypedPointer);
pointer -= OFFSET;
assert(std::memcmp(pointer, prefix, OFFSET) == 0);
std::free(pointer);
}