C语言深入探讨实战篇之结构体字节对齐(四)

在有些编程的时候,很多简单的数据类型之间有关系,如一个学生管理系统中,学生的身高,体重,名字等这些都要变量存起来,如果分别定义变量,在编程的时候比较乱,所以通常的做法是定义一个结构体,然后把这些东西放到结构体里面,要管理一个学生就操作这个结构体就行了。前面我们分析了int,float多个字节中字节的排列顺序。同样在结构体当中是由很多的基本数据类型组合起来的,这些里面也有些规则,在定义一个结构体后对结构体变量共占用多少字节内存,变量在内存中的布局是怎么样,如果这些清楚的话,运用指针去对这些数据处理非常方便,也是对C深入理解必须掌握的。

为什么要引入内存对齐?可以提高存储效率,CPU要访问某个数据,可以减少访问次数,提高访问速度,但是速度的提高会损失些内存,后面我们就知道了。

每个编译环境下,都会默认指定一个对齐的值;

每个结构体都有一个自身对齐的值(这个值是结构体内部所有字段中占字节最长的那个字段的尺寸值),如果在VC中结构体

struct tagA

{

char cNum;

int iAge;

char cCount[5];

}A1;

这个结构体int变量占字节最长(数组按基本类型算不能按数组长度算,char cCount[5]还是1)---4个字节,所以这个结构体的自身对齐的值是4,程序在编译分配内存的时候会从默认指定的对齐值和具体该结构体自身对齐值2个值中选出较少的那个值来分配内存,在VC中默认值为8,在Keil中默认值是1

所以A1在VC中分配内存是选用的是min(8,4),在Keil中选用的是min(1,4)来分配A1这个结构体。姑且称这个长度为L

编译的时候,首先选结构体首地址也不是乱选的,首先在内存中选到能够被结构体内部最长那个字段整除的地址

(VC中具体到这个例子就是4的倍数的地址,Keil中就是2的倍数的地址),然后开始依次排字段,结构体内部前面的字段排在靠近该结构体的地址的前面,先排cNum,接着iAge,最后排cCount[5]。

排的时候又有规则,对于基本数据类型长度少于等于L的字段必须排在能被自身长度整除的地址上不连续的部分用空字节补齐(具体到这里iAge这个字段为4,必须排在能被4整除的地址上,但是首地址也是被4整除,cNum只需要1个字节,这个时候在cNum后面就需要补3个空字节,这些空字节就是需要浪费的内存,但是用这些内存换取些效率还是值得的),对于基本数据类型长度大于L的字段必须排在能被L整除的地址上不连续的部分用空字节补齐(具体到这里,如果在Keil环境下L=1,整型字段iAge占2个字节,所以Keil排在能被1整除的任意位置就可以了),最后排完所有字段后如果首地址到最后字段的末尾的总字节数不是L的整数倍,需要用空字节补齐。

这就是字节对齐的所有规则。

另外需要补充一点,虽然编译环境默认有个对齐值,但是可以通过

#pragma pack(2)
struct a
{
char ch;
int i;
short st;
char ch1[3];
}ta;
#pragma pack 2个预处理命令重新设置,#pragma pack(x)表示下面的部分在分配内存时默认值改为x

#pragma pack 表示撤销#pragma pack(x)操作

C语言深入探讨实战篇之结构体字节对齐(四)

首先我们知道L=min(2,4)=2(2表示的是#pragma pack(2)被设置为了2,而字段int i占4个字节)

所以char short是小于等于L int是大于L

所以分配内存的时候,首先首地址应该是4的倍数,然后char ch;字段,然后发现int i;字段的尺寸大于L所以对齐在能被L整除的地址上就行,但是char ch;字段只占了1个字节,所以中间补一个字节

ch | |i \ \ \ |
排完i后的地址刚好是偶数,可以拍short st,最后排char ch1[3]; 所有字段排完后发现从首地址到ch1[2]只有11个字节,所以补1个字节使总的字节数能被L整除。

在Keil中默认L=1,所以都是很紧密的排在一起没有空字节,所以总共结构体占8个字节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值