内存对齐与自定义类型

1、内存对齐linux


  (一)、为何会有内存对齐?c++

    一、为了提升程序的性能,数据结构(尤为是栈)应该尽量的在天然边界上对齐。缘由是为了访问未对齐的内存,处理器须要进行两次访问,而访问对齐的内存,只须要一次就够了。这种方式称做“以空间换时间”在不少对时间复杂度有要求问题中,会采用这种方法。数组


  Center

  二、内存对齐可以增长程序的可移植性,由于不是全部的平台都能随意的访问内存,有些平台只能在特定的地址到处读取内存。数据结构


   通常状况下内存对齐是编译器的事情,咱们不须要考虑,但有些问题仍是须要考虑的,毕竟c/c++是直接操做内存的语言,须要了解程序在内存中的分布和运行原理。ide



(二)、内存对齐:那么如何对齐呢?性能

    对齐原则:数据存放的起始位置是自身大小的整数倍处。指针

    例:内存从0地址处开始。调试

       char1个字节,因此对齐后它能够存放到地址是1的倍数处。内存

       short2个字节,因此对齐后它能够存放到地址是2的倍数处。编译器

       int是4个字节,因此对齐后它能够存放到地址是4的倍数处。

       double是8个字节,因此对齐后它能够存放到地址是8的倍数处。


如今相信你已经明白了内存对齐的原则,接下来咱们看看结构体内存对齐。


(三)、在了解结构体内存对齐以前先来了解几个概念:

    一、默认对齐数:在vs下内存对齐数默认是8,linux是4.能够经过#program pack  ()来修改默认对齐数。

    二、偏移:相对于起始位置的的位置。例如起始位置是2,那么2就是0偏移处,3就是1偏移处。

Center


   三、对齐数:变量自身的大小和默认对齐数之中的最小值。假设默认对齐数是8,int类型的对齐数就是4.由于int大小是4,小于8



2、结构体内存对齐原则:

  一、结构或联合的数据成员,第一个成员放到0偏移的地方,之后每一个数据成员都放到自身对齐数的整数倍偏移处。

  二、结构体的大小必须是最大对齐数的整数倍。

   例1:

       struct stu

       {

         char   c;          //对齐数是1

         short  b;          //对齐数是2

         double d;           //对齐数是8

         int     i;         //对齐数是4

       };


Center


   例2:

       struct stu

       {

          char   c;          //对齐数是1

          short  b;          //对齐数是2

          struct A

          {

             double d;        //对齐数是8

          };

          int     i;         //对齐数是4

       }s;

    嵌套结构体的大小,其分析方法仍是同样,最大对齐数是8,sizeof(s)=24。



3、自定义类型 


(一)、结构体声明

   一、没有标签,不完整的声明。同时还定义一个变量。

                    struct

                   {

                          charc;

                           shortb;

                           inti;

                    }t1;


   二、有标签的声明,但没定义变量的声明。

                   struct    A

                  {

                      charc;

                      shortb;

                      inti;

                  };

     //定义一个变量struct A *s1; 

     //注意,在同一个程序中,同时声明一、2两个结构体,则一、2两个结构体会被认为是不一样类型的。因此 s1=&t1是错误的。



   三、有标签的声明,同时还定义一个变量。

                    struct    A

                   {

                        charc;

                        shortb;

                        inti;

                    }t3;


   四、声明的同时对结构体重命名为A.

                     typedef  struct    A

                    {

                         charc;

                         shortb;

                         inti;

                     }A;


   五、先有鸡仍是先有蛋

                       struct B                                     //不管哪一个放到前面都不对

                      {

                           structAa;

                       }s;

                       structA

                        {

                           structBb;

                         };


   若是两个结构体相互嵌套,则在声明的时候须要对其中一个结构体进行不完整的声明。

                          structA;

                          struct B

                             {

                                   structAa;

                              }s;

                         structA

                             {

                                    structBb;

                             };



(二)、结构体的初始化:

     例如:

                   typedef  struct    A

                  {

                       charc;

                       shortb;

                       inti;

                  }A;

                 As1 = {'c', 2 ,  4  };



(三)、结构体的自引用:(结构体的自引用一般会用在链表这种线性结构中用到)

   

  一、错误的自引用方式,很容易理解的,结构体里面又有结构体,这样一直循环下去。(从前有座庙,庙里有个老和尚,老和尚给小和尚讲故事..........^v^)

                     typedef struct   A

                      {

                             intdata;

                             structAn;                                         //死循环

                       }A;


  二、错误的只引用,由于结构体被从新命名为A是在引用以后。

                     typedef struct                                         //在结构体自引用的时候标签不能省略。

                     {

                               intdata;

                           An;                                                         //必须使用完整的结构体名称

                     }A;


  三、正确的方式

                  typedef struct   A

                 {

                         intdata;

                         structA *n;            //用完整的结构体名称,声明一个结构体指针,

                  }A;



(四)、结构体作参数传递的效率:

   当结构体很大时,结构体在做为参数传递时,咱们传递它的地址,这样可以提升效率,若是你不想改变结构体内容,则在形参处加上const就行。


(五)、柔性数组:

   在结构体中最后一个成员容许是未知大小的数组,这个数组成为柔性数组(柔性数组以前至少有一个成员变量)

             typedef  struct    A

               {

                     inti;

                      char  a[];

               }A;

   含有柔性数组的结构体大:这样的结构体,它的大小不包括柔性数组,因此sizeof(A)=4;空结构体的大小是1;



(六)、位段(位域):


  一、概念:在一个结构体中以位为单位来指定成员所占内存的实际大小,这种以位为单位的成员咱们称为位段,位段是一种特殊的结构体,位段的声明和任何普通的结构体成员声明相似,以下:


         Struct 位段结构体名

             {

                   Unsigned 位段名:位段长度;

                   Unsigned 位段名:位段长度;

………………..

                   Unsigned 位段名:位段长度;


             }位段结构体变量名;


   但有两个例外,首先位段成员必须声明成int ,unsigned int, signed int,。其次,在成员的后面是一个冒号和一个整数,这个整数指定该位段所占用位的个数。(实际验证后发现char类型也能够,可是注意,位段中不能将int 和char 混合使用)。


  二、 位段使用时须要注意是:

        一、位段结构体中的成员不能使用数组和指针,但结构体变量可使数组或者指针。

        二、由于数组和指针都是以字节为单位的,同理也不能用&获取位段的地址。

        三、位段不支持移植。

  例1:声明一个位段,咱们先来分析一下他在计算机里面是如何存储的(一个无符号的int是4字节)。        

               struct tagAAA

                {

                  unsigned int a : 1;

                  unsigned int b : 2;

                  unsigned int c : 6;

                  unsigned int d : 4;

                  unsigned int e;

                 }AAA_S;

Center


   由此咱们能够明白位段的优势,原本定义了5个成员,须要5个存储单位,可是使用位段后只须要4个存储空间就足够了。


  三、优势:

    但它的成员是一个或多个位的字段,这些不一样长度的字段其实是存储于一个或多个×××变量中,他的优势是可以以较少的内存单元存储数据。位段能够用×××形式输出。



例2:

     struct tagAAA

                {

                  unsigned int a : 1;

                  unsigned int  : 2;           //没有声明变量,可是却指定位段大小,称为占位。

                  unsigned int c : 6;

                  unsigned int d : 4;

                  unsigned int e;             //没有指定位段大小,默认为自身类型的大小

                 }AAA_S;                


             




(七)、联合


  一、联合的声明:

          typedefunionA

           {

                inti;

                charc;

            }A;


  二、联合的特色:

    联合成员之间共用同一块空间。联合的大小等于成员中所占内存最大变量大小。能够用来测大小端。



(八)、枚举:

    一、声明:

           typedefenumA

           {

                   zero,

                   one,

                    two

            }A;

若是没有对枚举成员进行初始化时,则默认枚举成员从0开始依次递增


 注意:

      一、在同一个程序中,不能不能声明同名的枚举类型

      二、在同一个程序中,不一样的枚举类型的枚举成员不能同名。

      三、任何枚举的大小都是4


  二、枚举与#define 标识符之间区别:

      一、#define 标识符在预编译期间进行简单替换。枚举类型在编译的时候肯定其值。

      二、枚举常量能够调试,#define 标识符不能够。

      三、枚举一次能够定义大量的枚举量。

相关文章
相关标签/搜索