为什么要进行内存对齐?

​​​​​​​为什么需要字节对齐?_zkf11387的博客-CSDN博客_字节对齐的好处

上文中参考全面的解释了字节对齐这一问题,自己简单总结一下:

内存字节对齐目的: 提高CPU访问效率;

64位系统默认进行8字节对齐,32位系统默认进行4自己对齐;

基本数据类型地址是其长度整数倍就可实现内存对齐;

数组中对第一个地址进行对齐后,就可实现内部地址对齐;

联合体对其内部最长元素进行对齐,就可以实现地址对齐;

结构体地址对齐需要对其内部所有元素进行对齐,如下是结构体对齐规则:

1. 数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的偏移量是( #pragma pack 指定的数值和这个数据成员自身长度两者之间的较小值)的整数倍。

2. 结构体的整体对齐规则:在数据成员按照 #1 完成各自对齐之后,结构体本身也要进行对齐。对齐会将结构体的大小增加为( #pragma pack 指定的数值和结构体最大数据成员长度)中较小那个值的整数倍。

例如:

typedef struct test {
    char a;//1字节
    int b; //4字节
    double c;  //8字节
    char d[11];//11字节
}Test;//数据成员

a的地址为0,则b的地址如果连续分配的话应该为1,但是由于内存对齐的要求,b的地址的偏移量应为4的整数倍,应为4,b占据[4,5,6,7] ,则c默认应从8地址开始,则相对于a的偏移量为8,满足要求,则c占据[8,9,10,11,12,13,14,15]字节位置,d从默认从16字节位置开始,d的长度是1,地址偏移量应为1的整数倍,现在16是1的16倍,所以地址从16开始是合理的,则d占据的空间是[16~27],根据规则2中描述结构体成员大小应扩充为32。

#include <iostream>

typedef struct test1 {
    char a;//1字节
    int b; //4字节
    double c;  //8字节
    char d[11];//11字节
}Test;//数据成员

int main(int argc, const char* argv[]) {

    Test t1;
    //默认对齐系数8,即#pragma pack(8)
    //char a,     1<8按1对齐,offset=0            [0]
    //int b,      4<8按4对齐,char a占到[0],int b应该从地址1开始排, 地址1不是对齐数4的倍数,所以int b的首地址是从4的最小倍数开始,所以offset=4,       [4...7];
    //double c,   8=8按8对齐,int b已经占到[4...7],double c从地址8开始排,地址8是对齐数8的倍数,所以double c的首地址是从8的最小倍数开始,offse=8             [8...15]
    //char d[11], 1<8按1对齐,double c已经占到[8...15], char d[11]从地址16开始,地址16是对齐数1的倍数,所以char d的,offset=16,          存储位置[16...26]
    //最后为27位,又因为27不是内部最大成员中double 8位字节的倍数,所以补齐为32)
    printf(" %lu\n %p\n %p\n %p\n %p\n %p\n", sizeof(t1), &t1 , &t1.a, &t1.b, &t1.c, &t1.d);
    
    system("pause");
    return 0;
}

输出结果为:

 对于结构体内部仍有结构体的情况,内存对齐规则按照以下规则进行执行

数据成员为结构体:如果结构体的数据成员还为结构体,则该数据成员的“自身长度”为其内部最大元素的大小。(struct a 里存有 struct b,b 里有char,int,double等元素,那 b “自身长度”为 8)

#include <iostream>

typedef struct test1 {
    char a;//1
    int  b;//4
    double c;//8
    char d[11];//11
}Test1;

typedef struct test2 {
    char a;//1
    int b; //4
    double c;//8
    char d[11];//11
    Test1 t1;
}Test2;

int main(int argc, const char* argv[]) {

    Test1 t1;
    //默认对齐系数8,即#pragma pack(8)
    //char a,     1<8按1对齐,offset=0            [0]
    //int b,      4<8按4对齐,char a占到[0],int b应该从地址1开始排, 地址1不是对齐数4的倍数,所以int b的首地址是从4的最小倍数开始,所以offset=4,       [4...7];
    //double c,   8=8按8对齐,int b已经占到[4...7],double c从地址8开始排,地址8是对齐数8的倍数,所以double c的首地址是从8的最小倍数开始,offse=8             [8...15]
    //char d[11], 1<8按1对齐,double c已经占到[8...15], char d[11]从地址16开始,地址16是对齐数1的倍数,所以char d的,offset=16,          存储位置[16...26]
    //最后为27位,又因为27不是内部最大成员中double 8位字节的倍数,所以补齐为32)
    printf(" %lu\n %p\n %p\n %p\n %p\n %p\n", sizeof(t1), &t1 , &t1.a, &t1.b, &t1.c, &t1.d);

    
    Test2 t2;
    //默认对齐系数8,即#pragma pack(8)
    //char a,     1<8按1对齐,offset=0            [0]
    //int b,      4<8按4对齐,char a占到[0],int b应该从地址1开始排, 地址1不是对齐数4的倍数,所以int b的首地址是从4的最小倍数开始,所以offset=4,       [4...7];
    //double c,   8=8按8对齐,int b已经占到[4...7],double c从地址8开始排,地址8是对齐数8的倍数,所以double c的首地址是从8的最小倍数开始,offse=8             [8...15]
    //char d[11], 1<8按1对齐,double c已经占到[8...15], char d[11]从地址16开始,地址16是对齐数1的倍数,所以char d的,offset=16,          存储位置[16...26]
    //t1          8=8按8对齐,自身大小为8,则使用32开始地址,ti的占用大小为[32.......64]
    //最后为27位,又因为27不是内部最大成员中double 8位字节的倍数,所以补齐为32)
    printf(" %lu\n %p\n %p\n %p\n %p\n %p\n %p\n", sizeof(t2), &t2, &t2.a, &t2.b, &t2.c, &t2.d, &t2.t1);

    system("pause");
    return 0;
}

如何更改内存对齐策略?

#pragma pack(2)

#include <iostream>

typedef struct test1 {
    char a;//1
    int  b;//4
    double c;//8
    char d[11];//11
}Test1;

int main(int argc, const char* argv[]) {

    Test1 t1;
    //默认对齐系数2,即#pragma pack(2)
    //char a,     1<2按1对齐,offset=0            [0]
    //int b,      2<4按2对齐,char a占到[0],int b应该从地址1开始排, 地址1不是对齐数2的倍数,所以int b的首地址是从2的最小倍数开始,所以offset=2,       [2...5];
    //double c,   2<8按2对齐,int b已经占到[2...5],double c从地址6开始排,地址6是对齐数2的倍数,所以double c的首地址是从2的最小倍数开始,offse=6             [6...13]
    //char d[11], 1<2按1对齐,double c已经占到[6...13], char d[11]从地址14开始,地址14是对齐数2的倍数,所以d的,offset=14,          存储位置[14...25]
    //共26位,又因为26不是内部最大成员中double 8位字节的倍数,所以补齐为32)
    printf(" %lu\n %p\n %p\n %p\n %p\n %p\n", sizeof(t1), &t1 , &t1.a, &t1.b, &t1.c, &t1.d);

    system("pause");
    return 0;
}

更改为2字节对齐后,输出为:

 可见,字节调整为2字节对齐后内存占用减小了,但是会增加CPU的访问时间。

引出问题:结构体相等的比较为什么不能使用memcmp函数?

由于编译器对结构体进行了内存对齐操作,对于结构体中补齐的内存地址,编译器进行了随机值填充,导致这部分内存空间是无法进行比较,而memcmp是对全内存空间进行比较,所以出现问题,两个结构体的比较需要使用符号重载函数。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值