结构体存储内存对齐

转自: 解析C语言结构体对齐(内存对齐问题)

 
  结构体对齐原因有很大部分是因为计算机扫描的内存单元个数,也就是数据总线的大小。

   原则1:数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。

   原则2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(structa里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)

   原则3:收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。


   这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。

    例1:struct{
                 shorta1;
                 shorta2;
                 shorta3;
                }A;


         struct{
                long a1;
                short a2;
                }B;

         sizeof(A)= 6; 这个很好理解,三个short都为2。

         sizeof(B)= 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。

    例2:structA{
                 inta;
                 charb;
                 shortc;
                 };


         structB{
                 charb;
                 inta;
                 shortc;
                 };

        sizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。

        sizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。

   深究一下,为什么是这样,我们可以看看内存里的布局情况。

                                                   c
       A的内存布局:1111,    1*,     11

                                                      c
        B的内存布局:1***,    1111,   11**

   其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。

   B中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。

   再看一个结构中含有结构成员的例子:

    例3:structA{
                 int a;
                 double b;
                 float c;
                 };

         structB{
                 char  e[2];
                 int  f;
                 double   g;
                 short     h;
                 structA   i;
                 };

        sizeof(A) = 24;这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。

                                         a               b              c

                                     1111****,11111111,11******

                             (a占4字节,b占8字节,b开始的位置要是自己的整数倍,所以b要在结构体指针为x%8=0的地方,因为a只占到了4,所以顺延的第一个适合的位置为8,b的起始位置为8,前面补齐;c为4,开始位置要在x%4=0的位置,前面为16位,指针位置15,第16位为顺延的第一个合适位置,所以c开始位置为16,又因为结构体大小是整个结构体最大元素长度的整数倍,所以c的后面要补齐6位(个人见解 *——*))

        sizeof(B) = 48; 看看B的内存布局。

                                                                                         
        B的内存布局:11* *,1111, 11111111, 11 * * * * **,1111* * * *, 11111111, 1111 * * * *

   省略宏与结构体中含位域字段两种情况说明,祥见原文。

   考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体系结构的  对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。

    最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间。

在C语言中,结构体内存对齐机制是为了提高内存访问效率而设计的。内存对齐规则确保了结构体成员变量在内存中的存储位置符合一定的对齐要求,从而避免因访问未对齐数据而导致的性能下降或硬件异常。 ### 结构体内存对齐的基本规则 1. **成员对齐**:每个结构体成员变量的起始地址必须是其对齐数(alignment)的整数倍。对齐数通常是该类型大小的整数倍。例如,`int` 类型通常需要 4 字节对齐,`double` 类型通常需要 8 字节对齐。 2. **结构体总大小对齐**:结构体的总大小必须是其内部成员中最大对齐数的整数倍。这意味着在最后一个成员之后可能会有填充字节(padding),以确保整个结构体的大小满足对齐要求。 3. **编译器对齐设置**:不同的编译器可能会有不同的默认对齐方式,也可以通过编译器指令(如 `#pragma pack`)来改变对齐方式。 ### 示例说明 假设有一个结构体如下: ```c struct Example { char a; // 1 byte int b; // 4 bytes short c; // 2 bytes }; ``` 按照默认的对齐规则,`char` 类型的对齐数为 1,`int` 类型的对齐数为 4,`short` 类型的对齐数为 2。 - `a` 占用 1 字节,起始地址为 0。 - `b` 需要 4 字节对齐,因此从地址 4 开始,占用 4 字节(地址 4~7)。 - `c` 需要 2 字节对齐,因此从地址 8 开始,占用 2 字节(地址 8~9)。 由于结构体的总大小必须是最大对齐数(这里是 `int` 的 4 字节对齐)的整数倍,因此结构体的总大小为 12 字节(地址 0~11),其中 `a` 后面填充了 3 字节以保证 `b` 的起始地址是 4 的倍数。 ### 自定义对齐方式 可以通过 `#pragma pack` 指令来改变结构体对齐方式。例如: ```c #pragma pack(1) // 设置为 1 字节对齐 struct PackedExample { char a; int b; short c; }; #pragma pack() // 恢复默认对齐方式 ``` 在这种情况下,结构体将不会有填充字节,总大小为 1 + 4 + 2 = 7 字节。 ### 结构体对齐的影响 - **性能影响**:合理的内存对齐可以提高程序的运行效率,尤其是在处理大量数据时。 - **可移植性影响**:不同的平台和编译器可能有不同的对齐规则,因此在跨平台开发时需要注意对齐问题。 ### 总结 结构体内存对齐机制确保了数据在内存中的高效访问,同时通过填充字节来满足对齐要求。理解这些规则有助于优化程序性能和内存使用,特别是在嵌入式系统或高性能计算中尤为重要。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值