I am compiling the code with gcc 11.4 with -m32 -std=c++20 on Linux
alignof(max_align_t) == 16
__STDCPP_DEFAULT_NEW_ALIGNMENT__ == 16
What is the guaranteed alignment that I should expect from new expression? According to https://en.cppreference.com/w/cpp/types/max_align_t:
Pointers returned by allocation functions such as std::malloc are suitably aligned for any object, which means they are aligned at least as strictly as std::max_align_t.
std::max_align_t is usually synonymous with the largest scalar type, which is long double on most platforms, and its alignment requirement is either 8 or 16.
So I would say that it should be 16 bytes, however I see in gdb/debug logs that adresses returned by new expressions in my application are aligned only to 8 bytes.
According to: https://www.gnu.org/software/libc/manual/html_node/Aligned-Memory-Blocks.html I should expect 8 bytes alignment. What is the truth?
If it's ok that new/malloc returns 8 byte aligned adresses and STDCPP_DEFAULT_NEW_ALIGNMENT == 16 then if you want to allocate memory for 16 bytes aligned entity you can fall into a "hole" in allocation mechanism as 8 bytes alignment is not sufficient but your 16 bytes aligned entity can not trigger operator new(sizeof(T), std::align_val_t(alignof(T)))) from c++17.
Is this simmilar problem to the one described here? https://gcc.gnu.org/bugzilla/show_bug.cgi?id=15795
Example on godbolt (unfortunatelly the issue is not visible here): https://godbolt.org/z/53db4sYe7
Output of the program from local machine:
Foo 0 align_val_t 8 char 8
Checking adresses with debug logs and gdb. Googling simmilar issues.
Edit: new statements -> new expressions
A global non-placement new expression aligns the object that it creates suitably as its type requires, in C++17 also for over-aligned types. This may not hold true if user overloads of operator new
are used.
There generally aren't any further requirements on stricter alignment, but some exceptions exist.
An important exception exists for std::byte
, unsigned char
and char
allocating array-new expressions without specified alignment, which will align the array object so that its address is suitably aligned for any object up to the array's size that doesn't exceed the largest fundamental alignment, i.e. alignof(max_align_t)
, and doesn't have new-extended alignment, i.e. alignment requirement larger than __STDCPP_DEFAULT_NEW_ALIGNMENT__
.
Technically any supported alignment may be explicitly specified in a placement-new, e.g. with new(std::align_val_t{/*alignment here*/}) T(/*...*/)
, however this will cause problems when trying to delete the object again, because the compiler will make the decision to provide alignment details to operator delete
based on the allocated type. Because allocations for different alignments may use different allocators, this can cause a mismatched allocation/deallocation and UB. So this isn't practically suitable. For array-new forms there is also generally no guarantee that the specified alignment will transfer to the object's alignment.
If __STDCPP_DEFAULT_NEW_ALIGNMENT__
is larger than what std::malloc
guarantees, then operator new(std::size_t)
cannot generally (for large enough size arguments) use std::malloc
as implementation.
operator new(std::size_t)
must always guarantee alignment for all objects of size equal to the provided size argument and alignment requirement up to __STDCPP_DEFAULT_NEW_ALIGNMENT__
and in the case of operator new[](std::size_t)
this also applies to all objects of sizes smaller than the provided size argument. This holds true even for user overloads and replacements of operator new
/operator new[]
. If a user defines (and uses) a operator new
/operator new[]
which doesn't guarantee this, then the program has undefined behavior.
The above paragraph also extends to all other operator new
/operator new[]
overloads that don't take a std::align_val_t
argument, except for the reserved placement forms (operator new(std::size_t size, void* ptr)
and operator new[](std::size_t size, void* ptr)
). With a std::align_val_t
the overload shall align to the specified argument.
Note however that alignment guarantee of the return value from operator new
/operator new[]
is generally not the same as the alignment guarantee for the object created by a new-expression. There are conditions under which the compiler may offset the object in the storage returned by the allocation function and there are conditions under which the compiler can drop or merge allocation function calls to optimize memory usage.