• 理解结构体内存对齐并算清楚结构体大小,结构体对齐的意义在哪?


    结构体对齐

    1. 为什么存在结构体对齐呢?

    结构体内存对齐的原因:

    1.平台原因(移植原因):        

    2.效率原因: 

    2. 结构体对齐规则

     重点来了,知道了这些规则后,具体要怎么算一个结构体的大小呢?

    结构体嵌套

    结构体对齐的意义:


    1. 为什么存在结构体对齐呢?

    结构体内存对齐的原因:

    1.平台原因(移植原因): 
           

            不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些特定地址处取某些特定的数据,否则就会抛出硬件异常。也就是说在计算机在内存读取数据时,只能在规定的地址处读数据,而不是内存中任意地址都是可以读取的。


    2.效率原因: 

            正是由于只能在特定的地址处读取数据,所以在访问一些数据时,对于访问未对齐的内存,处理器需要进行两次访问;而对于对齐的内存,只需要访问一次就可以。 其实这是一种以空间换时间的做法,但这种做法是值得的。


    2. 结构体对齐规则

            1.第一个成员在结构体变量偏移量为0的地址处,也就是第一个成员必须从头开始。
            2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数为编译器默认的一个
    对齐数与该成员大小中的较小值。在64位编译器中默认值是8 ,可以在代码前通过#pragmapack (N)修改,使用#pragma pack(show)可以查看对齐值,但修改时N的取值只能设置成(1,2,4,8,16)这几个值
            3.结构体总大小为最大对齐数的整数倍。(每个成员变量都有自己的对齐数)
            4.如果嵌套结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是
    所有最大对齐数(包含嵌套结构体的对齐数)的整数倍。

            不同位的编译器不同类型所占字节不一样,如下图所示。注:以下数据类型对应表供参考,绝大部分是对应的,但具体大小以所用编译器为准!

    但都会满足如下条件:

    sizeof(char) == 1
    sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long)

     重点来了,知道了这些规则后,具体要怎么算一个结构体的大小呢?

    来了,从小白开始,教你们怎么算,这个面试可能会考,特别是c语言岗位的

    以下面的这个结构体为例:

    1. typedef struct test
    2. {
    3. char a;
    4. int b;
    5. int d;
    6. long c;
    7. int *e;
    8. }test_t;

    我们根据对齐规则的第二点,要知道对齐数,具体怎么做呢,在代码上方加入这个

    #pragma pack(show)

    如下所示,我在编译时就会警告,告诉我们对齐数是多少

     知道对齐数了,还要知道每个类型的大小,代码如下

    1. #include
    2. #pragma pack(show)
    3. typedef struct test
    4. {
    5. char a;
    6. int b;
    7. int c;
    8. long d;
    9. int *e;
    10. }test_t;
    11. int main()
    12. {
    13. printf("short int的大小:%zd\n", sizeof(short int));
    14. printf("int 的大小:%zd\n", sizeof(int));
    15. printf("long 的大小:%zd\n", sizeof(long));
    16. printf("long long的大小:%zd\n", sizeof(long long));
    17. printf("char 的大小:%zd\n", sizeof(char));
    18. printf("char* 的大小:%zd\n", sizeof(char*));
    19. printf("float 的大小:%zd\n", sizeof(float));
    20. printf("double 的大小:%zd\n", sizeof(double));
    21. printf("long double的大小:%zd\n", sizeof(long double));
    22. //printf("int的大小:%d", sizeof(int));
    23. //printf("结构体test的大小:%zd\n", sizeof(test_t));
    24. return 0;
    25. }

    会输出如下:和上面表格差不多,我使用的是64位编译器

    1. short int的大小:2
    2. int 的大小:4
    3. long 的大小:8
    4. long long的大小:8
    5. char 的大小:1
    6. char* 的大小:8
    7. float 的大小:4
    8. double 的大小:8
    9. long double的大小:16

            接下来该算结构体大小了,我知道了对齐数为8,要从结构体中定义的变量,从上往下算,我们要明确规则第三点中的每个成员变量都有自己的对齐数

            我们设结构体的内存存储从1开始,实际是从0开始的,为了易理解:

            计算结构体中第1个变量,a为char型,占1个字节。

            计算结构体中第2个变量,b为int型,占4个字节,根据规则对齐规则2(其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处),如果从2地址开始存储,是错误的,起始地址为2,无法整除下一个变量的第一个整型变量大小,2/4 != 0,如下图所示

            所以要从4地址开始存储,4/4 = 1,能整除,存储后,结构体现在大小为1+'3'+4=8;存储结构如下图所示,才是正确的

            计算结构体中第3个变量,c为int型,8/4=2,能整除,从8开始存储,占4个字节,结构体现在大小为8+4=12

            接下来计算结构体中第4个变量,d为long型,占8个字节,从12往后找能被8整除最近的数,就是16啦,所以从16开始存储,结构体现在大小为12+'4'+8=24

            接下来计算结构体中第5个变量,e为指针型,占8个字节,现在总大小为24+8=32,图就不画了,和上面的类似,所以这个结构体test,总共占用字节数为32

    要是结构体中还是有第6个变量呢?如下所示

             有人说了,这不简单吗,+1不就得了么,总共33个字节。不是的!结构体对齐规则第三条中写着:结构体总大小为最大对齐数的整数倍,33显然不是,里33最近的就是8*5=40,那此时结构体大小就为40。

            我打印下结构体中的每个变量地址分配,就知道了,如下图所示,表示的是每个变量存储的地址起点。

     这和我上述分析是一样的,只不过我用的是10进制分析的,编译器是16进制,你们可以算一下。

              有人说了,这不是浪费空间吗,结构体中就多了一个字节的变量,结构导致整个结构体大小多了一个对齐数=8位。确实会这样,这个结构体需要优化,后面会讲,先说一下结构体中的特殊情况。

    结构体嵌套

            就是结构体中嵌套了结构体,也就是套娃,这时候要怎么算呢?我这里用一个简单的例子讲一下,结构体里面套一个结构体。如下所示

     这时候要怎么算呢?

            我们刚刚算到c这里时,这时已经占了12个字节了,接下来到结构体这个对象s了,先算这个嵌套结构体的大小,这个结构体比较简单,第一个是名字的cha*型指针,我的是64位编译器,所以所占字节位8位,第二个位int型变量,所占字节数位4,现在总大小为12byte,再根据结构体对齐规则第三条,结构体总大小为最大对齐数的整数倍,里12最近的就是8*2=16,所以总字节数为16.

            嵌套结构体的大小为16,在结构体test怎么存储呢?此时16已经超过最大对齐数,存储的话能够被最大字节数整除的下一个最近位置开始存储即可。刚刚算到c时,已经占到12这个位置了。所以要从16开始存储,一共占16位,所以总字节数为12+'4'+16=32;

            类型这种一个变量或者对象的大小超过最大对齐数的很多,如string类型的变量,就有固定的大小32位。存储的话,像上面那样套路存储计算,即可;

            接下来计算结构体中第5个变量,本身占8个字节,按上面的套路计算即可,加上去后此时总字节数40;

            计算第六个变量,d为long型,占8个字节,从40开始存储,现在总字节数为48;

            计算第七个变量,d为char型,占1个字节,从41开始存储,现在总字节数为49 ,根据结构体对齐规则3(结构体总大小为最大对齐数的整数倍 ),所以最终结构体总字节数为56.   

    地址分布如下图所示:

             C语言中还有一种情况:结构体中有一种如char  name[16],这种c语言字符串类型的,怎么算呢,这种很简单,他就相当于char 型的字符,有16个,连续排在一起。如下图所示,这种char型一个字符的,基本就是有地方就插,毕竟1能被任何数整除。

            结构体如何计算的已经搞定了,像上面说的,很多时间,结构体中的变量存储会有很多空隙,导致浪费存储空间,怎么办呢?

            可能很多人都想到了,把占用字节数小的变量放前面,大的放后面。

    如果没有考虑,对结构体对齐不了解的话,可能变量乱放,如下图所示,这个结构体test的大小为72字节

             如果有考虑,对结构体对齐了解的话,变量不乱放,对结构体的成员位置进行优化,如下图所示,此时这个结构体test的大小为56字节,比不优化的少了16个字节

     

             可能有人会说:不优化结构体,平时写代码时运行时也没什么感觉

    结构体对齐的意义:

            其实,在嵌入式领域,结构体是经常用到的,如单片机,stm32等,为什么要优化结构体呢?

            1.嵌入式领域的芯片,他的容量是很小的,不能我们电脑的磁盘那么大,可以节省空间。

            2.芯片中的数据读取,是有特定区域的,如果不优化结构体,结构体的数据读取的时候有些要拼接,这就浪费了时间,而芯片这种东西,效率是要求很高的。可以提高读取效率

            对于软件开发人员,不像嵌入式的芯片,很多软件并不在意这点运行所用的空间和时间,这是个细节吧。得要有这个意识,可能面试时问到,扣你细节时,回答对了,offer就是你的!

  • 相关阅读:
    证书格式说明
    Redis——Java客户端配置
    keycloak~对接login-status-iframe页面判断用户状态变更
    三分求单峰/单谷函数极值
    故障诊断实验台 | PT500mini轴承齿轮箱转子故障实验台
    定制ASP.NET Core的身份认证
    打破内卷宿命,电商只能出海,必须出海!
    CentOS 7.9如何禁止内核自动更新升级
    shell脚本学习
    【ArcGIS】11 河道断面提取
  • 原文地址:https://blog.csdn.net/qq_44667165/article/details/126757840