具有单个原始类型数组成员的标准布局结构的保证内存布局

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

考虑以下简单结构:

struct A
{
    float data[16];
};

我的问题是:

假设一个平台,其中 float 是一个 32 位 IEEE754 浮点数(如果这很重要的话),C++ 标准是否保证 [=13= 的预期内存布局]?如果不是,它保证什么 and/or 执行保证的方法是什么

通过预期的内存布局我的意思是该结构在内存中占用16*4=64字节,每个连续的4字节由一个float 来自 data 数组。换句话说,预期内存布局意味着以下测试通过:

static_assert(sizeof(A) == 16 * sizeof(float));
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));

offsetof 在这里是合法的,因为 A 是标准布局,见下文)

如果这让您感到困扰,请使用 gcc 9 HEAD 在 wandbox 上进行测试 actually passes。我从未见过平台和编译器的组合可以提供该测试可能失败的证据,如果它们确实存在,我很乐意了解它们。

为什么还要关心:

实际保证的是:

据我所知,这些是 struct A 可以预期的事情:

标准提供的不是(据我所知)的两个剩余保证是:

关于布局不能保证的一件事是字节顺序,即多字节对象中的字节顺序。 write_bytes(&x, sizeof(A)) 不是跨具有不同字节顺序的系统的可移植序列化。

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

更正:第一个数据成员是 data,您可以用它重新解释 cast。至关重要的是,数组不能与其第一个元素进行指针互换,因此您不能重新解释它们之间的强制转换。但是,地址保证是相同的,因此据我所知,在 std::launder 之后重新解释为 data[0] 应该没问题。

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

保证数组是连续的。对象的 sizeof 是根据将元素放入数组所需的填充来指定的。 sizeof(T[10]) 的大小恰好是 sizeof(T) * 10。如果相邻元素的非填充位之间有填充,则该填充位于元素本身的末尾。

原始类型通常不保证没有填充。例如x86扩展精度long double为80位,填充为128位。

charsigned charunsigned char 保证没有填充位。 C 标准(在这种情况下 C++ 委托规范)保证固定宽度 intN_tuintN_t 别名没有填充位。在不可能的系统上,不提供这些固定宽度的类型。

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]

因此,标准保证

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.

因此,下列说法正确

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));