struct结构体字节对齐原则及为什么要对齐?

http://blog.sina.com.cn/s/blog_14ecf1a5b0102x27j.html

https://blog.csdn.net/zhanghow/article/details/55262988

//--------------------------------------------------------

本文目录结构

|-为什么结构体内存对齐

|-结构体内存对齐规则

|-具体举例

//--------------------------------------------------------

一、为什么结构体内存对齐

     现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
     对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问 一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对 数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那 么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数 据。显然在读取效率上下降很多。

       其实我们都知道,结构体只是一些数据的集合,它本身什么都没有。我们所谓的结构体地址,其实就是结构体第一个元素的地址。这样,如果结构体各个元素之间不存在内存对齐问题,他们都挨着排放的。对于32位机,32位编译器(这是目前常见的环境,其他环境也会有内存对齐问题),就很可能操作一个问题,就是当你想要去访问结构体中的一个数据的时候,需要你操作两次数据总线,因为这个数据卡在中间,如图:

struct结构体字节对齐
 

       在上图中,对于第2个short数据进行访问的时候,在32位机器上就要操作两次数据总线。这样会非常影响数据读写的效率,所以就引入了内存对齐的问题。另外一层原因是:某些硬件平台只能从规定的地址处取某些特定类型的数据,否则会抛出硬件异常。

二、结构体内存对齐的规则【未指定#pragma pack时】

      a.第一个成员起始于0偏移处;

      b.每个成员按其类型大小和指定对齐参数n中较小的一个进行对齐;

      c.结构体总长度必须为所有对齐参数的整数倍;

      d.对于数组,可以拆开看做n个数组元素。

     下表是Windows XP/DEV-C++和Linux/GCC中基本数据类型的长度和默认对齐模数。 

char

short

int

long

float

double

long long

long double

Win-32

长度

1

2

4

4

4

8

8

8

模数

1

2

4

4

4

8

8

8

Linux-32

长度

1

2

4

4

4

8

8

12

模数

1

2

4

4

4

4

4

4

Linux-64

长度

1

2

4

8

4

8

8

16

模数

1

2

4

8

4

8

8

16

三、具体举例

例1:struct {
                 short a1;
                 short a2;
                 short a3;
                 }A;
          struct{
                 long a1;
                 short a2;
                 }B;

sizeof(A) = 6; 这个很好理解,三个short都为2。

sizeof(B) = 8; long为4,short为2,整个为8,因为规则b。

例2:struct A{
                  int a;
                  char b;
                  short c;
                  };
       struct B{
                  char b;
                  int a;
                  short c;
                  };

sizeof(A) = 8;sizeof(B) = 12。

深究一下,为什么是这样,我们可以看看内存里的布局情况。

                             a         b         c
     A的内存布局:1111,     1*,       11

                             b          a        c
     B的内存布局:1***,     1111,   11**

     其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。B中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。

再看两个结构中含有结构成员的例子:

例3:struct A{
                     int a;
                     double b;
                     float c;
                    };

                struct B{
                     char e[2];
                     int f;
                     double g;
                     short h;
                     struct A i;
                    };

       sizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。

       sizeof(B) = 48; 看看B的内存布局。

                        e         f             g                h                                    i 
B的内存布局:11* *,   1111,   11111111, 11 * * * * * *,        1111* * * *, 11111111, 1111 * * * *

i其实就是A的内存布局。i的起始位置要为24的倍数,所以h后面要补齐。

例4:struct S1  

           {  

              short a;  

              long b;  

           };  

     struct S2  

          {  

    char c;  

    struct S1 d;  

    double e;  

          };  

       在Dev c++中,默认的是8字节对齐。我们分析下在4字节对齐的情况下输出的是,S2是20,S1是8,分析如图:

struct结构体字节对齐
 

      在4字节对齐的情况中,有一个问题值得注意:就是图中画1的地方。这里面本应short是可以上去的。但是对于结构体中的结构体一定要十分警惕,S1是一体的,short已经由于long进行了内存对齐,后面还空了两个字节的内存,其实此时的short已经变成了4个字节了!!!即结构体不可拆,不管是多少字节对齐,他们都是一体的。所有的圈都变成了叉。所以说结构体只能往前篡位置,不能改变整体。

      我们在分析一些8字节对齐的情况,如图:

struct结构体字节对齐

       以上讲的都是没有#pragma pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义来。比如上面的结构体前加#pragma pack(1),内存的布局就会完全改变。sizeof(A) = 16; sizeof(B) = 32;

      有了#pragma pack(1),按1字节对齐。没错,这不是理想中的没有内存对齐的世界吗。

                               a                b             c
      A的内存布局:1111,     11111111,   1111

                             e        f             g         h                     i
      B的内存布局:11,   1111,   11111111, 11 ,            1111, 11111111, 1111

      那#pragma pack(2)的结果又是多少呢?#pragma pack(4)呢?留给大家自己思考吧,相信没有问题。

猜你喜欢

转载自blog.csdn.net/libaineu2004/article/details/85917991