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

关于C++字节对齐的分析与理解

2010-06-23 19:10 501 查看
 

一、为什么要进行字节对齐?

    简单来说就是提高cpu对内存的访问效率。为了访问未对齐的内存,处理器需要作两次内存访问;然而,对齐的内存访问仅需要一次访问。

    不同的平台(cpu)和编译器对字节对齐的处理是不一样的,但是原理上大同小异。

    一般来说是不需要太过关注字节对齐的,因为编译器会自动帮助我们完成。但是某些时候,字节对齐可能是非常隐蔽的bug的诱因。所以,了解其规则是有必要的。

 

二、基本原则:

      对齐值:一个变量的对齐值取的是自身长度大小和通过#pragma pack(n)指定的对齐值(没有指定的话,默认为8)中较小的

      圆整值:编译器会对结构体进行圆整(即,在结构体最末填充一定字节),圆整值的大小(也可以简单理解为结构体最后一个变量将占用的大小)取的是结构体中最宽的基本类型和通过#pragma pack(n)指定的对齐值中较小的

  1) 结构体的首地址能够被其最宽基本类型成员长度和通过#pragma pack(n)指定的对齐值之间较小的值所整除;
  2) 结构体每个成员相对于结构体首地址的偏移量都是其对齐值的整数倍,如有需要编译器会在成员之间加上填充字节;

  3) 结构体的总大小为结构体圆整值的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。

 

三、实例分析:

a、基本对齐例子

1、struct Test1 {

         char a;

         int i;

         char c;

     };

    

     sizeof(Test1) = 12

     分析:变量i会进行字节对齐,对齐值为4,编译器会在a和i之间填充3字节。最后编译器会对结构体进行圆整,在结构体最后填充3字节(因为结构体中最大的基本类型为int,长度为4字节)。

 

2、上例中,如果加上  #pragma pack(2)

     则,sizeof(Test1) = 8

    分析:同样成员变量i会进行字节对齐,但是由于指定的对齐长度为2,并且比int类型长度4要小,所以对齐值为2。由于指定了对齐值2,所以变量c后会再填充1字节,以进行圆整。

             如果c的类型为short,则sizeof(Test1) = 8, 因为short的长度为2,不需要再进行圆整。

             如果c的类型为int ,则sizeof(Test1) = 10,同样不需要进行圆整,总大小=2+4+4

3、上例中,如果加上  #pragma pack(8) 或 如果加上  #pragma pack(4), 则,依然 sizeof(Test1) = 12

 

b、另外一个例子

1、struct Test2 {

         char a;

         double d;

         int i;

     };

 

 

     分析: sizeof(Test2) = 24 = 8 + 8 + 8    对齐值=8,圆整值=8,所以变量a后会填充7字节,变量i后会填充4字节

     如果加上  #pragma pack(8),结果也是一样的。

     如果加上  #pragma pack(4),则 sizeof(Test2) = 16 = 4 + 8 + 4

 

c、结构体之间的对齐

 1、  struct Test3 {

       Test1 t1;

       Test2 t2;

   };

 

   sizeof(Test3) = 40

   分析: sizeof(Test1) = 4 + 4 + 4 = 12   sizeof(Test2) = 8 + 8 + 8 = 24

        由于原则1,Test2的最宽基本类型为double,长度为8,则Test2的首地址应该能被8整除。所以在Test1和Test2之间被填充了4字节。注意,这里看的是基本类型的宽度而不是结构体的总长度,即便Test1或Test2中再嵌套结构体或包含数组,最终看的也只是其中的一个基本类型。

2、如果加上  #pragma pack(4),则 sizeof(Test3) = 28

    分析:sizefo(Test1) = 4 + 4 + 4 = 12    sizeof(Test2) = 4 + 8 + 4 = 16

        由于指定了对齐值4,再看原则1时,Test2的首地址应该能被4整除(因为指定对齐值4小于最宽基本类型8)。所以结构体之间不需要再填充字节。

 

d、枚举间的对齐

1、union u1 {

         int i;

         char c;

     };

    union u2 {

        Test1 t1;

        double d;

    };

 

    struct Test4 {

         u1 uu1;

         u2 uu2;

    };

 

    sizeof(u1) = 4

    sizeof(u2) = 16

    sizeof(Test4) = 24

   分析:Test1的大小为12,但是由于u2中的最宽基本类型double为8,所以最后u2实际的圆整大小为8字节,故虽然Test1的大小为12,但是u2联合体所占用的内存空间并不仅仅是联合体中最长的成员,而是圆整后的16字节;

           同样,u2联合体的首地址也应该是8的倍数,而不是看Test1的大小,故uu1和uu2之间会再被填充4字节,所以Test4的大小为24

 

四、总结

    通过之前的例子和分析可见,只要掌握了字节对齐的原则,万变不离其宗。简单总结一下就是:一个变量的对齐看的是自身长度和指定对齐值之间较小的;结构体的对齐(包括头和尾)看的是结构体中最大基本类型的长度和指定对齐值之间较小的。

   

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ 编译器 struct c 平台