C++ 具有单个基元类型数组成员的标准布局结构的保证内存布局
考虑以下简单结构:C++ 具有单个基元类型数组成员的标准布局结构的保证内存布局,c++,language-lawyer,memory-layout,standard-layout,C++,Language Lawyer,Memory Layout,Standard Layout,考虑以下简单结构: struct A { float data[16]; }; 我的问题是: 是32位IEEE74浮点数(如果有问题),C++标准是否保证了结构A < /代码>的预期内存布局?如果没有,它保证什么和/或执行保证的方式是什么 通过预期内存布局,我的意思是结构占用内存中的16*4=64字节,每个连续的4字节由数据数组中的单个浮点占用。换句话说,预期内存布局意味着以下测试通过: static_assert(sizeof(A) == 16 * sizeof(float)); s
struct A
{
float data[16];
};
我的问题是:
<假设一个平台,代码<浮动> /COD>是32位IEEE74浮点数(如果有问题),C++标准是否保证了<代码>结构A < /代码>的预期内存布局?如果没有,它保证什么和/或执行保证的方式是什么
通过预期内存布局,我的意思是结构占用内存中的16*4=64
字节,每个连续的4
字节由数据
数组中的单个浮点
占用。换句话说,预期内存布局意味着以下测试通过:
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));
(offset of
在这里是合法的,因为A
是标准布局,见下文)
如果您对此感到不安,请在带有gcc 9头的魔杖盒上进行测试。我从来没有遇到过平台和编译器的组合,它们可以提供这个测试可能失败的证据,如果它们确实存在的话,我很想了解它们
为什么人们会关心:
- 类似SSE的优化需要特定的内存布局(和对齐,我在这个问题中忽略了这一点,因为它可以使用标准的
说明符来处理)alignas
- 这种结构的序列化可以简单地归结为一个好的、可移植的
写字节(&x,sizeof(a))
- 一些API(例如OpenGL,具体地说)期望这种精确的内存布局。当然,只需将指针传递到
数组,即可传递此类型的单个对象,但对于这些对象的序列(例如,用于上载矩阵类型顶点属性),仍然需要特定的内存布局数据
struct A
中得到的:
- 是的
- 作为标准布局的结果,指向
的指针可以a
重新解释为指向其第一个数据成员的指针(可能是
?),即在第一个成员之前没有填充data[0]
- 在基元类型数组的元素之间没有填充(我确信这是错误的,但我没有找到确认引用)
中的struct A
数组之后没有填充data
- 关于布局,有一件事是不能保证的,即多字节对象中的字节顺序<代码>写入字节(&x,sizeof(A))不是跨具有不同端号的系统的可移植序列化
A
可以重新解释\u cast
到指向其第一个数据成员的指针(可能是数据[0]
?)
更正:第一个数据成员是data
,您可以使用它重新解释强制转换。至关重要的是,数组与其第一个元素的指针不可相互转换,因此不能重新解释它们之间的强制转换。然而,地址保证是相同的,所以据我所知,在std::launder
之后重新解释为data[0]
应该没问题
基元类型数组的元素之间没有填充
数组保证是连续的<对象的code>sizeof是根据将元素放入数组所需的填充来指定的sizeof(T[10])
的大小正好是sizeof(T)*10
。如果相邻元素的非填充位之间存在填充,则该填充位于元素本身的末尾
基本类型一般不保证没有填充。例如,x86扩展精度长双精度为80位,填充为128位
char
、signed char
和unsigned char
保证没有填充位。C标准(在此例中C++代表了规范)保证了固定宽度<代码> Inntt和 Untnnt别名没有填充位。在不可能的系统上,不提供这些固定宽度类型
如果标准布局类对象具有任何非静态数据成员,则其
地址与其第一个非静态数据成员的地址相同。
否则,其地址与第一个基址的地址相同
类子对象(如果有)。[注:因此,标准布局结构对象中可能会有未命名的填充,但不会在其开头,这是实现适当对齐所必需的。-结束注]
因此,标准保证:
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
数组类型的对象包含连续分配的非空
类型为T的N个子对象的集合
因此,以下是正确的
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));
剩下的保证中的第一个是由C++ 2017(草案n469)113.4保证的,“数组”[dCL,数组]:“数组类型的对象包含一个连续分配的非空集合的<代码> n< /代码>类型<代码> t>代码> 1998版本具有相同的文本,除了连字符的子对象”。在8.3.4中,谢谢您的澄清!“连续分配”在本文中的确切含义是什么?@lisyarus:它是“普通英语”,或者至少是标准中没有正式定义的领域从业人员使用的英语。我很确定这意味着数组中元素的字节一个接一个地排列在内存中,元素之间没有填充。在C中,剩余的第二个保证是不保证的,并且有一些原因导致“困难”的C实现可能填充包含单个数组的结构。例如,我们可以想象,如果一个实现的目标硬件非常倾向于四个字节,那么它将把struct{char x[2];}
填充到四个字节