Guaranteed memory layout for standard layout struct with a single array member of primitive type

One thing that is not guaranteed about the layout is endianness i.e. the order of bytes within a multi-byte object. write_bytes(&x, sizeof(A)) is not portable serialisation across systems with different endianness.

A can be reinterpret_cast to a pointer to its first data member (which is, presumably, data[0] ?)

Correction: The first data member is data, which you can reinterpret cast with. And crucially, an array is not pointer-interconvertible with its first element, so you cannot reinterpret cast between them. The address however is guaranteed to be the same, so reinterpreting as data[0] should be fine after std::launder as far as I understand.

There is no padding in between elements of an array of primitive type

Arrays are guaranteed to be contiguous. sizeof of an object is specified in terms of padding required to place elements into an array. sizeof(T[10]) has exactly the size sizeof(T * 10). If there is padding between non-padding bits of adjacent elements, then that padding is at the end of the element itself.

Primitive type is not guaranteed to not have padding in general. For example, the x86 extended precision long double is 80 bits, padded to 128 bits.

char, signed char and unsigned char are guaranteed to not have padding bits. C standard (to which C++ delegates the specification in this case) guarantees that the fixed width intN_t and uintN_t aliases do not have padding bits. On systems where that is not possible, these fixed width types are not provided.


If a standard-layout class object has any non-static data members, its address is the same as the address of its first non-static data member. Otherwise, its address is the same as the address of its first base class subobject (if any). [Note: There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. — end note]

Hence, the standard guarantees that

static_assert(offsetof(A, data[0]) == 0 * sizeof(float));

An object of array type contains a contiguously allocated non-empty set of N subobjects of type T.

Hence, the following are true

static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
static_assert(offsetof(A, data[1]) == 1 * sizeof(float));
...
static_assert(offsetof(A, data[15]) == 15 * sizeof(float));