02内存对齐之结构体在内存所占字节数求法(学会这篇所有内存对齐面试题基本可以解决)

02内存对齐之结构体在内存所占字节数求法

1 结构体在内存所占字节数求法,需按成员类型划分来求
1)简单类型(即结构体没有内嵌结构体):
先求出总内存:
成员1类型所占字节数 x (0~n);
成员2类型所占字节数 x (0~n);
成员3类型所占字节数 x (0~n);
…以此类推,以0开始是因为根据上一篇我们知道,结构体内部是以0作为开始偏移,即绝对零地址。
最后:
判断结果是否符合最大类型字节数的倍数,若是直接取,不是则补0取最近的数。
注:若是数组,最大字节数按单个算,例如int a[10];最大也是4字节,char cc[20];按1字节算。

例1

typedef struct TEACHER {
	char name[4];
	int  age;
	int  id;
}Teacher;

成员1:
4 x 0 =0; // 成立,开始偏移必量为0,即存储首地址为0,共存储4个字节。

成员2:
4 x 0 =0; //不成立,因为age以0开始存储的话,则与成员name重复了,所以乘数加1。
4 x 1 = 4;//成立,即age的首地址为4,也就是偏移量为4,共存储4个字节。

成员3:
4 x 0 = 0; // 不成立,内存已被存储。
4 x 1 = 4; // 不成立,内存已被存储。
4 x 2 = 2; // 成立,共存储4个字节。
加起来12个字节,且12符合最大成员字节数4的倍数。所以该结构体共占4x3=12个字节。即内存情况如下:
na na na na
ag ag ag ag
id id id id

例2 (简洁说明)

typedef struct DATA2 {
	double a;
	int    b;
	short  c;
}Data2;

8 x 0 = 0; // 存8个(内存存0-7) 下一个必须为8
4 x 2 = 8; // 存4个后,下一个必须是12之后。
2 x 6 = 12; // 存2个

上面看着是总共14个,但不符合8的倍数,所以补零后变成16个。即内存情况如下:
a a a a a a a a
b b b b c c 0 0 // cc仍会存进b之后,不会新起一行存储。

例3 超过64位8字节的成员。

typedef struct DATA3 {
	char   a[9];    // 实际类型就是char 为1字节
	int    b;
	short  c;
}Data3;

9 x 0 = 0; //存了9个,8开始的偏移量剩余3不足存储一个int所以会补0
4 x 3 = 12;//存4个字节
2 x 8 = 16;//存了两个
每次存了多少个,必须用图画出来。即
a a a a //每行四个是因为最大字节类型是int=4字节
a a a a
a 0 0 0 //只剩余3个字节不足以存储int
b b b b
c c 0 0 //存两个,并补两个0
由于上面已经补了0,得出的结果肯定符合4的倍数,所以该结构体共20个字节。

例4 与例3同样道理。

typedef struct DATA4 {
	char   a[60];    // 实际类型就是char 为1字节
	int    b;
	short  c;
}Data4;

这里直接给出答案68,大家自己尝试列出方程核内存存储图。

2) 内嵌结构体方法:
注意三点即可:
1)最大对齐字节数为两个结构体的最大类型字节数;
2)遇到内嵌结构体,它的前一个数据存储不管是否有剩余地址,都不能再用于存储内嵌结构体的成员,必须全部补0。该内嵌结构体成员计算完也一样,也要补零,不能再存储大结构体剩余的数据。(后面例子说明)
3)在算内嵌结构体所占字节数大小时,其偏移起始地址必须加上之前的偏移总量。(后面例子说明)偏移起始地址即每次算出的结果,例如第一次算时为0。

例1

typedef struct SON {
	int    x;
	double y;
	float  z;
}Son;

typedef struct CONTAIN1 {
	char   a[2];
	int    b;
	double c;
	short  d;
	Son    e;
}Contain1;

步骤:
1)先找两个结构体中最大类型字节数为double=8对齐。
2)开始按第一种求法正常求。
1 x 0 = 0;
4 x 1 = 4;
8 x 1 = 8;
2 x 8 =16; //至此,short是内嵌结构体的前一个数据,不管有无剩余数据都必须补0,所以还没算内嵌结构体下,内存情况为:
a a 0 0 b b b b //看上面的计算,aa后是需要补零的。
c c c c c c c c
d d 0 0 0 0 0 0 // 即上面第2)点的注意。
现在是共24字节的偏移总量。

3)开始算内嵌结构体的所占字节数。
24 + 4 x 0 = 24; //即第3)注意。计算内嵌结构体必须加上之前的偏移总量。
24 + 8 x 1 =32;
24 + 4 x 4 = 40; // 存完4个后,然后补0符合8的倍数,即44+4=48,最终答案是48。

内存的最终情况:
a a 0 0 b b b b;
c c c c c c c c;
d d 0 0 0 0 0 0;
x x x x 0 0 0 0;
y y y y y y y y;
z z z z 0 0 0 0;

例2
证明第二点的后半句话,即若结构体内嵌于一个结构体的中间(即头尾除外),算完该内嵌结构体成员,剩余地址必须补0,不能再存储大结构体的剩余数据。

typedef struct CONTAIN2 {
	char   a[2];
	int    b;
	double c;
	Son    e;      // 把Son放上来
	short  d;
}Contain2;

1 x 0 = 0;
4 x 1 = 4;
8 x 1 = 8; //遇到结构体了,需计算总偏移量,为4+4+8=16。
16 + 4 x 0 = 16;
16 + 8 x 1 = 24;
16 + 4 x 4 = 32; // 这里存了float的4个字节,内存情况为:

a a 0 0 b b b b;
c c c c c c c c;
x x x x 0 0 0 0;
y y y y y y y y;
z z z z ? ? ? ?;//现在的问题是,若总字节为5x8=40字节,说明问好"?"没有补0,而是可以存储大结构体的剩余数据short;若总字节数为6x8=48的话,说明后面补0了,并且新开一行存储大结构体剩余数据。而结果正是后者。即注意的第2)点是结论。

short之前的总偏移量为40。所以最后short的计算为:
40 + 2 x 0 = 40; //存2两个 其余补0满足8的倍数即最后答案为48。

// 内存最终情况:
a a 0 0 b b b b;
c c c c c c c c;
x x x x 0 0 0 0;
y y y y y y y y;
z z z z 0 0 0 0;
d d 0 0 0 0 0 0;

3 完整代码调试:


#include  <stdio.h>

typedef struct DATA1 {
	char name[4];
	int  age;
	int  id;
}Data1;

typedef struct DATA2 {
	double a;
	int    b;
	short  c;
}Data2;

typedef struct DATA3 {
	char   a[9];    // 实际类型就是char 为1字节
	int    b;
	short  c;
}Data3;

typedef struct DATA4 {
	char   a[60];    // 实际类型就是char 为1字节
	int    b;
	short  c;
}Data4;



//内嵌结构体求法
typedef struct SON {
	int    x;
	double y;
	float  z;
}Son;

typedef struct CONTAIN1 {
	char   a[2];
	int    b;
	double c;
	short  d;
	Son    e;
}Contain1;

typedef struct CONTAIN2 {
	char   a[2];
	int    b;
	double c;
	Son    e;      // 把Son放上来
	short  d;
}Contain2;

int main() {

	printf("%d\n", sizeof(Data1));   //12
	printf("%d\n", sizeof(Data2));   //16
	printf("%d\n", sizeof(Data3));   //20
	printf("%d\n", sizeof(Data4));   //68

	//内嵌求法
	printf("%d\n", sizeof(Contain1));//48
	printf("%d\n", sizeof(Contain2));//48

	return 0;
}

4 自己设定最大内存对齐方法:

##pragma pack(2) //必须是2的倍数,最大是64位8字节。

上面表示最大对齐若超过2字节,仍以2字节计算,否则不用。
例如最大对齐是1字节,则以1字节;若最大对齐字节是4字节,以2字节对齐。

5 总结:
看到这里理解透的话,我相信你面试这种类型题基本有手就行。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值