当前位置 博文首页 > KOOKNUT的博客:C/C++结构体内存粒度对齐?结合调试信息来看

    KOOKNUT的博客:C/C++结构体内存粒度对齐?结合调试信息来看

    作者:[db:作者] 时间:2021-06-14 09:42

    C语言或者C++内存粒度对齐是我们经常提到的问题,而对内存的有效合理的利用,必然会使我们写出来的代码更加高效。这几天写代码过程中出现了一个bug,就是由于内存粒度原因导致,所以总结一下。
    首先我们来看一下理论知识(我个人理解的):
    结构体中数据成员内存对齐的原则:按照一个结构体中的成员中最大字节数整数倍对齐,比如在x86下,出现int(4字节) char(1字节) double(8字节),则肯定是按照最大的8字节整数倍对齐,8字节就相当于一个基准一样,如果遇到的字节数可以在8字节内存放,那就ok,否则需要重新申请8字节来存放,至于说是8的几倍,那需要看数据存放的顺序,内存满足多插少补的原则。
    接下来我就结合代码和调试信息来说明内存粒度对齐的相关问题:
    以下提到的测试数据,基于x86
    eg1:

    typedef struct _STRUCT_A_
    {
    	char a; 
    	int  b;
    	char c;
    }STRUCT_A;
    

    面对以上的一个结构体,初学者可能认为是1+4+1总共6个字节。那么我们来实际看看它的大小究竟是多少
    在这里插入图片描述
    它是12字节的大小,那么我们再来看看它的内存分布情况:
    在这里插入图片描述
    确实初始化为12个字节的0,那我们赋值字符a,int值5以及字符c之后,内存中又是如何存放的呢?
    在这里插入图片描述
    那我们可以如何优化这个结构体,让他它占有更少的内存呢?我们可以写成以下两种形式:
    eg2:

    typedef struct _STRUCT_A_
    {
    	int  b;
    	char a; 
    	char c;
    }STRUCT_A;
    typedef struct _STRUCT_A_
    {
    	char a; 
    	char c;
    	int  b;
    }STRUCT_A;
    

    我们再来看看内存中的情况以及结构体大小为多少:
    在这里插入图片描述
    在这里插入图片描述
    以上证实了我们一开始的说法,当char存放占4字节基准为1字节,接下来的成员还是char,拿它就可以继续存放,存放第二个char之后,还有2字节空余,但是遇到了4字节的int,不够了,只能存放在另外的4字节内存中了。
    那我们有没有办法,就是不管我们怎么存放,让它都有固定的内存对齐粒度呢?当然是可以的:
    eg3:

    #pragma pack(push)
    #pragma pack(2)
    typedef struct _STRUCT_A_
    {
    	char a;
    	int  b;
    	char c;
    }STRUCT_A;
    #pragma pack(pop)
    

    我们可以使用以上代码,使内存粒度对齐按照2字节对齐,我们来验证一下:
    在这里插入图片描述
    在这里插入图片描述
    当然也可以按照1字节对齐,只需要修改pack中的值为1,如下:
    eg4:

    #pragma pack(push)
    #pragma pack(1)
    typedef struct _STRUCT_A_
    {
    	char a;
    	int  b;
    	char c;
    }STRUCT_A;
    #pragma pack(pop)
    

    在这里插入图片描述
    在这里插入图片描述

    但是我们在实际情况中,可能会遇到更加复杂点的情况,比如结构体的成员是结构体或者联合体。这种情况又是如何对齐的呢?我们来看一个稍微复杂一点的结构体:
    eg5:

    typedef struct _STRUCT_A_
    {
    	uint32_t a;
    	uint32_t b;
    }STRUCT_A;
    typedef union _UNION_A_ {
    	char*  m1;
    	STRUCT_A m2;
    }UNION_A;
    
    typedef struct _STRUCT_B_
    {
    	UNION_A a;
    	UNION_A  b;
    	UNION_A c;
    	UNION_A d;
    	uint16_t e;//2字节
    }STRUCT_B;
    

    按照我们的想法,那应该UNION_A所占内存大小为8字节,那么STRUCT_B应该按照8字节对齐,那么5个数据成员,大小应该使5*8=40字节,但是实际情况好像并不是这样:
    在这里插入图片描述
    怎么会出现36这个情况呢?原因出现在STRUCT_A中,虽然unionA的大小确实为8字节,但是其8字节的主要原因是STRUCT_A是8字节,那STRUCT_A中是两个4字节的成员,所以内存粒度对齐还是4字节,这样就不难理解最后的STRUCT_B为36字节了,其中前4个成员是32字节,最后一个是2字节,但是按照4字节对齐,所以是36字节。

    “山不向我走来,我便向它走去”