内存对齐与自定义类型-创新互联

一、内存对齐

创新互联公司坚持“要么做到,要么别承诺”的工作理念,服务领域包括:成都做网站、成都网站建设、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的山阳网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!

 (一)、为什么会有内存对齐?

  1、为了提高程序的性能,数据结构(尤其是栈)应该尽可能的在自然边界上对齐。原因是为了访问未对齐的内存,处理器需要进行两次访问,而访问对齐的内存,只需要一次就够了。这种方式称作“以空间换时间”在很多对时间复杂度有要求问题中,会采用这种方法。

  内存对齐与自定义类型

 2、内存对齐能够增加程序的可移植性,因为不是所有的平台都能随意的访问内存,有些平台只能在特定的地址处处读取内存。

  一般情况下内存对齐是编译器的事情,我们不需要考虑,但有些问题还是需要考虑的,毕竟c/c++是直接操作内存的语言,需要了解程序在内存中的分布和运行原理。

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

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

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

    char1个字节,所以对齐后它可以存放到地址是1的倍数处。

    short2个字节,所以对齐后它可以存放到地址是2的倍数处。

    int是4个字节,所以对齐后它可以存放到地址是4的倍数处。

    double是8个字节,所以对齐后它可以存放到地址是8的倍数处。

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

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

  1、默认对齐数:在vs下内存对齐数默认是8,linux是4.可以通过#program pack  ()来修改默认对齐数。

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

内存对齐与自定义类型

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

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

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

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

  例1:

    struct stu

    {

     char  c;      //对齐数是1

     short  b;      //对齐数是2

     double d;      //对齐数是8

     int   i;     //对齐数是4

    };

内存对齐与自定义类型

  例2:

    struct stu

    {

     char  c;      //对齐数是1

     short  b;      //对齐数是2

     struct A

     {

       double d;     //对齐数是8

     };

     int   i;     //对齐数是4

    }s;

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

三、自定义类型

(一)、结构体声明

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

          struct

          {

             charc;

              shortb;

              inti;

          }t1;

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

          struct   A

         {

           charc;

           shortb;

           inti;

         };

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

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

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

          struct   A

          {

            charc;

            shortb;

            inti;

          }t3;

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

           typedef  struct   A

          {

             charc;

             shortb;

             inti;

           }A;

  5、先有鸡还是先有蛋

            struct B                   //无论哪个放到前面都不对

           {

              structAa;

            }s;

            structA

            {

              structBb;

             };

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

             structA;

             struct B

               {

                  structAa;

               }s;

             structA

               {

                  structBb;

               };

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

   例如:

          typedef  struct   A

         {

            charc;

            shortb;

            inti;

         }A;

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

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

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

           typedef struct  A

           {

               intdata;

               structAn;                     //死循环

            }A;

 2、错误的只引用,因为结构体被重新命名为A是在引用之后。

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

           {

                intdata;

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

           }A;

 3、正确的方式

         typedef struct  A

         {

             intdata;

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

         }A;

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

  当结构体很大时,结构体在作为参数传递时,我们传递它的地址,这样能够提高效率,如果你不想改变结构体内容,则在形参处加上const就行。

(五)、柔性数组:

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

       typedef  struct   A

        {

           inti;

           char  a[];

        }A;

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

(六)、位段(位域):

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

     Struct 位段结构体名

       {

          Unsigned 位段名:位段长度;

          Unsigned 位段名:位段长度;

………………..

          Unsigned 位段名:位段长度;

       }位段结构体变量名;

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

 2、 位段使用时需要注意是:

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

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

    3、位段不支持移植。

 例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;

内存对齐与自定义类型

  由此我们可以明白位段的优点,本来定义了5个成员,需要5个存储单位,但是使用位段后只需要4个存储空间就足够了。

 3、优点:

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

例2:

   struct tagAAA

        {

         unsigned int a : 1;

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

         unsigned int c : 6;

         unsigned int d : 4;

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

         }AAA_S;

(七)、联合

 1、联合的声明:

     typedefunionA

      {

        inti;

        charc;

      }A;

 2、联合的特点:

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

(八)、枚举:

  1、声明:

      typedefenumA

      {

          zero,

          one,

          two

      }A;

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

 注意:

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

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

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

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

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

   2、枚举常量可以调试,#define 标识符不可以。

   3、枚举一次可以定义大量的枚举量。

另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。


分享文章:内存对齐与自定义类型-创新互联
链接URL:http://myzitong.com/article/ddpope.html