什么是字节对齐?字节对齐与位域的计算

1.什么是字节对齐?为什么要字节对齐?

字节对齐也称为字节填充, 它是一种牺牲空间换取时间的方法。
字节对齐的作用不仅是便于CPU的快速访问,使CPU的性能达到最佳,而且可以有效地节省存储空间。

例如,32位计算机的数据传输是4字节,64位计算机的数据传输是8字节,这样,struct在默认情况下,编译器会对struct的结构进行(32位机)4的倍数或(64位机)8的倍数的数据对齐。

对于32位机来说,4字节的对齐能够使CPU访问速度提高,如一个long类型的变量,如果跨越了4字节边界存储,那么CPU要读取两次,这样效率就低了。但需要注意的是,如果在32位机中使用1字节或者2字节对齐,不仅不会提高效率,反而会使访问速度降低。

2.字节对齐的规则

  1. 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍。如有不满足,则需要在成员之间加上相应的填充字节。
  2. 结构体的总大小必须为最大对齐数的整数倍,如不满足,则需要在最后一个成员之后加上填充字节。

3.字节对齐的数值

  1. 指定对齐参数值:通过#pragma pack(push, n)设置。
  2. 自身对齐参数值:每个内部类型自身也都有一个对齐参数,一般来说这个对齐参数就是 sizeof(type) 的值,char是1,short是2,int是4,float是4,double是8等。
  3. 有效对齐参数值:内部类型的有效对齐是指它的自身对齐参数和指定对齐参数中较小的那个值;
  4. 结构体整体的有效对齐参数值:是指它的成员中,有效对齐参数最大的那个值。
///< 假设按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.位域的填充

  1. 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
  2. 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
  3. 如果相邻的两个位域字段的类型不同,则各个编译器的具体实现有差异,VC6采取不压缩方式,采用第一节说的字节对齐方式进行。
  4. 整个结构体的总大小采用字节对齐方式的第4点。
  5. 如果位域字段之间穿插着非位域字段,则不进行压缩,采用第一节说的字节对齐方式进行。
  6. 无名的位域不能使用,只能用于填充,位宽为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字节。

字节12345678910
1+2+5(补)7+1(补)6+2(补)4+4888888
成员ucpa1+ucpa2ucpa3ucpa4ucpa5+ucpa6ucpa7ulpa8ulpa8ulpa8ulpa8

b. 4字节对齐时
与2字节对齐相同,仅在ucpa7之后,即第六位字节开始补3位,即比第一种结果多两个字节,为12字节。

c. 8字节对齐时
与4字节对齐相同,为12字节。

  • 13
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
结构体字节对齐引起的访问越界问题通常出现在以下情况下: 1. 结构体中存在字节对齐的成员变量,访问该成员变量时,可能会访问到结构体之外的内存空间。例如,如果某个成员变量的长度是4字节,但该结构体的字节对齐方式是8字节,那么访问该成员变量时,可能会访问到结构体之外的4字节内存空间,从而导致访问越界。 2. 结构体中存在数组类型的成员变量,访问该数组时,可能会访问到数组之外的内存空间。例如,如果一个数组的长度是10,但该结构体的字节对齐方式是16字节,那么访问该数组时,可能会访问到数组之外的6字节内存空间,从而导致访问越界。 为了避免结构体字节对齐引起的访问越界问题,可以采取以下措施: 1. 使用编译器提供的或自定义的对齐方式,以确保结构体中成员变量的对齐方式符合要求。 2. 避免在结构体中使用数组类型的成员变量,或者使用动态内存分配等方法来管理数组,以确保数组访问不会越界。 3. 避免在结构体中使用位域类型的成员变量,或者使用位运算等方法来确保位域访问不会越界。 4. 在编写代码时,注意检查结构体中的成员变量访问是否越界,可以使用断言等方法来检查访问边界。 总之,结构体字节对齐引起的访问越界问题需要引起重视,需要在编写代码时注意结构体成员变量的对齐方式和访问边界,以确保程序的正确性和稳定性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值