struct Foo {
char data[3]; // size is 3, my arch is 64-bit (8 bytes)
};
Padding is允许在此处,在结构体之后data
成员——但不在它之前,也不在以下元素之间data
.
Foo array[4]; // total memory is 3 * 4 = 12 bytes.
此处数组中的元素之间不允许填充。数组要求是连续的。但是,如上所述,在 a 内部允许填充Foo
,遵循其data
成员。所以,sizeof(someFoo.data)
必须是 3,但是sizeof(someFoo)
可能是(通常是 4)。
void testArray() {
Foo * foo1 = array[0];
Foo * foo2 = array[1]; // is foo2 pointing to a non-aligned location?
// should I expect issues here?
}
Again, perfectly fine -- the compiler must allow this1.
但对于你的内存池来说,预测却没有那么好。你已经分配了一个数组char
,它必须充分对齐才能被访问为char
,但像任何其他类型一样访问它是not保证工作。不允许该实现对访问数据施加任何对齐限制,如下所示char
无论如何。
通常,对于这种情况,您可以创建您关心的所有类型的联合,并分配一个数组。这保证了数据对齐以用作联合中任何类型的对象。
或者,您可以动态分配块——两者malloc
and operator ::new
保证任何内存块都对齐以用作任何类型。
编辑:更改要使用的池vector<char>
情况有所改善,但只是轻微改善。这意味着first您分配的对象将起作用,因为向量持有的内存块将被(间接)分配operator ::new
(因为您没有另外指定)。不幸的是,这并没有多大帮助——第二次分配可能完全错位。
例如,假设每种类型都需要“自然”对齐,即对齐到等于其自身大小的边界。字符可以分配在任何地址。我们假设 Short 是 2 个字节,需要偶数地址,而 int 和 long 是 4 个字节,需要 4 字节对齐。
在这种情况下,请考虑如果您这样做会发生什么:
char *a = Foo.Allocate<char>();
long *b = Foo.Allocate<long>();
我们开始的块必须针对任何类型进行对齐,因此它绝对是偶数地址。当我们分配char
,我们只用了一个字节,因此下一个可用地址是奇数。然后我们分配足够的空间long
,但它位于一个奇怪的地址,因此尝试取消引用它会给出 UB.
1 Mostly anyway -- ultimately, a compiler can reject just about anything under the guise of an implementation limit having been exceeded. I'd be surprised to see a real compiler have a problem with this though.