在 Visual Studio 2015 及更高版本中,使用 说明符alignas (C++ 11) 来控制对齐效果。 有关详细信息,请参阅对齐。
Microsoft 专用
使用 __declspec(align(#)) 准确控制用户定义的数据的对齐方式(例如,函数中的静态分配或自动数据)。
语法
__declspec(align(#) )声明符
注解
编写使用最新的处理器指令的应用程序会引入一些新的约束和问题。 许多新指令需要与 16 字节边界对齐的数据。 将常用数据与处理器的缓存行大小对齐,这可以提高缓存性能。 例如,如果你定义一个大小小于 32 字节的结构,则可能需要 32 字节对齐,以确保有效缓存该结构类型的对象。
# 是对齐值。 有效项为介于 1 和 8192(字节)之间的 2 的整数幂,如 2、4、8、16、32 或 64。
declarator 是你声明为对齐的数据。
有关如何返回该类型对齐要求的 size_t 类型的值的信息,请参阅 alignof。 有关如何在面向 64 位处理器时声明未对齐指针的信息,请参阅 __unaligned。
在定义 __declspec(align(#))、struct 或 union 时或声明变量时,可以使用 class。
编译器不保证或不尝试保留复制过程中或数据转换操作中数据的对齐特性。 例如,memcpy 可将使用 __declspec(align(#)) 声明的结构复制到任何位置。 普通分配器(例如,malloc、C++ operator new 和 Win32 分配器)通常返回未对齐 __declspec(align(#)) 结构或结构数组的内存。 若要保证复制或数据转换操作的目标正确对齐,请使用 _aligned_malloc。 你也可以编写自己的分配器。
不能指定函数参数的对齐方式。 当在堆栈上按值传递具有对齐属性的数据时,由调用约定控制其对齐方式。 如果数据对齐在所调用函数中很重要,请在使用前将参数复制到正确对齐的内存中。
如果没有 __declspec(align(#)),编译器将根据目标处理器和数据的大小将数据对齐到自然边界,在 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(#)) 只能增大对齐限制。
有关详细信息,请参阅:
align 示例
以下示例说明 __declspec(align(#)) 如何影响数据结构的大小和对齐方式。 这些示例假定下列定义:
#define CACHE_LINE 32
#define CACHE_ALIGN __declspec(align(CACHE_LINE))
在此示例中,通过使用 S1 定义 __declspec(align(32)) 结构。 针对一个变量定义或其他类型声明中的全部 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 字节。 使用 sizeof 不会影响每个数组成员的 __declspec(align(#)) 值。
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,则该结果取决于操作数大小。 如果参数 = 8,返回的内存为 8 字节对齐>。 如果参数 8,所返回内存的对齐方式将为小于参数的 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;
align 如何与数据打包一起工作
/Zp 编译器选项和 pack pragma 对打包结构和联合成员的数据很有用。 此示例说明 /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(#)) 值。
结束 Microsoft 专用