C++中的内存对齐问题:alignof和alignas的用法

内存对齐和alignas

对齐(alignment)就是保证某个变量存储位置的首地址满足某个整数的倍数,通过设定特定的整数倍对齐位置可以提升处理器的性能,以及执行要求特定对齐的指令(例如SSE需要对齐为16bytes,AVX需要对齐到32bytes)。按照16对齐意味着,内存地址是16的整数倍。

alignas可以强制对齐的字节为2的n次方(n=1,2,3,4,5.....),下面通过代码来测试。

代码1:

#include 
#define CHECK_ALIGN(ptr,alignment) \
do{\
size_t status \
    =reinterpret_cast(ptr) % alignment; \
    if (status != 0) { \
        std::cout << "It is not alligned! " << std::endl; \
    } \
}while(0)
int main() {
    long long a[4];
    int b[8];
    CHECK_ALIGN(a, 64);
    CHECK_ALIGN(b, 1024);
    std::cout << a << std::endl;
    std::cout << b << std::endl;
    return 0;
}

这种情况下,大概率是对不齐的,通常会打印(当然也可能出现对齐情况,但概率很低):

It is not alligned! 
It is not alligned! 
0x7fff77c99490
0x7fff77c994b0

如果我们加上alignas:

#include 
#define CHECK_ALIGN(ptr,alignment) \
do{\
size_t status \
    =reinterpret_cast(ptr) % alignment; \
    if (status != 0) { \
        std::cout << "It is not alligned! " << std::endl; \
    } \
}while(0)
int main() {
    alignas(64) long long a[4];
    alignas(1024) int b[4];
    CHECK_ALIGN(a, 64);
    CHECK_ALIGN(b, 1024);
    std::cout << a << std::endl;
    std::cout << b << std::endl;
    return 0;
}

就可以保证绝对的对齐,打印结果:

0x7ffe38fdb7c0
0x7ffe38fdb400

alignof

了解了alignment的概念,我们来介绍关键字alignof,alignof返回的是对齐要求,如果没有通过alignas指定,则会返回满足对齐条件下的最小对齐长度,先给个例子:

#include 
#define CHECK_ALIGN(ptr,alignment) \
do{\
size_t status \
    =reinterpret_cast(ptr) % alignment; \
    if (status != 0) { \
        std::cout << "It is not alligned! " << std::endl; \
    } \
}while(0)
struct Foo {
  char a;
  int b;
  int c;
  long long d;
};
struct alignas(64) Foo1 {
  char a;
  int b;
  int c;
  long long d;
};
struct Foo2 {
  char a;
  long long d;
};
int main() {
    alignas(64) long long a[4];
    alignas(1024) int b[4];
    CHECK_ALIGN(a, 64);
    CHECK_ALIGN(b, 1024);
    Foo f1;
    alignas(128) Foo f2;
    std::cout << alignof(Foo) << std::endl;
    std::cout << alignof(Foo1) << std::endl;
    std::cout << &f1 << std::endl;
    std::cout << sizeof(f1) << std::endl;
    std::cout << alignof(f1) << std::endl;
    std::cout << &f2 << std::endl;
    std::cout << sizeof(f2) << std::endl;
    std::cout << alignof(f2) << std::endl;
    std::cout << alignof(a) << std::endl;
    std::cout << alignof(b) << std::endl;
    std::cout << alignof(int) << std::endl;
    std::cout << alignof(int*) << std::endl;
    std::cout << alignof(Foo2) << std::endl;
    return 0;
}

输出结果:

8
64
0x7ffcf0abc7e0
24
8
0x7ffcf0abc780
24
128
64
1024
4
8
8

其他比较容易理解,重点说一下结构体的例子,对于结构体必须要保证每个变量都要满足对齐要求,那么只要要求最严格的满足,其他也就满足了,所以会返回要求最严的变量的对齐要求,进一步解释,对于结构体Foo,我们看以下不同起始地址下,那么各个变量的起始地址为:

struct Foo { // 0 1 2 3 4 5 6 7 不同的起始地址
  char a; // 4 5 6 7 8 9 10 11 a的起始地址
  int b; //  8 9 10 11 12 13 14 15 b的起始地址
  int c; // 12 13 14 15 16 17 18 19 c的起始地址
  long long d; // 20 21 22 23 24 25 26 27 d的起始地址
};

再看下结构体Foo2:

struct Foo2 { // 0 起始地址
  char a; // 8 a的起始地址
  long long d; // 16 d的起始地址
};

注意这里,机构体本身有自动对齐的功能,后面接long long的话,char a位置会自动补为8字节。当然可以通过#pragma pack来控制对齐要求,例如:

#include
#pragma pack(2)
struct Foo2 {
  char a;
  long long d;
};
int main() {
    std::cout << sizeof(Foo2) << std::endl;
    std::cout << alignof(Foo2) << std::endl;
    return 0;
}

输出结果:

10
2

你可能感兴趣的:(C++编程,c++,开发语言)