文档介绍:该【结构体对齐 】是由【小sjj】上传分享,文档一共【6】页,该文档可以免费在线阅读,需要了解更多关于【结构体对齐 】的内容,可以使用淘豆网的站内搜索功能,选择自己适合的文档,以下文字是截取该文章内的部分文字,如需要获得完整电子版,请下载此文档到您的设备,方便您编辑和打印。关于C语言中的结构体对齐。
(1)什么是字节对齐
一个变量占用n个字节,则该变量的起始地址必须能够被n整除,即:存放起始地
址%n=0,对于结构体而言,这个n取其成员种的数据类型占空间的值最大的那个。
(2)为什么要字节对齐
内存空间是按照字节来划分的,从理论上说对内存空间的访问可以从任何地址开始,
但是在实际上不同架构的CPU为了提高访问内存的速度,就规定了对于某些类型的数据
只能从特定的起始位置开始访问。这样就决定了各种数据类型只能按照相应的规则在内存
空间中存放,而不能一个接一个的顺序排列。
举个例子,比如有些平台访问内存地址都从偶数地址开始,对于一个int型(假设32
位系统),如果从偶数地址开始的地方存放,这样一个读周期就可以读出这个int数据,但
是如果从数地址开始的地址存放,就奇需要两个读周期,并对两次读出的结果的高低字节
进行拼凑才能得到这个int数据,这样明显降低了读取的效率。
(3)如何进行字节对齐
每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(不指定则取
默认值)中较小的一个对齐,并且结构的长度必须为所用过的所有对齐参数的整数倍,不够
就补空字节。
这个规则有点苦涩,可以把这个规则分解一下,前半句的意思先获得对齐值后与指定
对齐值进行比较,其中对齐值获得方式如下:
:对于char型数据,其自身对齐值为1,对于short型
为2,对于int,long,float类型,其自身对齐值为4,对于double类型其自身对齐值
为8,单位为字节。
:其成员中自身对齐值最大的那个值。
其中指定对齐值获得方式如下:
#pragmapack(value)时的指定对齐值value。
未指定则取默认值。
后半句的意思是主要是针对于结构体的长度而言,因为针对数据类型的成员,它仅有
一个对齐参数,其本身的长度、于这个对齐参数,即1倍。对于结构体而言,它可能使用
了多种数据类型,那么这句话翻译成对齐规则:每个成员的起始地址%自身对齐值=
0,如果不等于0则先补空字节直至这个表达式成立。
换句话说,对于结构体而言,结构体在在内存的存放顺序用如下规则即可映射出来:
(一)每个成员的起始地址%每个成员的自身对齐值=0,如果不等于0则先补空
字节直至这个表达式成立;
(二)结构体的长度必须为结构体的自身对齐值的整数倍,不够就补空字节。
举个例子:
#pragmapack(8)
structA{
chara;
longb;
};
structB{
chara;
structAb;
longc;
};
structC{
chara;
structAb;
doublec;
};
structD{
chara;
structAb;
doublec;
intd;
};
structE{
chara;
intb;
structAc;
doubled;
};
对于structA来说,对于char型数据,其自身对齐值为1,对于long类型,其自
身对齐值为4,结构体的自身对齐值取其成员最大的对齐值,即大小4。那么structA
在内存中的顺序步骤为:
(1)chara,地址范围为0x0000~0x0000,起始地址为0x0000,满足0x0000%
1=0,这个成员字节对齐了。
(2)longb,地址起始位置不能从0x00001开始,因为0x0001%4!=0,所
以先补空字节,直到0x00003结束,即补3个字节的空字节,从0x00004开始存放b,
其地址范围为0x00004~0x0007.
(3)此时成员都存放结束,结构体长度为8,为结构体自身对齐值的2倍,符合条件
(二).
此时满足条件(一)和条件(二),structA中各成员在内存中的位置为:a***b,si
zeof(structA)=8。(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,
b占四位)
对于structB,里面有个类型为structA的成员b自身对齐值为4,对于long类
型,。那么structB在内存中的顺序
步骤为:
(1)chara,地址范围为0x0000~0x0000,起始地址为0x0000,满足0x0000%
1=0,这个成员字节对齐了。
(2)structAb,地址起始位置不能从0x00001开始,因为0x0001%4!=0,
所以先补空字节,直到0x00003结束,即补3个字节的空字节,从0x00004开始存
放b,其地址范围为0x00004~0x00011.
(3)longc,地址起始位置从0x000012开始,因为0x0012%4=0,其地
址范围为0x00012~0x0015.
(4)此时成员都存放结束,结构体长度为16,为结构体自身对齐值的4倍,符合条件
(二).
此时满足条件(一)和条件(二),structB中各成员在内存中的位置为:a***bc,
sizeof(structC)=24。(每个星号代表一位,成员各自代表自己所占的位,比如a占
一位,b占八位,c占四位)
对于structC,里面有个类型为structA的成员b自身对齐值为4,对于double
类型,。那么structC在内存中的顺
序步骤为:
(1)chara,地址范围为0x0000~0x0000,起始地址为0x0000,满足0x0000%
1=0,这个成员字节对齐了。
(2)structAb,地址起始位置不能从0x00001开始,因为0x0001%4!=0,
所以先补空字节,直到0x00003结束,即补3个字节的空字节,从0x00004开始存
放b,其地址范围为0x00004~0x00011.
(3)doublec,地址起始位置不能从0x000012开始,因为0x0012%8!=
0,所以先补空字节,直到0x000015结束,即补4个字节的空字节,从0x00016开始
存放c,其地址范围为0x00016~0x0023.
(4)此时成员都存放结束,结构体长度为24,为结构体自身对齐值的3倍,符合条件
(二).
此时满足条件(一)和条件(二),structC中各成员在内存中的位置为:a***b**
**c,sizeof(structC)=24。(每个星号代表一位,成员各自代表自己所占的位,比
如a占一位,b占八位,c占八位)
对于structD,自身对齐值为8。前面三个成员与structC是一致的。对于第四
成员d,因为0x0024%4=0,所以可以从0x0024开始存放d,其地址范围为0x
00024~,结构体长度为28,28不是结构体自身对齐值
8的倍数,所以要在后面补四个空格,即在0x0028~0x0031上补四个空格。补完了,
结构体长度为32,为结构体自
身对齐值的4被,,符合条件(二).
此时满足条件(一)和条件(二),structD中各成员在内存中的位置为:a***b**
**cd****,sizeof(structD)=32。(每个星号代表一位,成员各自代表自己所占
的位,比如a占一位,b占八位,c占八位,d占四位)。
对于structE中各成员在内存中的位置为:a***bcd,sizeof(structE)=2
4。(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,b占四位,c占八
位,d占八位)。
通过structD和structE可以看出,在成员数量和类型一致的情况,后者的所占
空间少于前者,因为后者的填充空字节要少。如果我们在编程时考虑节约空间的话,应该
遵循将变量按照类型大小从小到大声明的原则,这样尽量减少填补空间。另外,可以在填
充空字节的地方来插入reserved成员,例如
structA
{
chara;
charreserved[3];
intb;
};
这样做的目的主要是为了对程序员起一个提示作用,如果不加则编译器会自动补齐。