为什么存在内存对齐

1.平台原因(移植原因)

不是所有的硬件都能任意访问任意地址上的任意数据的,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出异常

2.性能原因

数据结构(尤其是栈)应该尽可能的在自然边界上对齐,为了访问未对齐的内存,CPU需要访问内存2次;访问对齐后的内存,CPU只需要访问内存1次。

在Windows的VS中默认对齐值为8

Linux中(GCC编译器)默认对齐值为4

在内存中我们一般读取数据不是按内存来读取,一般都是按内存块来读取。

未对齐的情况下,当需要访问int类型的数据时,需要CPU访问2次内存块(内存块1和内存块2)

对齐的情况下,当访问int类型的数据时,只需要CPU访问1次内存块(内存块2)即可

是一种空间换时间的做法

结构体内存对齐规则

  • 第一个成员在结构体变量偏移量为0的地址

  • 有效对齐值为Min(编译器默认对齐值,结构体中成员数据类型中占用内存最大值)

  • 其他成员要对齐到Min(有效对齐值,该成员数据类型的内存大小)的整数倍

  • ⚠结构体总大小为有效对齐值的整数倍

  • 如果嵌套结构体,有效对齐值为Min(编译器默认对齐值,Max嵌套结构体中成员数据类型中占用内存最大值,结构体中成员一数据类型占用内存、结构体中成员二数据类型占用内存……结构体中成员N数据类型占用内存嵌套结构体中成员数据类型中占用内存最大值,结构体中成员一数据类型占用内存、结构体中成员二数据类型占用内存……结构体中成员N数据类型占用内存

  • 如果嵌套共同体,有效对齐值为Min(编译器默认对齐值,Max( Max(共同体内成员一、共同体内成员二……共同体内成员N),结构体中成员一数据类型占用内存、结构体中成员二数据类型占用内存……结构体中成员N数据类型占用内存))

  • 共用体所占内存大小:Max(共同体内成员一、共同体内成员二……共同体内成员N)

举例

​#pramga packnn​​告诉编译器字节对齐方式为n字节对齐

#pramga pack(8)
struct s1{
    char a;
    int b;
    char c;
};

结构体中最大的成员数据类型占用4字节,有效对齐值为Min8,48,4=4;char为第一个成员,应在结构体偏移量为0的地址,占用1字节;int占用4字节,要对齐在Min8,48,4=4的整数倍位置,即4的位置4,5,6,74,5,6,7;char占用1字节,要对齐在Min8,18,1=1的整数倍位置,即8位置;因为结构体总大小为有效对齐值的整数倍,所以结构体的大小应为120 110 11

#pramga pack(8)
struct s1{
    char a;
    int b;
    char c;
};
struct s2{
    char a1;
    struct s1 S1;        //嵌套结构体
    double d;
};

结构体s1中最大数据类型结构为int占用4字节,结构体s2的有效对齐值为Min8,Max(4,1,88,Max(4,1,8)=8;char为第一个成员,应在结构体偏移量为0的地址,占用1字节;嵌套结构体s1占用12字节占用12字节要对齐在Min8,48,4=4的整数倍,即4(4-15);double占用8字节,要对齐在Min(8,8)的整数倍位置,即16(16-23);因为结构体总大小为有效对齐值的整数倍;此时结构体内存大小为24,满足要求。

#pramga pack(4)
struct s1{
    char a;
    int b;
    union w{
        int c;
        double d;
    }w;
    short e;
};

共同体w中最大数据类型占用内存为Max(4,8)=8;结构体s1的有效对齐值为Min(4,Max8,1,4,28,1,4,2)= 4;char为第一个成员,应在结构体偏移量为0的地址,占用1字节;int占用4字节,要对齐在Min4,44,4=4的整数倍位置,即4的位置4,5,6,74,5,6,7;共同体w占Max4,84,8=8字节,要对齐在Min4,124,12=4的整数倍位置,即8的位置8−158−15;short占2字节,要对齐在Min424,2=2的整数倍为止,即1616−1716−17;因为结构体总大小为有效对齐值的整数倍;所以结构体s1的大小需要补位到4的整数倍,即20字节。