问题描述
我最近学到了一些关于对齐,但我不确定在哪些情况下它将是一个问题或不。有两种情况我不知道:
I've learned a little about alignment recently but I am not certain in which situations it will be an issue or not. There are two cases that I wonder about:
第一种是使用数组时:
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?
}
第二种情况是使用内存池时:
The second case is when using a memory pool:
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);
}
我的问题是:
- 两个代码示例中是否存在对齐问题?
- 如果是,那么如何修复?
- 我在哪里可以了解更多?
我使用64位的Intel i7处理器与达尔文GCC。
但我也使用Linux,Windows(VC2008)用于32位和64位系统。
I am using a 64-bit Intel i7 processor with Darwin GCC.But I also use Linux, Windows (VC2008) for 32-bit and 64-bit systems.
Pool现在使用向量而不是数组。
Pool now uses a vector instead of array.
推荐答案
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.
此处不允许填充。数组必须是连续的。
[edit:但在数组中的结构体之间不允许填充 - 数组中的 struct
必须紧跟在另一个之后 - 但是如上所述,每个结构本身可以包含填充。]
No padding is allowed here. Arrays are required to be contiguous.[edit: but no padding is allowed between the struct's in the array -- one struct
in the array must follow immediately after another -- but as noted above, each struct can itself contain padding.]
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 this.
对于你的内存池,预测不是那么好。您分配了一个 char
数组,该数组必须与 char
充分对齐,但访问它因为任何其他类型是不保证工作。在任何情况下,执行都不允许对访问数据作为 char
施加任何对齐限制。
For your memory pool, the prognosis isn't nearly as good though. You've allocated an array of char
, which has to be sufficiently aligned to be accessed as char
, but accessing it as any other type is not guaranteed to work. The implementation isn't allowed to impose any alignment limits on accessing data as char
in any case though.
对于这样的情况,你创建一个所有你关心的类型的并集,并分配一个数组。这保证数据被对齐以用作联合中的任何类型的对象。
Typically for a situation like this, you create a union of all the types you care about, and allocate an array of that. This guarantees that the data is aligned to be used as an object of any type in the union.
或者,您可以动态分配块 - 两者 malloc
和 operator :: new
保证任何内存块对齐以用作任何类型。
Alternatively, you can allocate your block dynamically -- both malloc
and operator ::new
guarantee that any block of memory is aligned to be used as any type.
编辑:更改池以使用向量< char>
改善了情况,但只是轻微。这意味着您分配的第一个对象将工作,因为向量所持有的内存块将被分配(间接) operator :: new
因为你没有指定其他)。不幸的是,这没有什么帮助 - 第二个分配可能完全不对齐。
changing the pool to use vector<char>
improves the situation, but only slightly. It means the first object you allocate will work because the block of memory held by the vector will be allocated (indirectly) with operator ::new
(since you haven't specified otherwise). Unfortunately, that doesn't help much -- the second allocation may be completely misaligned.
例如,假设每个类型都需要自然到等于其自身大小的边界。可以在任何地址分配char。我们假设short是2个字节,需要一个偶数地址,int和long是4个字节,需要4个字节对齐。
For example, let's assume each type requires "natural" alignment -- i.e., alignment to a boundary equal to its own size. A char can be allocated at any address. We'll assume short is 2 bytes, and requires an even address and int and long are 4 bytes and require 4-byte alignment.
在这种情况下,如果你这样做:
In this case, consider what happens if you do:
char *a = Foo.Allocate<char>();
long *b = Foo.Allocate<long>();
我们开始的块必须对齐任何类型,所以它绝对是一个偶数地址。当我们分配 char
时,我们只使用一个字节,所以下一个可用的地址是奇数。然后,我们为 long
分配足够的空间,但它在一个奇怪的地址,因此尝试取消引用它给UB。
The block we started with had to be aligned for any type, so it was definitely an even address. When we allocate the char
, we use up only one byte, so the next available address is odd. We then allocate enough space for a long
, but it's at an odd address, so attempting to dereference it gives UB.
大多数情况下,编译器最终可以拒绝任何超出实现限制的伪装。我会惊讶地看到一个真正的编译器有一个问题,虽然。
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.
这篇关于我什么时候应该担心对齐?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!