c++c++23

To obtain correctly aligned memory in the allocation of `std::vector<T>`, does it matter what the choice of `T` is?


A std::vector<char> contains a pointer to some heap-allocated buffer.

Considering memory alignment issues, is the allocated buffer returned by the memory allocation guaranteed to be correctly aligned for only the type char, or will it be aligned to some common value, such as 4 or 8 bytes? (Or is it unspecified?)

To explain by way of example, the default allocator is new. Calling new may return an arbitrary memory address. But is it really arbitrary?

The reason for asking this question is that I want to know whether I can safely re-interpret the bytes contained within this block of memory as other types, for example float, double, uint64_t, etc.

Going the other way, the situation is more obvious. (I think - do correct me.)

For example, if we were to ask for a std::vector<uint64_t>, then the pointer returned by the memory allocation, and held by the std::vector object, would be 8-byte aligned.

So it would be safe to read and write data as:

However, if the allocation for std::vector<char> can return a memory address of 1025 (decimal), then this is clearly not guaranteed to be aligned correctly at any offset if re-interpreted as any type larger than 1 byte wide.

I think my understanding is correct here, but of course do let me know in the comments if any of this doesn't quite make sense or fit together correctly.


Edit:

I may have found an answer to this, but I am not sure how to interpret what I have read.

From:

This function is required to return a pointer suitably aligned to point to an object of the requested size.

But what does this mean?

For an object of width 1 byte, this would seemingly suggest any address value is acceptable.

For an object of width 2 bytes, it would suggest any even value address is acceptable.

For 4 bytes, any multiple of 4. And for 8 bytes any multiple of 8.

But what about other values? There is no instruction to load a 3 byte value from memory in x86, as far as I am aware. What happens if you request new[T] where T is a 3 byte wide struct?


Solution

  • While it technically doesn't seem to be guaranteed, I'm fairly sure no implementation is going to give you an under-aligned pointer from a std::vector<char>.


    The default operator new returns a memory aligned to __STDCPP_DEFAULT_NEW_ALIGNMENT__, and while I can't find any guarnatees that this is >= alignof(std::max_align_t), any sane implementation would do it this way.

    There's also a version of operator new that takes std::align_val_t, and while its intent is to support larger alignments, there's no guarantee that std::allocator doesn't call it for smaller alignments too, though a sane implementation probably either wouldn't do it, or if it would, the call would still ensure at least __STDCPP_DEFAULT_NEW_ALIGNMENT__ alignment.

    A big part of why I expect this to work in practice is that std::align_val_t and the overaligned operator new were added relatively recently, in C++17. Before that you had to have __STDCPP_DEFAULT_NEW_ALIGNMENT__ >= alignof(std::max_align_t) for new to work sanely, and std::vector<char> had to give you a pointer aligned at least to __STDCPP_DEFAULT_NEW_ALIGNMENT__, so I doubt the implementations are going to suddenly drop this guarantee.