我最近学到了一些关于对齐的知识,但我不确定在哪种情况下它会成为一个问题.我怀疑有两种情况:
第一个是使用数组时:
struct Foo {
char data[3]; // size is 3, my arch is 64-bit (8 bytes)
};
Foo array[4]; // total memory is 3 * 4 = 12 bytes.
// will this be padded to 16?
void testArray() {
Foo foo1 = array[0];
Foo foo2 = array[1]; // is foo2 pointing to a non-aligned location?
// should one expect issues here?
}
Run Code Online (Sandbox Code Playgroud)
第二种情况是使用内存池时:
struct Pool {
Pool(std::size_t size = 256) : data(size), used(0), freed(0) { }
template<class T>
T * allocate() {
T * result = reinterpret_cast<T*>(&data[used]);
used += sizeof(T);
return result;
}
template<class T>
void deallocate(T * ptr) {
freed += sizeof(T);
if (freed == used) {
used = freed = 0;
}
}
std::vector<char> data;
std::size_t used;
std::size_t freed;
};
void testPool() {
Pool pool;
Foo * foo1 = pool.allocate<Foo>(); // points to data[0]
Foo * foo2 = pool.allocate<Foo>(); // points to data[3],
// alignment issue here?
pool.deallocate(foo2);
pool.deallocate(foo1);
}
Run Code Online (Sandbox Code Playgroud)
我的问题是:
我正在使用64位Intel i7处理器和Darwin GCC.但我也将Linux,Windows(VC2008)用于32位和64位系统.
池现在使用向量而不是数组.
Jer*_*fin 14
struct Foo {
char data[3]; // size is 3, my arch is 64-bit (8 bytes)
};
Run Code Online (Sandbox Code Playgroud)
[编辑:我本来应该更明确:填充被允许在这里,在之后的结构data件(而不是之前它).
Foo array[4]; // total memory is 3 * 4 = 12 bytes.
Run Code Online (Sandbox Code Playgroud)
这里不允许填充.数组必须是连续的.[编辑:但是数组中的结构之间不允许填充 - 数组中的一个struct必须紧跟在另一个之后 - 但如上所述,每个结构本身都可以包含填充.
void testArray() {
Foo * foo1 = array[0];
Foo * foo2 = array[1]; // is foo2 pointing to a non-aligned location?
// should I expect issues here?
}
Run Code Online (Sandbox Code Playgroud)
再次,完全没问题 - 编译器必须允许这个1.
对于你的记忆库,预后并不是那么好.您已经分配了一个数组char,该数组必须足够对齐才能被访问char,但是不能保证以任何其他类型访问它.但是,char在任何情况下,都不允许实现对访问数据施加任何对齐限制.
通常,对于这种情况,您可以创建所关注的所有类型的并集,并分配一个数组.这可以保证数据对齐以用作联合中任何类型的对象.
或者,您可以动态分配块 - malloc并operator ::new保证任何内存块都对齐以用作任何类型.
编辑:更改池使用vector<char>改善了情况,但只是略有改进.这意味着您分配的第一个对象将起作用,因为向量持有的内存块将(间接)分配operator ::new(因为您没有另外指定).不幸的是,这没有多大帮助 - 第二次分配可能完全错位.
例如,假设每种类型都需要"自然"对齐 - 即,对齐到与其自身大小相等的边界.可以在任何地址分配字符.我们假设short是2个字节,需要一个偶数地址,int和long是4个字节,需要4个字节的对齐.
在这种情况下,请考虑如果您这样做会发生什么:
char *a = Foo.Allocate<char>();
long *b = Foo.Allocate<long>();
Run Code Online (Sandbox Code Playgroud)
我们开始的块必须对齐任何类型,所以它绝对是一个偶数地址.当我们分配时char,我们只使用一个字节,因此下一个可用地址是奇数.然后我们为a分配足够的空间long,但它位于奇数地址,因此尝试取消引用它会产生UB.
1大多数情况下 - 最终,编译器可以在超出实现限制的幌子下拒绝任何事情.我很惊讶地发现真正的编译器有这个问题.