结构体内存分配分析
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
结构体成员的内存分布与对齐
我们先看一道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 内存中成员分布如下:
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
补0
d 补0
c s
4byte
4byte
Strua 1245032
i
1245040
1245048