为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

结构体成员的内存分布与对齐

2017-12-19 11页 doc 26KB 13阅读

用户头像

is_511210

暂无简介

举报
结构体成员的内存分布与对齐结构体成员的内存分布与对齐 我们先看一道IBM和微软的笔试题: IBM笔试题: struct{ short a1; short a2; short a3; }A; struct{ long a1; short a2; }B; sizeof( A)=6, sizeof(B)=8,为什么, 注:sizeof(short)=2,sizeof(long)=4 微软笔试题: struct example1 { short a ; long b; }; struct example2 { cha...
结构体成员的内存分布与对齐
结构体成员的内存分布与对齐 我们先看一道IBM和微软的笔试: IBM笔试题: struct{ short a1; short a2; short a3; }A; struct{ long a1; short a2; }B; sizeof( A)=6, sizeof(B)=8,为什么, 注:sizeof(short)=2,sizeof(long)=4 微软笔试题: struct example1 { short a ; long b; }; struct example2 { char c; example1 struct1; short e; }; int main(int argc, char* argv[]) { example2 e2; int d=(unsigned int)&e2.struct1-(unsigned int)&e2.c; printf("%d,%d,%d\n",sizeof(example1),sizeof(example2),d); return 0; } 输出结果, 要能清除的上面的问题就要搞清楚结构体变量的成员在内存里是如何分布的、成员先后顺序是怎样的、成员之间是连续的还是分散的、还是其他的什么形式,其实这些问题既和软件相关又和硬件相关。所谓软件相关主要是指和具体的编程语言的编译器的特性相关,编译器为了优化CPU访问内存的效率,在生成结构体成员的起始地址时遵循着某种特定的规则,这就是所谓的 结构体成员“对齐”;所谓硬件相关主要是指CPU的“字节序”问题,也就是大于一个字节类型的数据如int类型、short类型等,在内存中的存放顺序,即单个字节与高低地址的对应关系。字节序分为两类:Big-Endian和Little-Endian,有的文章上称之为“大端”和“小端”,他们是这样定义的: Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端;Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。 Intel、VAX和Unisys处理器的计算机中的数据的字节顺序是Little-Endian,IBM 大型机和大多数Unix平台的计算机中字节顺序是Big –Endian。 关与Big-Endian和Little-Endian问题本文暂不做详细讨论,本文将以小端机(此处为intel x86架构的计算机)、OS:WindowsXp和VC++6.0编译器来详细讨论结构体成员的“对齐”问题。 前面说了,为了优化CPU访问内存的效率,程序语言的编译器在做变量的存储分配时就进行了分配优化处理,优化规则大致原则是这样: 对于n字节的元素(n=2,4,8,...),它的首地址能被n整除,这种原则称为“对齐”,如WORD(2字节)的值应该能被2整除的位置,DWORD(4字节)应该在能被4整除的位置。 对于结构体来说,结构体的成员在内存中顺序存放,所占内存地址依次增高,第一个成员处于低地址处,最后一个成员处于最高地址处,但结构体成员的内存分配不一定是连续的,编译器会对其成员变量依据前面介绍的 “对齐”原则进行处理。对待每个成员类似于对待单个n字节的元素一样,依次为每个元素找一个适合的首地址,使得其符合上述的“对齐”原则。通常编译器中可以设置一个对齐n,但这个n并不是结构体成员实际的对齐参数,VC++6.0中结构体的每个成员实际对齐参数N通常是这样计算得到的N=min(sizeof(该成员类型),n)(n为VC++6.0中可设置的值)。 成员的内存分配规律是这样的:从结构体的首地址开始向后依次为每个成员寻找第一个满足条件的首地址x,该条件是x % N = 0,并且整个结构的长度必须为各个成员所使用的对齐参数中最大的那个值的最小整数倍,不够就补空字节。 结构体中所有成员的对齐参数N的最大值称为结构体的对齐参数。 VC++6.0中n 默认是8个字节,可以修改这个设定的对齐参数,为在菜单“”的“设置”中的“C/C++”选项卡的“分类”中 “Code Generation ”的“Struct member alignment” 中设置,1byte、2byte、4byte、8byte、16byte等几种,默认为8byte 也可以程序控制,采用指令:#pragma pack(xx)控制 如#pragma pack(1),1字节对齐,#pragma pack(4),4字节对齐 #pragma pack(16),16字节对齐 接下来我们将分不同的情况来详细讨论结构体成员的分布情况,顺便提醒一下,常见类型的长度:Int 4byte,Short 2byte,Char 1byte,Double 8byte,Long 4byte 让我们先看下例: struct A { char c; //1byte double d; //8byte short s; //2byte int i; //4byte }; int main(int argc, char* argv[]) { A strua; printf("%len:d\n",sizeof(A)); printf("%d,%d,%d,%d",&strua.c,&strua.d,&strua.s,&strua.i); return 0; } 1)n设置为8byte时 结果:len:24, 1245032,1245040,1245048,1245052 内存中成员分布如下: 4byte 4byte Strua c 补0 1245032 d 1245040 s 补0 i 1245048 strua.c分配在一个起始于8的整数倍的地址1245032(为什么是这样读者先自己思考,读完就会明白),接下来要在strua.c之后分配strua.d,由于double为8字节,取N=min(8,8),8字节来对齐,所以从strua.c向后找第一个能被8整除的地址,所以取1245032+8得1245040, strua.s 为2byte小于参数n,所以N=min(2,8),即N=2,取2字节长度对齐,所以要从strua.d后面寻找第一个能被2整除的地址来存储strua.s,由于strua.d后面的地址为1245048可以被2整除,所以strua.s紧接着分配,现在来分配strua.i,int为4byte,小于指定对齐参数8byte,所以N=min(4,8)取N=4byte对齐,strua.s后面第一个能被4整除地址为1245048+4,所以在1245048+4的位置分配了strua.i,中间补空,同时由于所有成员的N值的最大值为8,所以整个结构长度为8byte的最小整数倍,即取24byte其余均补0. 于是该结构体的对齐参数就是8byte。 2)当对齐参数n设置为16byte时,结果同上,不再分析 3)当对齐参数设置为4byte时 上例结果为:Len:20 1245036,1245040,1245048,1245052 内存中成员分布如下: 4byte Strua c 补0 1245036 d 1245040 1245048 s 补0 1245052 i Strua.c起始于一个4的整数倍的地址,接下来要在strua.c之后分配strua.d,由于strua.d长度为8byte,大于对齐参数4byte,所以N=min(8,4)取最小的4字节,所以向后找第一个能被4整除的地址来作为strua.d首地址,故取1245036+4,接着要在strua.d后分配strua.s,strua.s长度为2byte小于4byte,取 2,4)2byte对齐,由于strua.d后的地址为1245048可以被2 N=min( 整除,所以直接在strua.d后面分配,strua.i的长度为4byte,所以取N=min(4,4)4byte对齐,所以从strua.s向后找第一个能被4整除的位置即1245048+4来分配和strua.i,同时N的最大值为4byte,所以整个结构的长度为4byte的最小整数倍16byte 4)当对齐参数设置为2byte时 上例结果为:Len:16 1245040,1245042,1245050,1245052 2byte Strua.c c 补0 1245040 d s i Strua.c分配后,向后找一第一个能被2整除的位置来存放strua.d,依次类推 5)1byte对齐时: 上例结果为:Len:15 1245040,1245041,1245049,1245051 此时,N=min(sizeof(成员),1),取N=1,由于1可以整除任何整数,所以各个成员依次分配,没有间空,如下图所示: 1byte 8byte 2byte 4byte c d s i 6)当结构体成员为数组时,并不是将整个数组当成一个成员来对待,而是将数组的每个元素当一个成员来分配,其他分配规则不变,如将上例的结构体改为: struct A { char c; //1byte double d; //8byte short s; //2byte char szBuf[5]; }; 对齐参数设置为8byte,则,运行结果如下: Len:24 1245032,1245040,1245048,1245050 8byte c 补0 d s1 szBuf 补0 Strua 的s分配后,接下来分配Strua 的数组szBuf[5],这里要单独分配它的每个元素,由于是char类型,所以N=min(1,8),取N=1,所以数组szBuf[5]的元素依次分配没有间隙。 7)当结构中有成员不是一个完整的类型单元,如int或short型,而是该类型的一段时,即位段时,如 struct A { int a1:5; int a2:9; char c; int b:4; short s; }; 对于位段成员,存储是按其类型分配空间的,如int 型就分配4个连续的存储单元,如果是相邻的同类型的段位成员就连续存放,共用存储单元,此处如a1,a2将公用一个4字节的存储单元,当该类型的长度不够用时,就另起一个该 类型长度的存储空间。有位段时的对齐规则是这样:同类型的、相邻的可连续在一个类型的存储空间中存放的位段成员作为一个该类型的成员变量来对待,不是同类型的、相邻的位段成员,分别当作一个单独得该类型的成员来对待,分配一个完整的类型空间,其长度为该类型的长度,其他成员的分配规则不变,仍然按照前述的对齐规则进行。 对于 struct A,VC++6.0中n设置为8时,sizeof(A)=16,内存分布: a1 a2 c d 空 s 又如: struct B { int a:5; int b:7; int c:6; int d:9; char e:2; int x; }; Vc++6.0的对齐参数设置为8、16、4字节对齐时,sizeof(A)=12内存分布为: (灰色部分未使用) 4byte a b c d ec 空 x 当对齐参数设置为2字节时:(灰色部分未使用)sizeof(A)=10 2byte a b d e x 又如intel的笔试题: #include “stdafx.h” #include struct bit { int a:3; int b:2; int c:3; }; int main(int argc, char* argv[]) { bit s; *c = (char*)&s; char *c = 0x99; << s.a <
/
本文档为【结构体成员的内存分布与对齐】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
热门搜索

历史搜索

    清空历史搜索