您的位置:首页 > 编程语言 > C语言/C++

C++解剖->内存对齐

2014-04-08 20:01 190 查看
首先先贴一段代码:

#pragma pack(4)
struct test1
{
char c1;
short s;
char c2;
int i;
};
#pragma pack()
struct test2
{
char c1;
char c2;
short s;
int i;
};
结构体申明:
   #pragma  pack(N)   作用是设置对齐系数,一般为 1,2,4,8,16,32。。。。

   #pragma  pack ( )    结束设置

测试源码:

struct test1 a1;
struct test2 a2;
cout<<"a1 "<<endl;
cout<<"c1="<<(unsigned int)(void*)&a1.c1-(unsigned int)(void*)&a1<<endl;
cout<<"c2="<<(unsigned int)(void*)&a1.c2-(unsigned int)(void*)&a1<<endl;
cout<<"s="<<(unsigned int)(void*)&a1.s-(unsigned int)(void*)&a1<<endl;
cout<<"i="<<(unsigned int)(void*)&a1.i-(unsigned int)(void*)&a1<<endl;
cout<<"a1 sizeof is "<<sizeof(a1)<<endl;

cout<<"a2 "<<endl;
cout<<"c1="<<(unsigned int)(void*)&a2.c1-(unsigned int)(void*)&a2<<endl;
cout<<"c2="<<(unsigned int)(void*)&a2.c2-(unsigned int)(void*)&a2<<endl;
cout<<"s="<<(unsigned int)(void*)&a2.s-(unsigned int)(void*)&a2<<endl;
cout<<"i="<<(unsigned int)(void*)&a2.i-(unsigned int)(void*)&a2<<endl;
cout<<"a2 sizeof is"<<sizeof(a2)<<endl;

默认的时候,a1为12 ,a2为8.
当pragma(1)时,a1为8;pragma(2),a1为10,;pragma(4),a1为12.。。。。

  现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。

   对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量) 
Char 
偏移量必须为sizeof(char)即1的倍数 
int 
偏移量必须为sizeof(int)即4的倍数 
float 
偏移量必须为sizeof(float)即4的倍数 
double 
偏移量必须为sizeof(double)即8的倍数 
Short 
偏移量必须为sizeof(short)即2的倍数 
各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节VC会自动填充。同时VC为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。 

对齐准则:

1.每个成员分别按自己的方式对齐,并能最小化长度。

2.复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度。

3.对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐。

Win32平台下的微软 编译器(cl.exe for 80×86)的对齐策略: 

1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除; 

备注:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为上面介绍的对齐模数。 

2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding); 

备注:为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。 

3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)。 

备注:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: