现在的位置: 首页 > 综合 > 正文

【深入解析C内存对齐】

2018年05月25日 ⁄ 综合 ⁄ 共 2540字 ⁄ 字号 评论关闭
【前言】
在C程序设计中我们经常需要用到一种数据类型的长度(占内存的字节数),例如:
  int *p = NULL;
  p = (int *)malloc(10*sizeof(int));/*用sizeof(int)来的到int类型的长度*/
用sizeof可得到C语言中数据类型的长度,对基本数据类型而言,结果值很容易理解,但当sizeof的操作对象是一个结构类型时意想不到麻烦就来

了,其结果值经常与我们设想的不一样.为什么呢?
现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况并非如此.一些平台对某些特定类型的数据只能从某些特定地址开始存取,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放.这就是所谓的字节对齐.

【解析】
C语言中,结构是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、结构、联合等)的数据单元.在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分配空 间.各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同.
先让我们看几个例子吧(32bit,x86环境,gcc编译器):设结构体如下定义:
struct A
{
    int a;
    char b;
    short c;
};
struct B
{
   char b;
   int a;
   short c;
};
 现在已知32位机器上各种数据类型的长度如下:
char:        1(有符号无符号同)          short:      2(有符号无符号同) 
int:          4(有符号无符号同)          long:        4(有符号无符号同) 
float:        4                               double:8
 那么上面两个结构大小如何呢?结果是:sizeof(strcut A)值为8【对齐2字节】;sizeof(struct B)的值却是12【对齐4字节】。
【之所以出现上面的结果就是因为编译器要对数据成员在空间上进行对齐.】
 那编译器是按照什么样的原则进行对齐的?先看四个重要的基本概念:
1.数据类型自身对齐值:对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节.
2.结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值.
3.指定对齐值:#pragma pack (value)时的指定对齐值value.
4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值.

【重点是】:有效对齐N,就是表示“对齐在N上”,也就是说该数据"存放起始地址%N=0"。而数据结构中的数据变量都是按定义的先后顺序来排放的.第一个数据变量的起始地址就是数据结构的起始地址.结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值调整。
当然,我们是不是可以改变编译器的这种默认对齐设置呢,??当然可以.例如:
#pragma pack (2) /*指定按2字节对齐*/
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
//////////////////////////////////////////////////////////////////////////////////////////////////////
【例子分析B:】
struct B
{
   char b;
   int a;
   short c;
};
  假设B从地址空间0x0000开始排放。该例子中没有定义指定对齐值,在笔者环境下,该值默认为4。
第一个成员变量b的自身对齐值是1,比指定或者默认指定对齐值4小,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0。
第二个成员变量a,其自身对齐值为4,所以有效对齐值也为4,所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中,符合0x0004%4=0,且紧靠第一个变量。
第三个变量c,自身对齐值为2,所以有效对齐值也是2,可以存放在0x0008到0x0009这两个字节空间中符合0x0008%2=0。所以从0x0000到0x0009存放的都是B内容。
再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求,0x0009到0x0000=10字节,(10+2)%4=0。所以0x0000A到0x000B也为结构体B所占用。故
B从0x0000到0x000B共有12个字节,sizeof(struct B)=12;
////////////////////////////////////////////////////////////////////////////////////////////////////
同理,分析例子C:
#pragma pack (2) /*指定按2字节对齐*/
struct C
{
   char b;
   int a;
   short c;
};
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
第一个变量b的自身对齐值为1,指定对齐值为2,所以,其有效对齐值为1,假设C从0x0000开始,那么b存放在0x0000,符合
0x0000%1=0;
第二个变量,自身对齐值为4,指定对齐值为2,所以有效对齐值为2,所以顺序存放在0x0002、0x0003、0x0004、0x0005四个连
续字节中,符合0x0002%2=0。
第三个变量c的自身对齐值为2,所以有效对齐值为2,顺序存放在0x0006、0x0007中,符合0x0006%2=0。所以从0x0000到0x00007共八字节存放
的是C的变量。又C的自身对齐值为4,所以C的有效对齐值为2。又8%2=0,C只占用0x0000到0x0007的八个字节。所以sizeof(struct C)=8。
 
 
 

 

抱歉!评论已关闭.