Default new alignment
Asked Answered
B

0

8

I'm moving some C++17 code to be used in a project that is built with Qt, on Windows, using MinGW 7.3.0, and noticed something weird happening in the 32bit builds:

#include <cstddef>
#include <iostream>
int main() {
  std::cout << __STDCPP_DEFAULT_NEW_ALIGNMENT__ << std::endl;
  std::cout << __BIGGEST_ALIGNMENT__ << std::endl;
  std::cout << sizeof(std::max_align_t) << std::endl;
  std::cout << alignof(std::max_align_t) << std::endl;    
  std::cout << "----------------------------------------" << std::endl;    
  for (int i = 0; i < 8; ++i) {
    const uintptr_t        ptr   = uintptr_t(new char);
    static const uintptr_t first = ptr;
    std::cout << (ptr - first) << " " //
              << (ptr % 16) << " "    //
              << (ptr % 8) << std::endl;
  }    
  return 0;
}

Outputs:

16
16
24
8
----------------------------------------
0 8 0
48 8 0
64 8 0
80 8 0
96 8 0
112 8 0
128 8 0
144 8 0

The same thing happens if I use bigger types / structs instead of char, and also if I call ::operator new(size_t) directly with various sizes, instead of the new operator.

As you can see, STDCPP_DEFAULT_NEW_ALIGNMENT is 16, but I get pointers that are only 8-aligned.

My understanding was that in C++17 operator new(size_t) would always return pointers with alignment of at least STDCPP_DEFAULT_NEW_ALIGNMENT, and the two parameter operator new(size_t, align_val_t) is only required for alignments exceeding STDCPP_DEFAULT_NEW_ALIGNMENT. Here, it seems, operator new(size_t) only respects the lower alignof(std::max_align_t).

Is this expected behavior? What am I getting wrong here? This seems to contradict my reading of: https://en.cppreference.com/w/cpp/memory/new/operator_new

Broads answered 14/11, 2019 at 15:19 Comment(5)
I'd be more concerned about the fact that every pointer value comes out, not just 8-byte aligned, but specifically misaligned for 16-bytes. You'd think that, sooner or later, one would accidentally be 16-byte aligned. That suggests the issue lives within malloc itself.Sennit
@NicolBolas My guess would be that there are large blocks that are 16 aligned, and in those the allocations are made with some 8-byte headers prepended, so that every allocation here is (Header: 8 bytes, Payload: 8 bytes)Setula
Could you try to find the alignment using struct alignas(__BIGGEST_ALIGNMENT__) foo {}; and then new foo;?Maurine
@Maurine that gives a repeating pattern of 8;0;8;0... for (ptr%16). It seems this is a problem with MinGW 7.3.0, and it seems it's fixed in 8.1.0.Broads
malloc on Windows always has alignment MEMORY_ALLOCATION_ALIGNMENT, and for 32bit this is 8.Deerdre

© 2022 - 2024 — McMap. All rights reserved.