保持同步

C + + 的低级功能之一是能够指定内存中对象的精确对齐方式,以最大限度利用特定的硬件体系结构。 默认情况下,编译器会根据大小值对齐类和结构成员:boolchar 在 1 字节边界上对齐,short 在 2 字节边界上对齐,intlongfloat 在 4 字节边界上对齐,long longdoublelong double 在 8 字节边界上对齐。

在大多数情况下,你永远无需注意对齐方式,因为默认对齐方式已经是最佳的。 但是,在某些情况下,你可以通过指定数据结构的自定义对齐方式,获得显著的性能提升或节约内存。 在 Visual Studio 2015 之前,可以使用 Microsoft 专用关键字 __alignof__declspec(align) 来指定大于默认值的对齐方式。 从 Visual Studio 2015 开始,应使用 C++11 标准关键字 alignofalignas 以获得最强大的代码可移植性。 新关键字实质上以与 Microsoft 专用扩展相同的方式运行。 这些扩展的文档也适用于这些新关键字。 有关详细信息,请参阅 alignof 运算符alignas 说明符对齐。 C++ 标准不指定用于在小于目标平台编译器默认值的边界上对齐的装箱行为,因此在这种情况下,你仍需要使用 Microsoft #pragma pack

使用 aligned_storage 类 为具有自定义对齐方式的数据结构分配内存。 aligned_union 类用于指定与非平凡构造函数或析构函数联合时的对齐方式。

对齐方式和内存地址

对齐方式是内存地址的一个属性,表示为数字地址对 2 的幂次方取模。 例如,地址 0x0001103F 对 4 取模为 3。 该地址对齐到 4n+3,其中 4 表示选择的 2 的幂次方。 地址的对齐方式取决于选择的 2 的幂次方。 相同的地址对 8 取模为 7。 如果一个地址的对齐方式是 Xn+0,它将对齐到 X。

CPU 执行作用于内存中所存储数据的指令。 数据在内存中用地址标识。 单个基准也具有大小。 如果一个基准的地址对齐到其大小,则称它为自然对齐。 否则,称为未对齐。 例如,如果地址用于标识其采用 8 字节对齐,则自然对齐 8 字节浮点基准。

数据对齐的编译器处理

编译器尝试以防止数据未对齐的方式分配数据。

对于简单的数据类型,编译器将分配是数据类型的大小(以字节为单位)的倍数的地址。 例如,编译器将地址分配给类型为 long 且是 4 的倍数的变量,并将地址的最底部 2 位设置为零。

编译器还以自然对齐结构的每一个元素的方式填充结构。 来看看下面代码示例中的结构 struct x_

struct x_
{
   char a;     // 1 byte
   int b;      // 4 bytes
   short c;    // 2 bytes
   char d;     // 1 byte
} bar[3];

编译器填充此结构以自然强制实施对齐方式。

下面的代码示例展示了编译器如何将填充的结构置于内存中:

// Shows the actual memory layout
struct x_
{
   char a;            // 1 byte
   char _pad0[3];     // padding to put 'b' on 4-byte boundary
   int b;            // 4 bytes
   short c;          // 2 bytes
   char d;           // 1 byte
   char _pad1[1];    // padding to make sizeof(x_) multiple of 4
} bar[3];

两个声明都将 sizeof(struct x_) 作为 12 个字节返回。

第二个声明包括两个填充元素:

  1. char _pad0[3],对齐 4 字节边界上的 int b 成员。
  2. char _pad1[1] 用于在 4 字节边界上对齐结构 struct _x bar[3]; 的数组元素。

填充以允许自然访问的方式对齐 bar[3] 的元素。

下面的代码示例展示了 bar[3] 的数组布局:

adr offset   element
------   -------
0x0000   char a;         // bar[0]
0x0001   char pad0[3];
0x0004   int b;
0x0008   short c;
0x000a   char d;
0x000b   char _pad1[1];

0x000c   char a;         // bar[1]
0x000d   char _pad0[3];
0x0010   int b;
0x0014   short c;
0x0016   char d;
0x0017   char _pad1[1];

0x0018   char a;         // bar[2]
0x0019   char _pad0[3];
0x001c   int b;
0x0020   short c;
0x0022   char d;
0x0023   char _pad1[1];

alignofalignas

alignas 说明符是一种可移植的 C++ 标准方法,用于指定变量和用户定义类型的自定义对齐方式。 alignof 运算符同样也是一种标准的、可移植的方法,可以获取指定类型或变量的对齐方式。

示例

你可以在类(结构或联合)或者单个成员上使用 alignas。 遇到多个 alignas 说明符时,编译器会选择其中值最大的一个。

// alignas_alignof.cpp
// compile with: cl /EHsc alignas_alignof.cpp
#include <iostream>

struct alignas(16) Bar
{
    int i;       // 4 bytes
    int n;      // 4 bytes
    alignas(4) char arr[3];
    short s;          // 2 bytes
};

int main()
{
    std::cout << alignof(Bar) << std::endl; // output: 16
}

另请参阅

数据结构对齐方式