通常,数据根据其大小以两个地址的幂对齐。
我应该如何对齐大小为20字节的结构或类或另一种非二次幂的大小?
我正在创建一个自定义堆栈分配器,所以我猜编译器不会为我对齐数据,因为我正在使用连续的内存块。
更多背景信息:
我有一个使用malloc()分配大量数据的Allocator类。 然后我使用void * allocate(U32 size_of_object)方法返回指针,我可以在哪里存储我需要存储的对象。 这样,所有对象都存储在相同的内存区域中,并且它有望适合缓存,从而减少缓存未命中。
答案 0 :(得分:3)
虽然编译器(或解释器)通常在对齐的边界上分配单个数据项,但数据结构通常具有不同对齐要求的成员。为了保持正确的对齐,翻译器通常会插入其他未命名的数据成员,以便每个成员都正确对齐。此外,整个数据结构可以用最终未命名的成员填充。这允许结构阵列的每个成员正确对齐。 http://en.wikipedia.org/wiki/Data_structure_alignment#Typical_alignment_of_C_structs_on_x86
这表示编译器会为您处理它,99.9%的时间。至于如何强制对象以特定方式对齐特定方式,并且仅在某些情况下有效。
MSVC:http://msdn.microsoft.com/en-us/library/83ythb65.aspx
__declspec(align(20))
struct S{ int a, b, c, d; };
//must be less than or equal to 20 bytes
海湾合作委员会:http://gcc.gnu.org/onlinedocs/gcc-3.4.0/gcc/Type-Attributes.html
struct S{ int a, b, c, d; }
__attribute__ ((aligned (20)));
我不知道这样做的跨平台方式(包括宏!),但在某处可能有整齐的宏。
答案 1 :(得分:3)
C ++ 11具有专门用于此目的的alignof运算符。不要使用其他帖子中提到的任何技巧,因为它们都有边缘情况,或者可能因某些编译器优化而失败。 alignof运算符由编译器实现,并且知道正在使用的对齐方式。
答案 2 :(得分:1)
除非您想直接访问内存,或者在内存块中挤压最大数据,否则您不必担心对齐 - 编译器会为您解决这个问题。
答案 3 :(得分:1)
由于处理器数据总线的工作方式,您要避免的是“错位”访问。通常,您可以从一次访问中读取32位值,该地址是四的倍数;如果你试图从一个不是这么多的地址读取它,CPU可能必须抓住两个或多个部分。所以,如果你真的在这个细节层面上担心事情,那么你需要关注的不是整体结构,而是整体结构。您会发现编译器会经常使用虚拟字节填充结构以确保对齐访问,除非您特别强制它们不使用编译指示。
答案 4 :(得分:1)
由于您现在已经添加了实际上想要编写自己的分配器,因此答案很简单:只需确保分配器返回一个指针值,该指针的值是请求大小的倍数。对象的大小本身已经适当调整(通过内部填充),以便所有成员对象本身都正确对齐,所以如果你请求sizeof(T)
个字节,你的所有分配器需要做的就是返回一个值可分的指针sizeof(T)
。
如果您的对象确实具有大小为20(由sizeof
报告),那么您无需进一步担心。 (在64位平台上,对象可能会填充为24个字节。)
更新:事实上,正如我现在才意识到的那样,严格来说,您只需要确保指针以递归方式对齐,以便最大的成员你的类型。这可能更有效,但是与整个类型的大小保持一致绝对不会出错。
答案 5 :(得分:0)
我应该如何对齐大小为20字节的结构或类或另一种非二次幂的大小?
对齐是特定于CPU的,因此至少在不了解目标CPU的情况下,对此问题没有答案。
一般来说,对齐不是你必须担心的事情;您的编译器将为您实现规则。它偶尔出现,就像编写分配器一样。在 C编程语言(K& R)中讨论了经典的解决方案:使用最差的对齐方式。 malloc
执行此操作,尽管it's phrased as,“如果分配成功,则返回的指针应适当对齐,以便可以将其指定给指向任何类型对象的指针。”
的方法是使用union
(union
的元素都在union
的基地址分配,并且因此,union
必须以这样一种方式对齐,即每个元素都可以存在于该地址;即union
的对齐方式与元素与最严格规则的对齐方式相同):
typedef Align long;
union header {
// the inner struct has the important bookeeping info
struct {
unsigned size;
header* next;
} s;
// the align member only exists to make sure header_t's are always allocated
// using the alignment of a long, which is probably the worst alignment
// for the target architecture ("worst" == "strictest," something that meets
// the worst alignment will also meet all better alignment requirements)
Align align;
};
通过创建一个大小足以满足请求的sbrk()
的数组(使用像header
这样的东西)来分配内存,另外还有一个实际包含簿记信息的header
元素。如果数组被调用arry
,则簿记信息位于arry[0]
,而指针返回arry[1]
处的点(next
元素用于遍历空闲列表)。
This works, but can lead to wasted space(“在Sun的HotSpot JVM中,对象存储与最近的64位边界对齐”)。我知道a better approach试图获得特定于类型的对齐,而不是“对任何东西都有效的对齐。”
编译器通常还具有特定于编译器的命令。它们不是标准的,它们要求您知道相关类型的正确对齐要求。我会避免他们。