• 结构体内存对齐与位域


    C语言结构体的对齐问题
    C语言的结构体对齐问题是常见类型的题目,在笔试和面试中也是常考的题目,例如:
    struct
    {
       int a;
       char b;
       long c;
       short d;
    }s;
    问sizeof(s) = ?
    我们假设在32位机器下,要解决这个问题我们必须明确各种数据类型占用的空间是多大:
         int 类型:4字节;
      long类型:4字节;
    double类型:8字节;
    float 类型:4字节;
    short 类型:2字节;
    char  类型:1字节;
    所有指针类型:4字节;
    int a[] = {1,2,3,4}:sizeof(a) = 16。
    好,明确了上述就开始进入正题:
    一般情况下,即没有#pragma pack宏定义和使用位域的情况下,结构体对齐一般满足三个原则:
    1.普通数据成员:第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储)。
    2.结构体数据成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储)。
    3.结构体的总大小,必须是其内部最大元素占据字节的整数倍。
    struct {
    int a;
    int b;
    int c;
    }A;
    sizeof(A) = 12;//简单,内部成员均为int型,自动对齐。
      struct{
               char a;
               int b;
               short c;
               double d;
        }B;
    sizeof(B)=?
    a占用一个字节:
    1
    b占用四个字节,根据原则1,b起始地址必须为4的整数倍,现在为1,不符合条件,因此必须填充:
    1*** 1111
    c占用二个字节,符合原则1,根据原则3,总位数应该为4的整数倍,因此最后应该填充两个字节:
    1*** 1111 11**
    d占用八个字节,根据原则1,起始地址应该为8的整数倍,现在为12,需要填充:
    1*** 1111 11** **** 11111111
    所以sizeif(B) = 24
    typedef struct
    {
       int a;
       double b;
       float c;
    }A1;
    struct
    {
      char e[2];
      int  f;
      double g; 
      short h;
      A1 sa;
    }B1;
    sizeof(A1)=?;sizeof(B1)=?
    先来看A1,
    a占用四个字节:
    1111
    b占用八个字节,根据原则1,b的起始地址为8的整数倍,现在为1,应该填充七个字节:
    1111**** 11111111
    c占用四个字节,现在满足原则1,
    1******* 11111111 1111
    但是根据原则3,A1的总大小必须是8的倍数,因此,还要填充四个字节:
    1111****11111111 1111****
    所以sizeof(A1) = 24;
    现在来看B1,
    首先e中有两个char型元素,占两个字节:
    11
    int型的f占据四个字节,根据原则1,f的起始地址为4的整数倍,填充:
    11**1111
    double类型的g占八个字节,根据原则1,g的起始地址为8的整数倍,符合,不用填充:
    11** 11111111 1111
    short类型h占据二个字节,符合原则1,根据原则3,填充:
    11** 11111111 11******
    最后sa占据24个字节,其内部最大成员的字节数为8,因此sa的起始地址为8的整数倍,符合
    11** 11111111 11****** 111111111111111111111111
    所有sizeof(B1) = 48;
    上述都是没有#pragma pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义。比如上面的结构体前加#pragma pack(1),则sizeof(A1) = 16; sizeof(B1) = 32;
    有了#pragma pack(1),内存不会再遵循原则1和原则3了,按1字节对齐,即可理解为内部成员占用字节的和。
    那么#pragma pack(2)呢?
    代表按照两个字节对齐,这种情况下,sizeof(A1) = 16; sizeof(B1) = 32
    #pragma pack(4)宏定义下:

    sizeof(A1) = 16; sizeof(B1) = 36


    最后介绍下结构体中的位域。
    字面理解位域就是说某些数据元素并不需要占据一整个字节,只需要占据几位,例如数字8,就只需要占据一个字节的四位即可表示。所谓位域,就是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。
    例如下面的定义:
    struct
    {
     int a:8;
     int b:2;
     int c:6;
    }A; 
    位域a占8位,位域b占2位,位域c占6位,总共占据两个字节。
    关于位域的对齐,有如下几点:

    1. 如果相邻位域类型相同,位宽之和小于类型的sizeof大小,则后面的字段紧邻前一个字段存储,直到不能容纳为止;
    2. 如果相邻位域类型相同,位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
    3. 如果相邻位域类型不同,则vc6采取不压缩方式,dev-c++ 和GCC都采取压缩方式。依然满足结构体内存对齐三个原则中的原则1,在不压缩方式下,如果前一个位域类型有填充,后面的位域类型和前面的位域类型不相同,则填充的区域不能存放放后面的位域,需另开辟空间;而在压缩方式下,填充的区域如果可以放下后者位域,则存放,放不下的情况下再另开辟空间。
    4.一个位域必须存储在同一个字节中,不能跨字节;
    5.如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。
     struct
        {
              char a:2;
              int b:4;
              int c:4;
        }A;
    在不压缩条件下:char类型占据一个字节,而a占用其中两位;根据原则1,b的类型int占据四个字节,应该从4的整数倍处开始存放,所以char后应该填充三个字节,这三个字节虽然能够容纳b,但是必须另开空间,再开辟四个字节的空间存放int,而b占据四个字节,后面c的类型和b的类型相同,所以紧邻b存储,占据四个字节。所以在vc下,sizeof(A) = 8;
    压缩条件下:char类型占据一个字节,而a占用其中两位;根据原则1,b的类型int占据四个字节,应该从4的整数倍处开始存放,所以char后应该填充三个字节,这三个字节能够容纳b和c,因此不需要重新开辟空间,直接在这三个字节上存储,所以sizeof(A) = 4;







  • 相关阅读:
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    深度学习入门笔记(十九):卷积神经网络(二)
    用Excel表达图片如何由像素点构成
    使用you-get爬取各大网站视频
    自从有了这样的可视化报表,我们社区再没有过疫情(内含福利)
    端口测试程序
  • 原文地址:https://www.cnblogs.com/sunp823/p/5601433.html
Copyright © 2020-2023  润新知