c/c++ 的内存字节对齐

内存对齐

 

先了解下C/C++基本类型的字节占用情况,

 

 

alignas 关键字

 

 

 

#pragma pack(N)

pragma pack 规定的对齐长度,实际使用的规则是: 

  • 结构(如struct,union,或者class )内部的数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。 
  • 而结构之间整体的对齐,则按照结构中最大的数据成员 和 #pragma pack指定值 之间,较小的那个进行。

也就是说,当#pragma pack的值等于或超过所有数据成员长度的时候,这个值的大小将不产生任何效果。 

再来看个例子:

#include <iostream>
using namespace std;

#define OFFSET(struct_type, member) ((size_t) &((struct_type *) 0)->member)

#pragma pack(4)
struct TestB
{
    int aa;   // 第一个成员,放在[0,3]偏移的位置,
    char a;  // 第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。
    short b;  //第三个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。 
    char c;  //第四个,自身长为1,放在[8]的位置。
};
#pragma pack()

int main() {

    cout<< sizeof(TestB) << endl;    // 12
    cout<< OFFSET(TestB, aa) <<endl;  // 0
    cout<< OFFSET(TestB, a) <<endl;  // 4
    cout<< OFFSET(TestB, b) <<endl;  // 6
    cout<< OFFSET(TestB, c) <<endl;  // 8
}

上面的例子中,这个struct 实际占据的内存空间是9字节,但sizeof返回的是12,原因:

结构之间的对齐,是按照结构内部最大的成员的长度,和#pragma pack规定的值之中较小的一个对齐的。 
所以这个例子中,结构之间对齐的长度是min(sizeof(int), 4),也就是4。 4的倍数且比8(最后一个成员的偏移)大的是12,所以整个结构的占用字节数是12。

上面的例子中,去掉第一个int成员再来看:

#pragma pack(4)

struct TestB
{
//    int aa; 
    char a; // 第一个成员,放在[0]偏移的位置
    short b; // 第二个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[2,3]的位置
    char c; //第三个,自身长为1,放在[4]的位置。

};

#pragma pack()

int main() {
    cout<< sizeof(TestB) << endl;   // 6
//  cout<< OFFSET(TestB, aa) <<endl;
    cout<< OFFSET(TestB, a) <<endl; // 0
    cout<< OFFSET(TestB, b) <<endl; // 2
    cout<< OFFSET(TestB, c) <<endl; // 4

}

这个例子中,结构之间对齐的长度是min(sizeof(short), 4),也就是2。 2的倍数且比4(最后一个成员的偏移)大的是6,所以整个结构的占用字节数是6。

 

换一种修改,只修改pragma的值,

#pragma pack(2)

struct TestB
{
    int aa; // 第一个成员,放在[0,3]
    char a; // 二个成员,自身长1,放在[4]
    short b; // 第三个成员,自身长2,#pragma pack(2),取2,按2字节对齐,所以放在偏移[6,7]的位置
    char c; //第四个,自身长为1,放在[8]的位置。

};

#pragma pack()

int main() {

    cout<< sizeof(TestB) << endl;   // 10
    cout<< OFFSET(TestB, aa) <<endl;    // 0
    cout<< OFFSET(TestB, a) <<endl; // 4
    cout<< OFFSET(TestB, b) <<endl; // 6
    cout<< OFFSET(TestB, c) <<endl; // 8

}

 

 

更多例子:

有数组的情况:

#pragma pack(4)

struct TestB
{
    float a[5];     // 第一个成员,放在[0, 19]
    char b;         // 第二个成员,自身长1,放在[20]
    float c[7];    // 第三个成员,自身长4,#pragma pack(4),取4,按4字节对齐,所以放在偏移[24,51]的位置
    char d;         //第四个,自身长为1,放在[52]的位置。
};

#pragma pack()


int main() {

    cout<< sizeof(TestB) << endl;   // 56
    cout<< OFFSET(TestB, a) <<endl;    // 0
    cout<< OFFSET(TestB, b) <<endl; // 20
    cout<< OFFSET(TestB, c) <<endl; // 24
    cout<< OFFSET(TestB, d) <<endl; // 52

}

 

修改pack参数:

#pragma pack(2)

struct TestB
{
    float a[5];     // 第一个成员,放在[0, 19]
    char b;         // 第二个成员,自身长1,放在[20]
    float c[7];    // 第三个成员,自身长4,#pragma pack(2),取2,按2字节对齐,所以放在偏移[22,49]的位置
    char d;         //第四个,自身长为1,放在[50]的位置。
};

#pragma pack()


int main() {

    cout<< sizeof(TestB) << endl;   // 52
    cout<< OFFSET(TestB, a) <<endl;    // 0
    cout<< OFFSET(TestB, b) <<endl; // 20
    cout<< OFFSET(TestB, c) <<endl; // 22
    cout<< OFFSET(TestB, d) <<endl; // 50

}

 

 

 

参考:

http://www.yebangyu.org/blog/2015/12/30/falsesharing/ 

 

 

posted @ 2022-05-06 18:45  如果的事  阅读(577)  评论(0编辑  收藏  举报