C++结构体对齐详解

发布时间 2023-05-30 12:10:43作者: 白菜茄子
  • 内存对齐是一种提高内存访问速度的策略,CPU在访问未对齐的内存可能需要经过两次的内存访问,而经过内存对齐一次就可以了
cout<<"char:"<<sizeof(char)<<endl;
cout<<"int:"<<sizeof(int)<<endl;
cout<<"short:"<<sizeof(short)<<endl;
cout<<"float:"<<sizeof(float)<<endl;
cout<<"double:"<<sizeof(double)<<endl;

结构体对齐原则

  1. 数据对齐原则:结构的数据成员每个数据成员存储的起始位置要从该成员大小的整数倍开始,第一个成员都是从0开始(因为0是任何整数的整数倍)
  2. 结构体作为成员:如果有一个结构里面有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍开始存储
  3. 结构体的总大小,必须是内部最大成员的整数倍
struct A1{
    char a1;
    int a2;
    char a3;
};

struct A2{
    char a1;
    char a2;
    int a3;
};

struct A3{
    char a1;
    int a2;
    double a3;
    char a4;
};

struct A4{
    char a1;
    struct A3 a2;
    int a3;
    double a4;
};
int main(){
    cout<<"A1:"<<sizeof(A1)<<endl;
    cout<<"A2:"<<sizeof(A2)<<endl;
    cout<<"A3:"<<sizeof(A3)<<endl;
    cout<<"A4:"<<sizeof(A4)<<endl;
}

上面代码的输出结果未

A1:12
A2:8
A3:24
A4:48

A1

  • a1在offset为0的地方(长度为1,范围为0-0)
  • a2在offset为4的地方(长度为4,范围为4-7 )
  • a3在offset为8的地方(长度为1,范围为8-8)
  • 此时大小为9,但是使用规则3,结构体大小应该为其内部最大成员的整数倍,也就是4的整数倍,所以此时A1的大小为12,则此时a3后面三个字节被填充补齐,所以此时A1的范围是0-11
    image

A2

  • a1在offset为0的地方(长度为1,范围是0-0)
  • a2在offset为1的地方(长度为1,范围是1-1)
  • a3在offset为4的地方(长度为4,范围是4-7)
  • 此时A2的大小为8
    image

A3

  • a1在offset为0的地方(长度为1,范围是0-0)
  • a2在offset为4的地方(长度为4,范围是4-7)
  • a3在offset为8的地方(长度为8,范围是8-15)
  • a4在offset为16的地方(长度为1,范围是16-16)
  • 此时结构体大小为17,根据规则3,此时结构体大小应该为8的整数倍,也就是24,所以A3的大小为24
    image

A4

  • a1在offset为0的地方(长度为1,范围是0-0)
  • 根据规则2,因为A3的成员最大为double8个字节,所以a2的offset为8(长度为24,范围是8-31)
  • a3在offset为32的地方(长度为4,范围是32-35)
  • a4在offset为40(长度为8,范围是40-47)
  • 所以结构体A4的大小为48
    image

上面解释了四个结构体的成员分布,并且将其内存分布使用图片的形式进行一个展现,我们从A1 A2可以知道成员变量的不同定义顺序会导致其结构体大小的不同

#pragma pack

  • #pragma pack(show) 会在编译阶段给出一个警告,说明当前对齐字节数,默认为8

  • #pragma pack() 将对齐字节数恢复成默认的对齐字节数

  • #pragma pack(n) n只能是1 2 4 8 16中的任意值,表示设置当前对齐字节数

  • #pragma pack(push) 将当前对齐字节数压入栈顶,并设这这个值为新的对齐字节数,也就是当前对齐字节数并没有改变

  • #pragma pack(push,n) 将当前对齐字节数压入栈顶,并设置n为新的对齐字节数

  • #pragma pack(pop) 会弹出栈顶对齐字节数,并设置为新的内存对齐字节数

  • #pragma pack(pop,n)弹出栈顶并直接丢弃,设置n为其新的内存对齐字节数

    • 在之前上面四个结构体前面加上#pragma pack(1),则输出变为了
A1:6
A2:6
A3:14
A4:27

这是因为将默认对齐字数改为1之后,在使用规则1 2 3进行计算的时候,都让1和之前大小取最小值,因为之前默认为8,所以可以获得之前的结果

A1

  • a1在offset为0的地方(长度为1,范围0-0)
  • a2在offset为1的地方(长度为4,范围为1-4),这是因为int的大小为4,之前默认对齐字节数为8,所以取4 8的最小值为4,将其放在4的整数倍上,而此时取值为4 1中的最小值为1,所以将其放在1的整数倍上
  • a3在offset为5的地方(长度为1,范围是5-5)
  • 之前默认对齐字数为8的时候,取得是数据成员变量最大值4和默认对齐字数8得最小值4,为结构体整体对齐得整数倍,而此时变为了4和1得最小值1,所以此时结构体应该是1的整数倍,所以结构体大小为6

其他三个结构体也是这么进行分析