align (C++)
Microsoft 专用
使用 __declspec(align(#)) 准确控制用户定义的数据的对齐方式(例如,函数中的静态分配或自动数据)。
__declspec( align( # ) ) declarator
备注
编写使用最新的处理器指令的应用程序会引入一些新的约束和问题。 具体而言,许多新指令要求数据必须与 16 字节边界对齐。 另外,通过将常用数据与特定处理器的缓存行大小对齐,可以提高缓存性能。 例如,如果你定义一个大小小于 32 字节的结构,你可能希望将此结构对齐到 32 字节,以确保有效缓存该结构类型的对象。
# 是对齐值。 有效项为介于 1 和 8192(字节)之间的 2 的整数幂,如 2、4、8、16、32 或 64。 declarator 是你声明为对齐的数据。
有关如何返回该类型对齐要求的 size_t 类型的值的信息,请参阅 __alignof。 有关如何在面向 64 位处理器时声明未对齐指针的信息,请参阅 __unaligned。
在定义 struct、union 或 class 时或声明变量时,可以使用 __declspec(align(#))。
编译器不保证或不尝试保留复制过程中或数据转换操作中数据的对齐特性。 例如,memcpy 可将使用 __declspec(align(#)) 声明的结构复制到任何位置。 注意,普通分配器(例如,malloc、C++ operator new 和 Win32 分配器)返回的内存通常不会为 __declspec(align(#)) 结构或结构的数组充分对齐。 若要保证复制或数据转换操作的目标正确对齐,请使用 _aligned_malloc,或编写你自己的分配器。
不能指定函数参数的对齐方式。 当通过堆栈上的值传递具有对齐特性的数据,它的对齐由调用约定控制。 如果数据对齐在所调用函数中很重要,请在使用前将参数复制到正确对齐的内存中。
如果没有 __declspec(align(#)),Visual C++ 将根据目标处理器和数据的大小将数据对齐到自然边界,在 32 位处理器上对齐到 4 字节边界,64 位处理器上则对齐到 8 字节边界。 类或结构中的数据在类或结构中依据其自然对齐的最小值和当前包装设置(来自 #pragma pack 或 /Zp 编译器选项)进行对齐。
本示例演示 __declspec(align(#)) 的使用:
__declspec(align(32)) struct Str1{
int a, b, c, d, e;
};
此类型现在具有 32 字节对齐特性。 这意味着所有静态和自动实例都在 32 字节边界上启动。 使用该类型声明为成员的其他结构类型保留了该类型的对齐特性,也就是说,任何将 Str1 作为元素的结构将至少具有 32 字节的对齐特性。
请注意,sizeof(struct Str1) 等于 32。 这意味着,如果创建 Str1 对象的数组,并且该数组的基为对齐的 32 字节,则数组的每个成员也是对齐的 32 字节。 若要创建其基已在动态内存中正确对齐的数组,请使用 _aligned_malloc,或编写你自己的分配器。
任何结构的 sizeof 值是最终成员的偏移量加上该成员的大小,向上舍入为最大成员对齐值的最接近倍数或整个结构的对齐值(以较大者为准)。
编译器使用以下结构对齐规则:
除非使用 __declspec(align(#)) 进行重写,否则标量结构成员的对齐方式为其大小和当前包装的最小值。
除非使用 __declspec(align(#)) 进行重写,否则结构的对齐方式是其成员单个对齐方式的最大值。
结构成员被放置从其父级结构的开头的偏移量位置,父级结构是大于或等于前一个成员末尾的偏移量的对齐的最小倍数。
结构的大小是大于或等于其最后一个成员末尾的偏移量的对齐的最小倍数。
__declspec(align(#)) 只能增大对齐限制。
有关更多信息,请参见:
对齐示例
使用 __declspec(align(#)) 定义新类型
在线程本地存储区中对齐数据
对齐如何与数据打包配合使用
结构对齐示例(特定于 x64)
对齐示例
以下示例说明 __declspec(align(#)) 如何影响数据结构的大小和对齐方式。 这些示例假定下列定义:
#define CACHE_LINE 32
#define CACHE_ALIGN __declspec(align(CACHE_LINE))
在此示例中,通过使用 __declspec(align(32)) 定义 S1 结构。 针对一个变量定义或其他类型声明中的全部 S1 使用均为 32 字节对齐。 sizeof(struct S1) 返回 32,并且 S1 在包含四个整数所需的 16 字节之后有 16 个填充字节。 每个 int 成员要求为 4 字节对齐,但它自身结构的对齐声明为 32 字节。 因此,整体为 32 字节对齐。
struct CACHE_ALIGN S1 { // cache align all instances of S1
int a, b, c, d;
};
struct S1 s1; // s1 is 32-byte cache aligned
在此示例中,sizeof(struct S2) 返回 16,它恰好为成员大小的总和,因为这是最大对齐要求的倍数(8 的倍数)。
__declspec(align(8)) struct S2 {
int a, b, c, d;
};
在下面的示例中,sizeof(struct S3) 返回 64。
struct S3 {
struct S1 s1; // S3 inherits cache alignment requirement
// from S1 declaration
int a; // a is now cache aligned because of s1
// 28 bytes of trailing padding
};
在此示例中,注意 a 具有自然类型的对齐方式,在这种情况下为 4 字节。 但是,S1 必须是对齐的 32 字节。 填充的 28 个字节遵循 a,因此 s1 从偏移量 32 开始。 然后,S4 将继承 S1 的对齐要求,因为这是结构中的最大对齐要求。 sizeof(struct S4) 返回 64。
struct S4 {
int a;
// 28 bytes padding
struct S1 s1; // S4 inherits cache alignment requirement of S1
};
以下三个变量声明还使用 __declspec(align(#))。 在每种情况下,变量必须是对齐的 32 字节。 对于数组,数组的基址(而非每个数组成员)是对齐的 32 字节。 使用 __declspec(align(#)) 不会影响每个数组成员的 sizeof 值。
CACHE_ALIGN int i;
CACHE_ALIGN int array[128];
CACHE_ALIGN struct s2 s;
若要对齐数组的每个成员,应使用如下代码:
typedef CACHE_ALIGN struct { int a; } S5;
S5 array[10];
在此示例中,注意对齐结构本身与对齐第一个元素具有相同的效果:
CACHE_ALIGN struct S6 {
int a;
int b;
};
struct S7 {
CACHE_ALIGN int a;
int b;
};
S6 和 S7 具有相同的对齐、分配和大小特性。
在此示例中,a、b、c 和 d 的起始地址的对齐方式分别为 4、1、4 和 1。
void fn() {
int a;
char b;
long c;
char d[10]
}
在堆上分配了内存时,对齐方式取决于所调用的分配函数。 例如,如果你使用 malloc,则该结果取决于操作数大小。 如果 arg >= 8,返回的内存为 8 字节对齐。 如果 arg < 8,所返回内存的对齐方式将为小于 arg 的 2 的一次幂。 例如,如果你使用 malloc(7),则对齐方式为 4 字节。
使用 __declspec(align(#)) 定义新类型
可以使用对齐特性定义类型。
例如,你可以使用对齐值定义 struct ,如下所示:
struct aType {int a; int b;};
typedef __declspec(align(32)) struct aType bType;
现在,aType 和 bType 的大小相同(8 字节),但类型 bType 的变量将是对齐的 32 字节。
在线程本地存储区中对齐数据
使用 __declspec(thread) 特性创建的置于映像中的 TLS 部分中的静态线程-本地存储 (TLS) 与常规静态数据一样适用于对齐。 若要创建 TLS 数据,操作系统需分配 TLS 部分大小的内存并遵循 TLS 部分对齐特性。
此示例说明用于将对齐的数据置于线程本地存储区中的各种方法。
// put an aligned integer in TLS
__declspec(thread) __declspec(align(32)) int a;
// define an aligned structure and put a variable of the struct type
// into TLS
__declspec(thread) __declspec(align(32)) struct F1 { int a; int b; } a;
// create an aligned structure
struct CACHE_ALIGN S9 {
int a;
int b;
};
// put a variable of the structure type into TLS
__declspec(thread) struct S9 a;
对齐如何与数据打包配合使用
/Zp 编译器选项和 pack 杂注对打包结构和联合成员的数据很有用。 此示例说明了 /Zp 和 __declspec(align(#)) 的协作方式:
struct S {
char a;
short b;
double c;
CACHE_ALIGN double d;
char e;
double f;
};
下表列出了各种 /Zp(或 #pragma pack)值下的每个成员的偏移量,并演示了二者的交互方式。
变量 |
/Zp1 |
/Zp2 |
/Zp4 |
/Zp8 |
---|---|---|---|---|
a |
0 |
0 |
0 |
0 |
b |
1 |
2 |
2 |
2 |
c |
3 |
4 |
4 |
8 |
d |
32 |
32 |
32 |
32 |
e |
40 |
40 |
40 |
40 |
f |
41 |
42 |
44 |
48 |
sizeof(S) |
64 |
64 |
64 |
64 |
有关详细信息,请参阅/Zp(结构成员对齐)。
对象的偏移量基于上一个对象的偏移量和当前打包设置,除非该对象具有 __declspec(align(#)) 特性,在此情况下,对齐将基于上一个对象的偏移量和该对象的 __declspec(align(#)) 值。