Chinaunix首页 | 论坛 | 博客
  • 博客访问: 363881
  • 博文数量: 168
  • 博客积分: 6895
  • 博客等级: 准将
  • 技术积分: 1726
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-12 23:01
文章分类

全部博文(168)

文章存档

2011年(6)

2010年(162)

我的朋友

分类: LINUX

2010-08-28 07:25:26

  c 语言内存对齐详解

   #pragma   pack(4)  
  class   TestB  
  {  
  public:  
    int   aa;  
    char   a;  
    short   b;  
    char   c;  
  };  
  int   nSize   =   sizeof(TestB);  
  这里nSize结果为12,在预料之中。  

  现在去掉第一个成员变量为如下代码:  
  #pragma   pack(4)  
  class   TestC  
  {  
  public:  
    char   a;  
    short   b;  
    char   c;  
  };  
  int   nSize   =   sizeof(TestC);  
  按照正常的填充方式nSize的结果应该是8,为什么结果显示nSize为6呢?  

事实上,很多人对#pragma   pack的理解是错误的。  
#pragma   pack规定的对齐长度,实际使用的规则是:  
结构,联合,或者类的数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma  
pack指定的数值和这个数据成员自身长度中,比较小的那个进行。  
也就是说,当#pragma  
pack的值等于或超过所有数据成员长度的时候,这个值的大小将不产生任何效果。  
而结构整体的对齐,则按照结构体中最大的数据成员   和   #pragma  
pack指定值   之间,较小的那个进行。  

具体解释  
#pragma   pack(4)  
  class   TestB  
  {  
  public:  
    int   aa;   //第一个成员,放在[0,3]偏移的位置,  
    char   a;   //第二个成员,自身长为1,#pragma   pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。  
    short   b;   //第三个成员,自身长2,#pragma   pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。  
    char   c;   //第四个,自身长为1,放在[8]的位置。  
  };  
这个类实际占据的内存空间是9字节  
类之间的对齐,是按照类内部最大的成员的长度,和#pragma   pack规定的值之中较小的一个对齐的。  
所以这个例子中,类之间对齐的长度是min(sizeof(int),4),也就是4。  
9按照4字节圆整的结果是12,所以sizeof(TestB)是12。  


如果  
#pragma   pack(2)  
class   TestB  
  {  
  public:  
    int   aa;   //第一个成员,放在[0,3]偏移的位置,  
    char   a;   //第二个成员,自身长为1,#pragma   pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。  
    short   b;   //第三个成员,自身长2,#pragma   pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。  
    char   c;   //第四个,自身长为1,放在[8]的位置。  
  };  
//可以看出,上面的位置完全没有变化,只是类之间改为按2字节对齐,9按2圆整的结果是10。  
//所以   sizeof(TestB)是10。  

最后看原贴:  
现在去掉第一个成员变量为如下代码:  
  #pragma   pack(4)  
  class   TestC  
  {  
  public:  
    char   a;//第一个成员,放在[0]偏移的位置,  
    short   b;//第二个成员,自身长2,#pragma   pack(4),取2,按2字节对齐,所以放在偏移[2,3]的位置。  
    char   c;//第三个,自身长为1,放在[4]的位置。  
  };  
//整个类的大小是5字节,按照min(sizeof(short),4)字节对齐,也就是2字节对齐,结果是6  
//所以sizeof(TestC)是6。  

感谢   Michael   提出疑问,在此补充:  

当数据定义中出现__declspec(   align())时,指定类型的对齐长度还要用自身长度和这里指定的数值比较,然后取其中较大的。最终类/结构的对齐长度也需要和这个数值比较,然后取其中较大的。  

可以这样理解,   __declspec(   align()   )   和   #pragma   pack是一对兄弟,前者规定了对齐的最小值,后者规定了对齐的最大值,两者同时出现时,前者拥有更高的优先级。  
__declspec(   align()   )的一个特点是,它仅仅规定了数据对齐的位置,而没有规定数据实际占用的内存长度,当指定的数据被放置在确定的位置之后,其后的数据填充仍然是按 照#pragma   pack规定的方式填充的,这时候类/结构的实际大小和内存格局的规则是这样的:  
在__declspec(   align()   )之前,数据按照#pragma   pack规定的方式填充,如前所述。当遇到__declspec(   align()   )的时候,首先寻找距离当前偏移向后最近的对齐点(满足对齐长度为max(数据自身长度,指定值)   ),然后把被指定的数据类型从这个点开始填充,其后的数据类型从它的后面开始,仍然按照#pragma   pack填充,直到遇到下一个__declspec(   align()   )。  
当所有数据填充完毕,把结构的整体对齐数值和__declspec(   align()   )规定的值做比较,取其中较大的作为整个结构的对齐长度。  
特别的,当__declspec(   align()   )指定的数值比对应类型长度小的时候,这个指定不起作用。
阅读(467) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~