还剩2页未读,继续阅读
文本内容:
结构体中数据的内存对齐问题C当在中定义了一个结构类型时,它的大小是否等于各字段大小之和?编译器将如何C field在内存中放置这些字段?对结构体的内存布局有什么要求?而我们的程序又能否依赖ANSI C这种布局?这些问题或许对不少朋友来说还有点模糊,那么本文就试着探究它们背后的秘密首先,至少有一点可以肯定,那就是保证结构体中各字段在内存中出现的位置是ANSI C随它们的声明顺序依次递增的,并且第一个字段的首地址等于整个结构体实例的首地址比如有这样一个结构体struct vector/int xy,z;f}s;int%*q,*r;struct vector*ps;p=s.x;q-s.y;r-s.z;ps-s;assertpq;assertpr;assertqr;assert int^ps==p;〃上述断言一定不会失败这时,有人可能会问”标准是否规定相邻字段在内存中也相邻?\没有做出保ANSI C证,你的程序在任何时候都不应该依赖这个假设那这是否意味着我们永远无法勾勒出一幅更清晰更精确的结构体内存布局图?当然不是我们先来了解一下内存对齐的问题许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数通常它为或的倍数,这就是所谓的内存对齐,而这个则被称为k48k该数据类型的对齐模数当一种类型的对齐模数与另一种类型的对齐模alignment modulusoS T数的比值是大于的整数,我们就称类型的对齐要求比强严格,而称比弱宽松1S TT S这种强制的要求一来简化了处理器与内存之间传输系统的设计,二来可以提升读取数据的速度比如这么一种处理器,它每次读写内存的时候都从某个倍数的地址开始,一次读出或写入个88字节的数据,假如软件能保证类型的数据都从倍数地址开始,那么读或写一个double8double类型数据就只需要一次内存操作否则,我们就可能需要两次内存操作才能完成这个动作,因为数据或许恰好横跨在两个符合对齐要求的字节内存块上某些处理器在数据不满足对齐要8求的情况下可能会出错,但是的架构的处理器则不管数据是否对齐都能正确工作Intel IA32不过奉劝大家,如果想提升性能,那么所有的程序数据都应该尽可能地对齐平台Intel Win32下的微软编译器()在默认情况下采用如下的对齐规则任何基本数据类型C cl.exe for80x86T的对齐模数就是的大小,即)比如对于类型(字节),就要求该类型数据T sizeoffTdouble8的地址总是的倍数,而类型数据(字节)则可以从任何一个地址开始下的8char1Linux GCC奉行的是另外一套规则(在资料中查得,并未验证,如错误请指正):任何字节大小的数据类2型(比如)的对齐模数是而其它所有超过字节的数据类型(比如)都以short2,2long,double4为对齐模数现在回到我们关心的上来规定一种结构类型的大小是它所有字段的大小struct ANSIC以及字段之间或字段尾部的填充区大小之和这就是为了使结构体字段满足内存对齐要求而额外分配给结构体的空间那么结构体本身有什么对齐要求吗?有的,标准规定结构体类ANSIC型的对齐要求不能比它所有字段中要求最严格的那个宽松,可以更严格(但此非强制要求,VC
7.1就仅仅是让它们一样严格)我们来看一个例子(以下所有试验的环境是Intel Celeron
2.4G内存对齐编译选项是“默认”,即不指定+WIN2000PRO+vc
7.1,与选项)/Zp/packtypedef structmsl{char a;int h;JMS1;假设按如下方式内存布局(本文所有示意图中的内存地址从左至右递增)MSIa t02468因为中有最强对齐要求的是字段()所以根据编译器的对齐规则以及标准,MSI bint,ANSIC对象的首地址一定是(类型的对齐模数)的倍数那么上述内存布局中的字段能满MSI4int b足类型的对齐要求吗?嗯,当然不能如果你是编译器,你会如何巧妙安排来满足的int CPU癖好呢?呵呵,经过毫秒的艰苦思考,你一定得出了如下的方案1这个方案在与之间多分配了个填充字节,这样当整个对象首地址a b3padding struct满足字节的对齐要求时,字段也一定能满足血型的字节对齐规定那么显然就4b4sizeofMSl应该是而字段相对于结构体首地址的偏移就是非常好理解.,现在我们把8,b4中的字段交换一下顺序MSItypedef structms2{int a;char b;}MS2;或许你认为比的情况要简单,它的布局应该就是MS2MSI因为对象同样要满足字节对齐规定,而此时的地址与结构体的首地址相等,所以MS24a它一定也是字节对齐分析得有道理,可是却不全面让我们来考虑一下定义一个类型4MS2的数组会出现什么问题标准保证,任何类型包括自定义结构类型的数组所占空间的大小C一定等于一个单独的该类型数据的大小乘以数组元素的个数换句话说,数组各元素之间不会有空隙按照上面的方案,一个数组的布局就是MS2array当数组首地址是字节对齐时,也是字节对齐,可是呢4array[l].a4array
[2].a….呢?可见这种方案在定义结构体数组时无法让数组中所有元素的字段都满足array
[3].a对齐规定,必须修改成如下形式:b4现在无论是定义一个单独的变量还是数组,均能保证所有元素的所有字段都满MS2MS2足对齐规定那么仍然是而的偏移为的偏移是sizeofMS28,a0,b4好的,现在你已经掌握了结构体内存布局的基本准则,尝试分析一个稍微复杂点的类型吧typedef structms3char a;short h;double c;MS3;我想你一定能得出如下正确的布局图:a bc0246810121416等于字段应从偶数地址开始,所以的后面填充一个字节,而sizeofshort2,b asizeofdouble等于字段要从倍数地址开始,前面的、字段加上填充字节已经有所以后面再8,c8a b4bytes,b填充个字节就可以保证字段的对齐要求了等于的偏移是的偏移是4c sizeofMS316,b2,c8接着看看结构体中字段还是结构类型的情况typedef structms4char a;MS3b;}MS4;中内存要求最严格的字段是那么类型数据的对齐模数就与的一致为MS3c,MS3double8,字段后面应填充个字节,因此的布局应该是a7MS4024681012141618202224显然,等于的偏移等于sizeofMS424,b8在实际开发中,我们可以通过指定编译选项来更改编译器的对齐规则比如指定/Zp中可以是、、、、就是告诉编译器最大对齐模数是在这种情况下,所/ZpnVC
7.1n124816n有小于等于字节的基本数据类型的对齐规则与默认的一样,但是大于个字节的数据类型的n n对齐模数被限制为事实上,的默认对齐选项就相当于仔细看看对这个选n VC
7.1/Zp8MSDN项的描述,会发现它郑重告诫了程序员不要在和平台上用和选项,也不要MIPS Alpha/Zpl/Zp2在位平台上指定和改变编译器的对齐选项,对照程序运行结果重新分析上面种16/Zp4/Zp84结构体的内存布局将是一个很好的复习。
个人认证
优秀文档
获得点赞 0