该标准相当具体,即使是 POD 结构(我相信最严格的结构类)也可以在成员之间进行填充。(“因此,在 POD 结构对象中可能存在未命名的填充,但不是在其开头,这是实现适当对齐所必需的。”——非规范性注释,但仍使意图非常清楚)。
例如,对比标准布局结构的要求(C++11,§1.8/4):
普通可复制或标准布局类型 (3.9) 的对象应占用连续的存储字节。”
...与数组的那些(§8.3.4/1):
数组类型的对象包含一个连续分配的非空集合,由 N 个类型为 T 的子对象组成。
在数组中,元素本身需要连续分配,而在结构中,只需要存储是连续的。
可能使“连续存储”要求更有意义的第三种可能性是考虑不可简单复制或标准布局的结构/类。在这种情况下,存储可能根本不连续。例如,一个实现可能会留出一个内存区域来保存所有私有变量,并留出一个完全独立的内存区域来保存所有公共变量。为了更具体一点,请考虑以下两个定义:
class A {
int a;
public:
int b;
} a;
class B {
int x;
public:
int y;
} b;
有了这些定义,内存的布局可能类似于:
a.a;
b.x;
// ... somewhere else in memory entirely:
a.b;
b.y;
在这种情况下,元素和存储都不需要是连续的,因此完全独立的结构/类的交错部分是允许的。
也就是说,第一个元素必须与整个结构位于同一地址 (9.2/17):“指向 POD 结构对象的指针,使用 reinterpret_cast 适当转换,指向其初始成员(或者如果该成员是一个位域,然后是它所在的单元),反之亦然。”
在您的情况下,您有一个 POD 结构,因此(第 9.2/17 节):“指向 POD 结构对象的指针,使用 reinterpret_cast 适当转换,指向其初始成员(或者如果该成员是位字段,然后到它所在的单元),反之亦然。” 由于第一个成员必须对齐,并且其余成员都是相同的类型,因此其他成员之间不可能真正需要任何填充(即,除了位字段,您可以放入结构中的任何类型也可以放入数组,其中需要连续分配元素)。如果您的元素小于一个单词,那么在面向单词的机器(例如早期的 DEC Alphas)上,填充可能会使访问变得更简单一些。例如,早期的 DEC Alpha(在硬件级别)一次只能读/写一个完整的(64 位)字。因此,让我们考虑类似四个char
元素的结构:
struct foo {
char a, b, c, d;
};
如果需要将它们布置在内存中以使它们是连续的,则访问foo::b
(例如)将需要 CPU 加载该字,然后将其向右移动 8 位,然后屏蔽以对该字节进行零扩展以填充整个登记。
存储会更糟——CPU 必须加载整个单词的当前值,屏蔽掉相应字符大小的部分的当前内容,将新值移动到正确的位置,或者将其放入单词中,最后存储结果。
相比之下,通过元素之间的填充,每个元素都成为一个简单的加载/存储,没有移位、屏蔽等。
至少如果内存服务,使用 DEC 的 Alpha 正常编译器,int
是 32 位,并且long
是 64 位(它早于long long
)。因此,使用 4 个int
s 的结构,您可能会期望在元素之间看到另外 32 位的填充(以及最后一个元素之后的另外 32 位)。
鉴于您确实有一个 POD 结构,但您仍然有一些可能性。我可能更喜欢使用offsetof
获取结构成员的偏移量,创建它们的数组,并通过这些偏移量访问成员。我在之前的几个答案中展示了如何做到这一点。