首页 > 学院 > 开发设计 > 正文

关于内存对齐的一些总结

2019-11-06 09:27:33
字体:
来源:转载
供稿:网友
关于内存对齐的一些总结在介绍内存对齐之前,我们需要了解几个关于内存方面的知识     1)#PRagma pack(n)    用途:设定变量以n字节对齐   程序编译器对结构的存储的特殊处理能提高CPU存储变量的速度,但是有时候也带来了一些麻烦,我们也屏蔽掉变量默认的对齐方式,自己可以设定变量的对齐方式。   编译器中提供了#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。2)基本变量在不同系统下的字节数      在X86,32位系统下基于Microsoft、Borland和GNU的编译器,#pragma pack(4),即4字对齐 有如下数据对齐规则:       a、一个char(占用1-byte)变量以1-byte对齐。       b、一个short(占用2-byte)变量以2-byte对齐。       c、一个int(占用4-byte)变量以4-byte对齐。       d、一个long(占用4-byte)变量以4-byte对齐。       e、一个float(占用4-byte)变量以4-byte对齐。       f、一个double(占用8-byte)变量以8-byte对齐。       g、一个long double(占用12-byte)变量以4-byte对齐。       h、任何pointer(占用4-byte)变量以4-byte对齐。           而在64位系统下,#pragma pack(8),即8字对齐 与上面规则对比有如下不同:      a、一个long(占用8-byte)变量以8-byte对齐。      b、一个double(占用8-byte)变量以8-byte对齐      c、一个long double(占用16-byte)变量以16-byte对齐。      d、任何pointer(占用8-byte)变量以8-byte对齐。3)在结构体中成员数据必须满足以下规则    结构体数据对齐,是指结构体内的各个数据对齐。在结构体中的第一个成员的首地址等于整个结构体的变量的首地址,而后的成员的地址随着它声明的顺序和实际占用的字节数递增。为了总的结构体大小对齐,会在结构体中插入一些没有实际意思的字符来填充(padding)结构体。     a、结构体中的第一个成员的首地址即结构体变量的首地址。      b、结构体中的每一个成员的首地址相对于结构体的首地址的偏移量(offset)是该成员数据类型大小(sizeof(该成员))的整数倍。     c、在数据成员完成各自对齐之后,结构(或联合)体本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行,且为它的整数倍。4)以下是我在Ubuntu  x64位下做的demo(因为只有64位,32位的就不验证了@_@)
#include <stdio.h>struct stu1{    char a;    int b;    short c;}stu1;struct stu2{    short c;    char a;    int b;}stu2;struct stu3{    char a;    short c;    int b;}stu3;struct stu4{    char a;    int b;    short c;    double d;    char e;}stu4;int main(int argc, char *argv[]){    printf("stu1.a:%p   stu1.b:%p     stu1.c:%p    sizeof(stu1):%d/n", &stu1.a, &stu1.b, &(stu1.c), sizeof(stu1));    printf("stu2.c:%p   stu2.a:%p     stu2.b:%p    sizeof(stu2):%d/n", &stu2.c, &stu2.a, &(stu2.b), sizeof(stu2));    printf("stu3.a:%p   stu3.c:%p     stu3.b:%p    sizeof(stu3):%d/n", &stu3.a, &stu3.c, &(stu3.b), sizeof(stu3));    printf("stu4.a:%p   stu4.b:%p     stu4.c:%p    stu4.d:%p    stu4.e:%p    sizeof(stu4):%d/n", &stu4.a, &stu4.b, &stu4.c, &stu4.d, &stu4.e, sizeof(stu4));    return 0;}运行结果如下分析stu1:stu1结构体可以看到,a是一个char型,为一个字节,接下来为int b,为整形,四个字节,这时候离结构体首地址(即a的地址)的偏移为4规则b),所以a必须再填充三个字节(从打印的地址也可以看出,即0x6010a9、0x6010aa、0x6010ab),然后short c2个字节,而且距离首地址偏移为6能整除2(short) 所以加起来为4+4+2 = 10字节。这时候肯定很多人疑问了,这不10字节么?怎么打印的是12呢?那么现在我们的规则c起作用了(即结构体本身的对齐),在64位系统下,#pragma pack(8)  为8stu1结构体中最大的类型为int4个字节,所以我们需要在short c后面填充2个字节(从打印地址可以看出,c的首地址为0x6010b0、那么填充的地址应该为0x6010b2、0x6010b3),所以总共的字节数为4+4+2+2 = 12  (字节)。中间两个就不分析了,按着规则去一步一步匹配就行,而且地址我都打印出来了应该好判断,接着分析下stu4。分析stu4:从stu4结构体可以看到(和结构体stu1相似,我特意添加了一个double和char来区分下以及更加好理解规则c),char a,int b,short c,我就不再具体分析,可以看下分析stu1,算到这里我们知道是4+4+2 = 10 字节,接下来是double d,为8字节,到这里我们可以知道a+b+c+d = 4+4+2+8 = 18(这时候注意了,根据规则b,我们知道18是不能整除8的,所以必须填充4字节,为18+6 = 24),所以这时候我们不妨看看打印的地址是否和我们想的一样(short c的首地址为0x601088,占2字节,0x601088~0x601089,从图中我们可以看到d的首地址为0x601090,所以就证实了我们说的需要填充6个字节,即0x60108a~0x60108f),最后加上char e ,1个字节,为25字节,这时候我想很多人应该知道了,根据我们的规则c,在64位系统下,#pragma pack(8)  为8stu4结构体中最大的类型为double8个字节,所以我们需要在char e后面在填充7个字节,即0x601099~0x6010af,所以总共的字节数为1+3(填充)+4+2+6(填充)+8+1+7(填充) = 32(字节)。由于上周看到网上很多对内存对齐都讲的很模糊,所以决定自己玩一遍,毕竟身为程序猿各种玩就是@_@,周末总结下希望能帮助到别人!!!该睡觉了,还要上班明天......
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表