
什么是字节对齐?字节对齐与位域的计算
看的晕晕的,整理下来吧
1.什么是字节对齐?为什么要字节对齐?
字节对齐也称为字节填充, 它是一种牺牲空间换取时间的方法。
字节对齐的作用不仅是便于CPU的快速访问,使CPU的性能达到最佳,而且可以有效地节省存储空间。
例如,32位计算机的数据传输是4字节,64位计算机的数据传输是8字节,这样,struct在默认情况下,编译器会对struct的结构进行(32位机)4的倍数或(64位机)8的倍数的数据对齐。
对于32位机来说,4字节的对齐能够使CPU访问速度提高,如一个long类型的变量,如果跨越了4字节边界存储,那么CPU要读取两次,这样效率就低了。但需要注意的是,如果在32位机中使用1字节或者2字节对齐,不仅不会提高效率,反而会使访问速度降低。
2.字节对齐的规则
- 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍。如有不满足,则需要在成员之间加上相应的填充字节。
- 结构体的总大小必须为最大对齐数的整数倍,如不满足,则需要在最后一个成员之后加上填充字节。
3.字节对齐的数值
- 指定对齐参数值:通过#pragma pack(push, n)设置。
- 自身对齐参数值:每个内部类型自身也都有一个对齐参数,一般来说这个对齐参数就是 sizeof(type) 的值,char是1,short是2,int是4,float是4,double是8等。
- 有效对齐参数值:内部类型的有效对齐是指它的自身对齐参数和指定对齐参数中较小的那个值;
- 结构体整体的有效对齐参数值:是指它的成员中,有效对齐参数最大的那个值。
///< 假设按4字节对齐
#pragma pack(push, 4)
struct data {
char a; //a的有效对齐参数值是min(1,4)为1字节,从第1字节开始,占1字节
char b; //b的有效对齐参数值是min(1,4)为1字节,从第2字节开始,占1字节
long long c; //d的有效对齐参数值是min(8,4)为4字节,从第5个字节开始,占8字节
short d; //e的有效对齐参数值是min(2,4)为2字节,从第13个字节开始,占2字节
};
它们的和为1字节(a)+1字节(b)+2字节(填充)+8字节(c)+2字节(d)=14字节。
因为整体结构体还需要进行对齐,结构成员最大有效对齐参数值为4,所以整个结构为4的整数倍,最后的总大小为4 * 4 = 16字节。
例题:
采用2字节、4字节、8字节对齐时,sizeof(ABC)的值是多少?
typedef struct tagABC
{
unsigned char ucpa1:2;
unsigned char ucpa2:1;
unsigned char ucpa3:7;
unsigned char ucpa4:6;
unsigned char ucpa5:4;
unsigned char ucpa6:4;
unsigned char ucpa7;
unsigned long ulpa8;
}ABC;
答案是:10、12、12
首先需要知道以下语句的含义
unsigned char ucpa1:2
4.位域
该种形式出现于结构体或共用体的定义中,是位域定义的标准形式。
其使用方式为
struct student
{
type var_student : n;
};
在结构体student中,成员变量var_student占用空间为n位。(1字节=8位,1 bit = 8 bytes)
n为正整数,其值必须小于type类型占用的位数。比如type如果是int,占4字节32位,那么n必须是1~31之间的整数。
5.位域的填充
- 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
- 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
- 如果相邻的两个位域字段的类型不同,则各个编译器的具体实现有差异,VC6采取不压缩方式,采用第一节说的字节对齐方式进行。
- 整个结构体的总大小采用字节对齐方式的第4点。
- 如果位域字段之间穿插着非位域字段,则不进行压缩,采用第一节说的字节对齐方式进行。
- 无名的位域不能使用,只能用于填充,位宽为0表示强制下一位域对齐到当前类型的边界。
///< 假设按4字节对齐
#pragma pack(push, 4)
struct data {
short a : 7; //从第1个字节开始,占1字节,使用7位
short b : 5; //从第2个字节开始,占1字节,使用5位
long long e : 4; //与上个类型不一致,e的有效对齐参数值是min(8,4)为4字节,所以从第5个字节开始,占8字节,使用4位
char f : 5; //与上个类型不一致,f的有效对齐参数值是min(1,4)为1字节,所以从第13个字节开始,占1字节,使用5位
};
它们的和为1字节(a)+1字节(b)+2字节(填充)+8字节(e)+1字节(f)=13字节。
因为整体结构体还需要进行对齐,结构成员最大有效对齐参数值为4,所以整个结构为4的整数倍,最后的总大小为4 * 4 = 16字节。
///< 假设按4字节对齐
#pragma pack(push, 4)
struct bits8 {
short a : 3; //从第1个字节开始,占1字节,使用3位
short : 0; //占位,占15位,强制下一类型对齐到边界
char : 3; //占位,从第3个字节开始,占3位
char b : 5; //从第3字节第4位开始,使用5位
int i : 4; //从第5个字节开始,占4字节,使用4位
};
它们的和为2字节(a)+1字节(b)+1字节(填充)+4字节(i)=8字节。
因为整体结构体还需要进行对齐,结构成员最大有效对齐参数值为4,所以整个结构为4的整数倍,满足条件,最后的总大小为8字节。
最后再来看这道题
typedef struct tagABC
{
unsigned char ucpa1:2;
unsigned char ucpa2:1;
unsigned char ucpa3:7;
unsigned char ucpa4:6;
unsigned char ucpa5:4;
unsigned char ucpa6:4;
unsigned char ucpa7;
unsigned long ulpa8;
}ABC;
a. 2字节对齐时
ucpa1占第一字节的2位,ucpa2占第一字节的1位,由于第一字节只剩下8-2-1=5位,7>5,所以ucpa3要从第二字节开始,同时在第一字节补5位。
同理,ucpa3占用第二字节后只剩下1位,无法容纳ucpa4,所以ucpa4要从第三字节开始,同时在第二字节补1位。
接下来在第三字节补2位,ucpa5从第四字节开始,并可以与ucpa6共用第四个字节。
ucpa7从第五字节开始,占用一个字节。
ulpa8占用4个字节,由于要求二字节对齐,所以ulpa8之前的字节数要为2的整数倍,所以需要补一个字节,使ulpa8从第七字节开始,保证其前面的字节数可以被2整除。
它们的和为1字节(ucpa1、ucpa2)+1字节(ucpa3)+1字节(ucpa4)+1字节(ucpa5、ucpa6)+1字节(ucpa7)+1字节(补)+4字节(ulpa8)=10字节
因为整体结构体还需要进行对齐,结构成员最大有效对齐参数值为2,所以整个结构为2的整数倍,满足条件,最后的总大小为10字节。
字节 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
位 | 1+2+5(补) | 7+1(补) | 6+2(补) | 4+4 | 8 | 8 | 8 | 8 | 8 | 8 |
成员 | ucpa1+ucpa2 | ucpa3 | ucpa4 | ucpa5+ucpa6 | ucpa7 | 补 | ulpa8 | ulpa8 | ulpa8 | ulpa8 |
b. 4字节对齐时
与2字节对齐相同,仅在ucpa7之后,即第六位字节开始补3位,即比第一种结果多两个字节,为12字节。
c. 8字节对齐时
与4字节对齐相同,为12字节。
更多推荐
所有评论(0)