美文网首页C++
结构体内存对齐

结构体内存对齐

作者: 黑夜no烟丝 | 来源:发表于2019-02-15 19:08 被阅读15次

    为什么要进行内存对齐

    1.为了cpu读取更快
    使CPU的访问内存数据的速度加快,如果一个内存的数据是经过对齐的,那么CPU可以整块地将内存数据(为了提升效率,cpu读取内存数据都是按块读取的,块大小可以设置为已知数据类型的大小如1,2,4,8,16等)读到寄存器中。
    2.方便移植
    不是所有的硬件平台都能访问任意地址上的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常

    结构体内存对齐的原理

    1.偏移量
    偏移量指的是结构体变量中成员的地址和结构体变量地址的差。结构体大小等于最后一个成员的偏移量加上最后一个成员的大小,比如下面这个结构体,默认按4个字节大小对齐

    struct stu
    {
        char a;
        int b;
        short c;
    };
    

    这里a的地址就是结构体stu的的地址,所以a的偏移量为0,占1个字节,但默认对齐大小是4,所以还要填充3个字节到3,b的偏移地址就是4,整好不用填充,c的偏移量就是8,大小为2,因为是最后一个成员,后面就不用填充了,所以这个结构体的大小应该为12个字节
    2.对齐原则
    实际上,由于存储变量时地址对齐的要求,编译器在编译程序时会遵循两条原则:一、结构体变量中成员的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍) 二、结构体大小必须是所有成员大小的整数倍

    举例说明

    普通对齐

    接着上一个例子,我们都知道默认系统默认是4,这个数字是可以更改的,但也不能随意更改,必须是有对应数据类型的字符大小,这里我们先修改为1,再修改为8,再修改为5,看下效果

    #include<stdio.h>
    
    #pragma pack(1) //括号里面的数字表示你设置的内存对齐大小
    int main()
    {
        typedef struct stu 
        {   
            char a;
            int  b;  
            short  c;  
        }A; 
        
        printf("%lu\n",sizeof(A));
    
        return 0;
    }
    

    运行以后我们发现结果是7,如果改为8,发现还是12,哎?不应该是18吗?这里注意一下:如果#pragma pack (n)中指定的n大于结构体中最大成员size,则其不起作用,结构体仍然按照size最大的成员进行对界。所以我们这个结构体最大成员size就是int类型,只有4,所以设置任何高于4的值都被当做4。然后我们这里再将值改为2,发现结构体大小为8,这就完美对应上了,如果设置为1或者3呢?这里我们试一下:

    struct.c:3:9: warning: expected #pragma pack parameter to be '1', '2', '4', '8',
          or '16' [-Wignored-pragmas]
    #pragma pack(3)
    

    发现系统会给出一个警告提醒你,然后还是被当做4大小处理了,结果还是12。

    位域对齐

    为了最大限度的节约内存和提高cpu的读取速度,位域的使用被提上了日程,从上一个例子我们可以看到在对齐时作了填充增加了不少内存开销,为了降低这种开销,位域的使用显得很重要。
    字面理解位域就是说某些数据元素并不需要占据一整个字节,只需要占据几位,例如数字8,就只需要占据一个字节的四位即可表示。所谓位域,就是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。
    先说明一下位域对齐的原则:

    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;

    回到我们之前那个例子,我们将其附上位域大小

    #include<stdio.h>
    
    int main()
    {
        typedef struct stu 
        {   
            char a:2;
            int  b:8;  
            short  c:2;  
        }A; 
        
        printf("%lu\n",sizeof(A));
    
        return 0;
    }
    

    运行以后结果为4,为4就是对的,我们来看一下,首先这是三个相邻域都不相同的,然后,看大小2比特+8比特+2比特小于sizeof(int),加上我编译程序用的GCC/G++,所以是压缩的,结果就是4。

    相关文章

      网友评论

        本文标题:结构体内存对齐

        本文链接:https://www.haomeiwen.com/subject/suimeqtx.html