• C语言结构体的内存对齐问题


    在C语言开发当中会遇到这样的情况:

     1 #include <stdio.h>
     2 
     3 struct test {
     4     int a;
     5     char b;
     6 };
     7 
     8 int main(int argc, const char * argv[])
     9 {
    10     printf("%lu
    ", sizeof(struct test));
    11     return 0;
    12 }

    sizeof操作输出的结果是8,可是int和char类型的长度加起来只有5,那么为什么输出了8呢?

      这就牵扯到结构体的内存对齐问题,事实上,结构体中的变量在内存当中并不是以一种连续紧凑的方式存储的。

      上面这个例子是一种最简单的情况,来看一下它的内存排布:

      0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xA 0xB 0xC 0xD 0xE 0xF
    0x10 a b 空位                
    0x20                                

      可见,从0x15到0x18都是空位,为什么要这样存储呢?因为计算机字长为4,处理结构体变量时,需要一次读入32位,也就是4个字节,那么假定两个结构体变量a和b,a从0x10开始,b从0x15开始的话,则需要两次寻址,第一次是读出0x14到0x17,取出后三位,第二次是从0x18到0x1B,取出第一位,然后做位运算,由于变量处理很频繁,这样就增加了性能的开销,那么只能采取用空间换时间的策略了。

      假设有两个结构体变量,那么内存排布是这样的:

      0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xA 0xB 0xC 0xD 0xE 0xF
    0x10 a b 空位 a b 空位
    0x20                                

      这便是内存对齐的第一个方面——结构体的末尾如果没有对齐,则会留出空位来,为排在它前后的变量提供寻址上的便利。

      事实上,结构体的大小,必然是其中占用空间最大的类型的长度的整数倍。

      接下来改变一下我们的结构体:

    1 struct test {
    2     char b;
    3     int a;
    4 };

    它的长度同样是8,内存排布如下:

      0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xA 0xB 0xC 0xD 0xE 0xF
    0x10 b 空位 a                
    0x20                                

      同样基于上面提到的原因,如果将a放在0x11的位置,需要两次寻址。那么,结构体内存排列的一个重要原则就是要得到最少的寻址次数,显然,为了方便找到int型的a,需要在b后面空出3个字节的空间来。

      下面举一个稍微复杂一点的例子:

    1 struct test {
    2     char a;
    3     int b;
    4     char c;
    5     char d;
    6     double e;
    7     char f;
    8 };

    它的长度是32,看一下内存排列:

      0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xA 0xB 0xC 0xD 0xE 0xF
    0x10 a 空位 b c d 空位
    0x20 e f 空位

      为了让变量b寻址方便,在a的后面空出了3个字节,而c和d无论如何都要寻址一次,所以顺序排下去就可以了,而e长度为8,按说可以从0x1C开始排,但是这里需要和类型本身的长度8进行对齐,则排在了0x20的位置,于是乎d的后面又空出了6个字节的空间,最后f排在e的后面,之后为了结构体本身和最长类型的长度8对齐,后面又留出了7个字节的空位。

      显而易见结构体当中变量的顺序是会影响存储空间的,在设计结构体的时候需要进行优化,比如将上面的结构体改写成下面的形式可以节省空间:

    1 struct test {
    2     char a;
    3     char c;
    4     char d;
    5     char f;
    6     int b;
    7     double e;
    8 };

    这种排列只占用16字节,内存如下:

      0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xA 0xB 0xC 0xD 0xE 0xF
    0x10 a c d f b e
    0x20                                

      可见,内存对齐是一种空间换时间的手段,当然,为了节约内存,可以通过如下代码令结构体紧凑排列:

    1 #pragma pack(1) 
  • 相关阅读:
    sh_09_字典的定义
    sh_08_格式化字符串
    sh_07_元组遍历
    sh_06_元组基本使用
    sh_05_列表遍历
    sh_04_列表排序
    sh_03_列表的数据统计
    图片懒加载
    UA池和ip代理池
    爬虫篇 --- 分布式爬虫
  • 原文地址:https://www.cnblogs.com/Steak/p/3660403.html
Copyright © 2020-2023  润新知