The default global allocators will When invoked by a new expression for a given type, the default global non-placement forms of C++ operator new
attempt to allocate sufficient storage for an object of the type and, if successful, return a pointer with alignment suitable alignment for that any object with a fundamental alignment requirement. However, the default placement new operator simply returns the given pointer back to the caller without guaranteeing that there is sufficient space in which to construct the object or ensuring that the pointer meets the proper alignment requirements. The C++ Standard, [expr.new], paragraph 16 [ISO/IEC 14882-2014], nonnormatively states:
...
(This note is a reminder of the general requirements specified by the C++ Standard, [basic.stc.dynamic.allocation], paragraph 1, which apply to placement new operators by virtue of [basic.stc.dynamic], paragraph 3.)
In addition, the standard provides the following example later on in the same section:
new(2, f) T[5]
results in a callof operator new[](sizeof(T) * 5 + y, 2, f).
Here,
x
andy
are non-negative unspecified values representing array allocation overhead; the result of the new-expression will be offset by this amount from the value returned byoperator new[]
. This overhead may be applied in all array new-expressions, including those referencing the library functionoperator new[](std::size_t, void*)
and other placement allocation functions. The amount of overhead may vary from one invocation of new to another.
Do not pass a pointer that is not suitably aligned for the object being constructed to placement new. Doing so results in an object being constructed at a misaligned location, which results in undefined behavior. Do not pass a pointer that has insufficient storage capacity for the object being constructed, including the overhead required for arrays. Doing so may result in initialization of memory outside of the bounds of the object being constructed, which results in undefined behavior.
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <new>
void f() {
char c; // Used elsewhere in the function
alignas(long) unsigned char buffer[sizeof(long)]
long *lp = ::new (buffer) long;
// ...
} |
Noncompliant Code Example (Failure to Account For Array Overhead)
This noncompliant code example attempts to allocate sufficient storage of the appropriate alignment for the array of objects of struct S
. However, it fails to account for the overhead an implementation may add to the amount of storage for array objects. The overhead (commonly referred to as a cookie) is necessary to store the number of elements in the array so that the array delete expression or the exception unwinding mechanism can invoke the type's destructor on each successfully constructed element of the array. While some implementations are able to avoid allocating space for the cookie in some situations, assuming they do in all cases is unsafe.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <new>
struct S {
void *p;
S ();
~S ();
};
void f() {
const unsigned N = 32;
alignas (S) unsigned char buffer[sizeof(S) * N];
S *sp = ::new (buffer) S [N];
// ...
} |
Compliant Solution
The amount of overhead required by array new expressions is unspecified but should be documented by quality implementations. The following snippet outlines an example compliant solution that's portable to the Clang and GNU GCC compilers. Porting it to other implementations requires adding similar conditional definitions of the Overhead
constant. When an implementation does not document the overhead, assuming it's at least as large as twice the size of sizeof(size_t)
should typically be safe. To verify that the assumption is, in fact, safe, the compliant solution defines an overload of the placement operator new that accepts the buffer size as a third argument and verifies that it is at least as large as the total amount of storage required.
Code Block | ||||
---|---|---|---|---|
| ||||
#if __clang__ || __GNUG__
const size_t Overhead = sizeof(size_t);
#else
const size_t Overhead = 2 * sizeof(size_t);
#endif
void* operator new[] (size_t n, void *p, size_t bufsize) {
assert (n <= bufsize); // alternatively, throw an exception
return p;
}
void f() {
const size_t N = 32;
alignas(S) unsigned char buffer[sizeof(S) * N + Overhead];
S *sp = new (buffer, sizeof buffer) S [N];
// ...
// Destroy elements of the array.
for (size_t i = 0; i != N; ++i)
sp[i].~S ();
} |
Risk Assessment
Providing improperly aligned pointers to placement new can result in undefined behavior, including abnormal termination.
...