struct内存对齐+例题帮助理解

转载自https://www.zhihu.com/question/27862634/answer/208895189
字节对齐主要是为了提高内存的访问效率,比如intel 32位cpu,每个总线周期都是从偶地址开始读取32位的内存数据,如果数据存放地址不是从偶数开始,则可能出现需要两个总线周期才能读取到想要的数据,因此需要在内存中存放数据时进行对齐。 因为处理器读写数据,并不是以字节为单位,而是以块(2,4,6,8,16字节)为单位进行的,如果不进行内存对齐,本来只需要一次进行的访问,可能要好几次才能完成。
我们假设处理器一次从内存中取四个字节,显然没有经过内存对齐的话,要取两次才能得到i。当结构体足够复杂的时候,效率明显降低。
在这里插入图片描述

通常我们说字节对齐很多时候都是说struct结构体的内存对齐,比如下面的结构体:

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

在32位机器上char 占1个字节,int 占4个字节,short占2个字节,一共占用7个字节.但是实际真的是这样吗?

我们先看下面程序的输出:

#include <stdio.h>

struct A{
    char a;
    int b;
    short c;
};
int main(){
    struct A a;
    printf("A: %ld\n", sizeof(a));
    return 0;
}

测试输出的结果是A: 12, 比计算的7多了5个字节。这个就是因为编译器在编译的时候进行了内存对齐导致的。
内存对齐主要遵循下面三个原则:

  1. 结构体变量的起始地址能够被其最宽的成员大小整除
  2. 结构体每个成员相对于起始地址的偏移能够被其自身大小整除,如果不能则在前一个成员后面补充字节
  3. 结构体总体大小能够被最宽的成员的大小整除,如不能则在后面补充字节
    其实这里有点不严谨,编译器在编译的时候是可以指定对齐大小的,实际使用的有效对齐其实是取指定大小和自身大小的最小值,一般默认的对齐大小是4。
    再回到上面的例子,如果默认的对齐大小是4,结构体a的起始地址为0x0000,能够被最宽的数据成员大小(这里是int, 大小为4,有效对齐大小也是4)整除,姑char a的从0x0000开始存放占用一个字节即0x0000~ 0x0001,然后是int b,其大小为4,故要满足2,需要从0x0004开始,所以在char a后填充三个字节,因此a对齐后占用的空间是0x0000~ 0x0003,b占用的空间是 0x0004~ 0x0007, 然后是short c其大小是2,故从0x0008开始占用两个字节,即0x0008~ 0x0009。 此时整个结构体占用的空间是0x0000~ 0x0009, 占用10个字节,10%4 != 0, 不满足第三个原则,所以需要在后面补充两个字节,即最后内存对齐后占用的空间是0x0000~0x000B,一共12个字节。
struct A {
char a;
char b;
char c;
};
 
struct B {
int a;
char b;
short c;
};
 
struct C {
char b;
int a;
short c;
};
 
#pragma pack(2)
struct D {
char b;
int a;
short c;
};

根据三条法则我们来推断一下每个结构体的大小
A起始地址为0,最宽的成员变量大小就是1,可以被1整除。分别放在012的位置。
B起始地址为0,最宽成员大小为4,可以被4整除,int放在0123的位置,char从4开始,4可以被当前char的大小整除,short从5开始,5不能被short(2)整除,所以short从6开始,放在67。总的结构体大小为8,可以被最宽的4整除。
C起始地址为0,最宽的成员大小为4,可以被4整除。char放在0的位置,int本来应该从1开始,但是根据法则2,当前成员变量的起始地址应该可以整除当前成员变量大小,不能的话要在前一个成员变量后面补字节,所以在char后面补字节,一直补到3。int从4开始,占据4567,short从8开始,满足法则2,占据89。最后大小为10,不满足法则3总大小是最宽成员大小的整数倍,再补2,一共是12。
D#pragma pack(n)就是手动定义内存对齐系数,D结构体自定义为2,
如果n位对齐,那么n位以下的所有基本类型,地址必须为本类型长度的整数倍;
n位以上的基本类型,保持n位对齐。
所以D的起始地址为0,char占据的是0。int从1开始是不行的,因为int=4大于2,所以是n位以上的按照n位对齐,int从2开始,占据2345。最后short=2从6开始,占据67。最后大小是8.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值