C语言中结构体大小的计算(内存对齐详解)

首先来看一个例子;

struct S
{
	char a;
	int b;
	char c;
};

我们先来计算一下这个结构体的大小,如果不存在内存对齐这个问题,按理说这个结构体应该占(1+4+1)6个字节;然而事实上它占了12个字节,为什么?我们需要解决下面几个问题。

1.为什么存在内存对齐

  • 平台原因(移植问题):一些资料上是这样说的,“不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些特定地址处取某些特定的数据,否则就会抛出硬件异常”。也就是说在计算机在内存读取数据时,只能在规定的地址处读数据,而不是内存中任意地址都是可以读取的。
  • 性能问题:正是由于只能在特定的地址处读取数据,所以在访问一些数据时,对于访问未对齐的内存,处理器需要进行两次访问;而对于对齐的内存,只需要访问一次就可以。
    这里写图片描述
    看上面的图,因为规定只能在特定地址处读数据,所以我们假设只能在4的倍数的地址处读数据,那么可以访问的地址分别为0、4、8、12、16等,这时我们给出刚才的结构体,在处理器访问第一个结构体元素(char) a 时,它会从0号下标的地址处访问a,而下个结构体元素 b 为int型,它占了4个字节,很显然,要访问它时需要先从0号下标的地址开始访问,之后还得取它后面3个字节的内容作为一部分,之后再从4号下标的地址处访问1个字节,共同的内容组成了元素 b ,很明显,访问 b 时要进行两次访问,并且很麻烦。这时如果存放数据时内存对齐了,则只需访问一次就可以读取到 b 的内容。第二幅图就是内存对齐的情况,在访问每个数据时,都可以一次性访问完毕。
    ###2.内存对齐后,怎么计算结构体的大小 ####
    1. 第一个成员在与结构体变量偏移量为0的地址处
    2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数=编译器默认的一个对齐数与该成员大小的较小值,在VS环境下默认值为8,在Linux环境下默认值为4。
    3. 结构体的总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
    4. 如果嵌套了结构体的情况,被嵌套的结构体对齐到其自身对齐数的整数倍处(结构体的对齐数就是其内部成员中最大的对齐数),此时结构体的整体大小就是所有最大对齐数(含被嵌套结构体的对齐数)的整数倍。

###看上面的图 ##
利用这四个规则就可以计算结构体的大小。这时参照上面的图中第二种情况来计算,对于上面的结构体,先定义了char类型的 a ,规则1说第一个元素在偏移量为0的地址处,即它对齐到0号地址处的位置,其占一个字节;第二个元素要对齐到自己对齐数的整数倍处,因为第二个元素的大小为int型,占4个字节,而VS默认对齐数是8,取最小的数,所以 b 的对齐数为4,规则2说要对其自身对齐数(4)的整数倍,而此时 a 占了一个字节,下个地址依次为2、 3 、4等,当走到4号地址处时,此时4能被4整除,所以第二个元素对齐在4号下标的地址处,占用4个字;接下来,第三个元素为char类型的 c ,c的大小为1,和8比较取小的数,所以 c 的对齐数为1,而此时 b 存放完之后的下个地址为9号地址,根据规则2,9是1的整数倍,所以 c 直接存放在9号开头的地址处。接下来,计算它们的大小,分别为1(char)+3(偏移量)+4(int)+1(char)=9,而正确的结果为12,原因在于规则3,规则3规定结构体的总大小为最大对齐数的整数倍,上面的3个元素中,它们的对齐数分别为0、4、1。所以最大对齐数为4,而结构体大小要整除最大对齐数,刚才计算出结构体的大小为9,很明显不能整除,所以结构体的大小为1(char)+3(偏移量)+4(int)+1(char)+3(偏移量)=12。

###再来看第二个例子 ##

struct S1
{
	char c1;
	char c2;
	int i;
};
struct S2
{
	char c1;
	struct S1 s3;
	double d;
};

首先计算出结构体S1的大小为1(char)+1(char)+2(偏移量)+4(int)=8,S1的对齐数就是其结构体成员中最大的对齐数,即4,在结构体S2中,对齐数分别是0、4、8;首先 c1 对齐在0号地址的位置,其占用1个字节,根据规则4,嵌套的结构体S1要对齐到自身对齐数4的整数倍处,所以此时结构体对其在4号地址处,其大小是8个字节,之后,d 的对齐数为8,所以它要对齐到16号地址处,所以此时计算出S2的大小是1(char)+3(偏移量)+8(struct S1)+4(偏移量)+8(double)=24,结构体S2中个成员的对齐数分别为0、4、8,因为24是8的倍数,符合规则4。所以S2的总大小就是24个字节。

#pragma pack(n) 表示设置为n字节对齐。

  • 至此,结构体的大小就是利用上面的4个规则来计算,刚开始计算有点繁琐,熟练掌握后,很好计算。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值