内存对齐

3030阅读 0评论2015-04-21 linux_player_c
分类:C/C++


内存对齐问题

首先我们拿几个问题热热身,如下所示的结构体,请诸位来判断下sizeof后的值的大小。

1.什么都没有的结构体(类),C++中这个结构体是多少?

struct A

{


};

2.那么在C语言中呢?上述的结构体是多少?

如果上面的例子让你拿捏不准的话,下面的例子给你一些自信:

3.

struct A

{

    char a;

    short b;

    int c;

};

Sizeof(A)的值是多少?

如果你答的是7的话,可以回去等(xi)通(xi)知(shui)了(le)。

显然正确的结果基础扎实的同学是知道了,我们就以此为突破口来对内存对齐的知识进行剖析。

 

为什么要进行内存对齐?

回答7的同学一定很疑惑,1+2+4=7难道有错么,从我们直观的感受来说确实是正确的。但是计算机好像并不是以此为原则的,呵呵,结果说明一切。那么显然它有自己的一套规则。

许多计算机系统对基本数据类型合法地址做出了一些限制,要求某种类型对象的地址必须是某个值K(通常是2、4或8)的倍数。这种对齐限制简化了形成处理器和存储器系统之间接口的硬件设计。无论数据是否对齐,其实程序都能跑下去。但是Intel还是要建议对齐数据以提高存储器的性能。

上面的扯了那么多无非就是说,内存对齐是为了效率。那么它的对齐原则是什么,我们列举出来一一进行验证:

1.对于结构体的各个成员,第一个成员位于偏移量为0的位置,以后每个数据成员的偏移量必须是MIN(#pragma pack(x), 这个数据成员的自身长度)的倍数。

2.在数据成员完成各自对齐之后,结构体本身也要进行对齐,对齐将按照MIN(#pragma pack(x), 结构体(共用体)最大数据成员长度)。

这个#pragma pack(x)是什么意思?

指定成员按x个字节对齐。

#pragma pack(n)   编译器将按照n个字节对齐

#pragma pack()    编译器将取消自定义字节对齐方式

但是正如刚在的公式所说,每个成员还得按照自己的方式对齐。配合上边的#pragma:每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是n字节)中较小的一个对齐。

例如
     #pragma pack(8)

      struct A

      {

          char a;

          long b;

      };


      struct B

      {

          char c;

          A d;

          long long e;

      };

A和B元素的所占字节的大小?

 

3.有得时候只是位置的调整就可以让他所占空间发生变化。

    struct test

     {

         char a;

         short b;

         char c;

         int d;

     };


     struct test2

     {

         short b;

         char a;

         char c;

         int d;

     };

     上述的两个结构体大小分别为多少?

4.万事万物都有特例,回到第一个问题上。一个“空的结构体”有多大?

在C++中,一个空的struct占1字节,虽然struct没有任何元素,但是C++把struct当做类去看待。空类的默认大小为1。

而在C语言中,一个空的struct确实被当做一个空的结构体,那么它的大小也就是0了(在linux的gcc下编译)。

5.但是还有一些比较怪异结构体我们没有见过:

    struct C

     {

          struct C a;

     };

   这个结构体的元素时结构本身。。。。。。所以大小是多少?

  

 

如果概念上大家已经明确的话,请接招(由简到难)。

1.

union A

{

    int a;

    double b;

    int c;

    char d;

};

Sizeof(A)?

2.

union A

{

    int a;

    double b;

    int c;

    char d;

};

struct B

{

    A a;

    char b;

};


Sizeof(B) ?

 

3.

union A

{

    int a;

    double b;

    int c;

    char d;

};


struct B

{

    A a;

    char b;

};


struct C

{

    int i_value;

    B b;

    char ch;

};

Sizeof(C)?

4.

struct A

{

    char a;

    short b;

    int c;

    struct B

    {

        char d;

        double e;

    };

    int f;

};

这个的结构体的大小是多少?(你可能会整错!)

5.

如果上边的算错了别灰心,看看下边这个。

struct A

{

    char a;

    short b;

    int c;

    struct B

    {

        char d;

        double e;

    } f;

    int g;

};

这个和上边的非常相似,但是有区别?

4、5例子的对比将会引入一个新的概念。

 

6.

好吧,已经遇到了不少的陷阱了。那么再看看下边的。我们将会在结构体中添加位段(关于位段的概念参考《C和指针》)。

(1)

struct B

{

    char c1:6;

    char c2:1;

    int  in:16;

};

(2)

struct B

{

    short s1:6;

    char c2:1;

    int  in:16;

};

(3)

struct B

{

    short s1:6;

    char c2:1;

    short s2:15;

    int  in:16;

};

(4)

热身完毕后得来点干货了!

struct B

{

    short s1:6;  

    char c2:1;

    short s2:15;

    int  in:16;

};


struct A

{

    char a;

    short b; 

    int c;

    struct B e;

    int g;

};

Sizeof(A)等于多少?

(5)

union B

    short s1:6;

    char c2:1;

    short s2:15;

    int  in:16;

};


struct A

{

    char a;

    short b;

    int c; 

    union B e;

    int g;

};

(6)

union B

{

    short s1:6;

    char c2:1;

    short s2:15;

    int  in:16;   

    double d:32;

};

这个的是多少?(算对说明思路清晰的很)。

(7)

struct A

{

    char a;

    short b;

    int c;

    union B

    {

        int b;

        char c1:3;

        short s2:12;

        struct 

        {

            int a;

            double e;

        };

    };

    int g;

};

长度很多,请说出sizeof(A)大小?

关于位段的考察告一段落,位段对齐原则的细节请自行在网上查阅。我们再转战到enum(枚举)。看看enum在结构体中的应用。

7.

struct A

{

    enum a{
       INDEX1,

        INDEX2,

        INDEX3,

        INDEX4
   };

};

这个的sizeof(A)是多少?

8.

struct A

{

    int a;

    short b;

    int c;

    double d;

    struct B

    {

        int a;

        enum c;

    } e;

    double f;

};

Sizeof(A)多少?

9.我们来看看数组问题。

struct A

{

    char c[13];

    int  b; 

    double e[2];

    char d[5];

};


总结:

至此我们已经考察了内存对齐的方方面面,对它的使用原则进行了简单的介绍,而且给出了很多变态的问题(不少的难度远超于面试难度)。那么引申出一个问题:

     为什么企业面试这么看重内存对齐的概念?

     1.在网络的数据传输中,如果能够组织起高效的数据报可以大大节省空间。

     2.TCP和UDP的数据报,很多的位段用来描述数据报的状态。

     3.考察学生是否进行过数据报的设计。

那么内存对齐的好处确实不少:

1.加快内存的读取速度,跳了效率;

2.确保程序的正常执行,有的存储器访问要求存储器地址必须是16的倍数,不满足的会导致程序终止(这个时候需要强制对齐)。

 

延伸阅读,请看看linux 和windows的对齐策略有什么不同?

参考《深入理解计算机系统》第三章:程序机器级表示。171页。


 

       关于这篇博客博客上各sizeof值是多少,欢迎大家讨论。过段时间我将给出正确答案。

 

 

    

                                                                                                                                                             

                                                                                                                                                   linux_player_c

                                                                                                                                                   西部开源技术中心

                                                                                                                                                   597114476@qq.com

 








上一篇:linux基础知识的总结
下一篇:在redhat下使用x11vnc进行桌面共享