27

考虑以下简单结构:

struct A
{
    float data[16];
};

我的问题是:

假设一个平台float是 32 位 IEEE754 浮点数(如果这很重要),C++ 标准是否保证预期的内存布局struct A?如果不是,它保证什么和/或执行保证的方法是什么

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

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。我从来没有遇到过一个平台和编译器的组合可以提供这个测试可能失败的证据,如果它们确实存在,我很想了解它们。

为什么有人会关心:

  • 类似 SSE 的优化需要一定的内存布局(和对齐,我在这个问题中忽略了它,因为它可以使用标准alignas说明符来处理)。
  • 这种结构的序列化可以简单地归结为一个漂亮且可移植的write_bytes(&x, sizeof(A)).
  • 一些API(例如OpenGL,特别是glUniformMatrix4fv)期望这种精确的内存布局。当然,可以只传递指向data数组的指针来传递这种类型的单个对象,但是对于这些序列(例如,用于上传矩阵类型顶点属性),仍然需要特定的内存布局。

实际保证:

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

  • 这是标准布局
  • 作为标准布局的结果,指向的指针A可以是reinterpret_cast指向其第一个数据成员的指针(大概是?),即在第一个成员之前data[0]没有填充。

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

  • 原始类型数组的元素之间没有填充(我确信这是错误的,但我未能找到确认参考),
  • 里面的数组后面没有填充。datastruct A
4

2 回答 2

13

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

A可以是reinterpret_cast指向其第一个数据成员的指针(大概是data[0]?)

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

原始类型数组的元素之间没有填充

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

基本类型一般不保证没有填充。例如,x86 扩展精度long double为 80 位,填充为 128 位。

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

于 2019-04-12T12:13:19.713 回答
2

如果标准布局类对象有任何非静态数据成员,则其地址与其第一个非静态数据成员的地址相同。否则,其地址与其第一个基类子对象(如果有)的地址相同。[注意:因此,标准布局结构对象中可能存在未命名的填充,但不是在其开头,这是实现适当对齐所必需的。——尾注]

因此,该标准保证

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

数组类型的对象包含一个连续分配的非空集合,由 N 个类型为 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));
于 2019-04-12T12:17:28.763 回答