结构体中的元素在内存中并不是紧密排列的,原因在于CPU从内存取数据时,是一次取多个字节的,可以将内存看做多个抽屉,如每个抽屉中存放4字节,此时如果要取一个4字节的int值,如果该int值不在一个抽屉中而是一个抽屉里各存放一部分字节(即没有内存对齐),就需要读取两次内存内容(开两次抽屉)才能取到完整的int值,对性能有影响,甚至有些硬件架构下,不能访问任意地址的数据,只能从某些地址下才能访问特定类型的数据,否则会报错。
对齐规则1:结构体中的每个成员必须存放在该成员大小的整数倍偏移处。
对齐规则2:结构体大小必须是其最大元素的整倍数。
为了对齐而空出来的字节填0。
如以下结构体:
structs1{char c1;// char大小为1字节,需要存放在1的整数倍处,即可存放在任意地址处,此处的c1存放在偏移量0处int i1;// int大小为4字节,因此需要存放在4的整数倍处,因此c1和i1之间有3字节的0填充,此处的i1存放在偏移量4处char c2;// char大小为1字节,需要存放在1的整数倍处,即可存放在任意地址处,此处的c2存放在偏移量8处int i2;// int大小为4字节,因此需要存放在4的整数倍处,因此c2和i2之间有3字节的0填充,此处的i1存放在偏移量12处char c3;// char大小为1字节,需要存放在1的整数倍处,即可存放在任意地址处,此处的c3存放在偏移量16处// c3后需要将结构体大小补齐到最大元素整数倍,未补齐时结构体大小为17字节,结构体中最大元素大小为4,因此结构体最后需要补3字节的0到20字节};
以上结构体在内存中的大小是20字节而非简单的元素大小之和(11字节)。
结构体中的元素顺序不同,结构体在内存中的大小也不同:
structs2{char c1;// char大小为1字节,需要存放在1的整数倍处,即可存放在任意地址处,此处的c1存放在偏移量0处char c2;// char大小为1字节,需要存放在1的整数倍处,即可存放在任意地址处,此处的c2存放在偏移量1处char c3;// char大小为1字节,需要存放在1的整数倍处,即可存放在任意地址处,此处的c3存放在偏移量2处int i1;// int大小为4字节,因此需要存放在4的整数倍处,因此c3和i1之间有1字节的0填充,此处的i1存放在偏移量4处int i2;// int大小为4字节,因此需要存放在4的整数倍处,此处的i1存放在偏移量8处};
以上交换元素顺序后的结构体大小为12字节。
我们可以使用预编译命令
#pragma pack(n)
来将对齐模数设为n,对齐模数指的是:如果是int数据,则对齐模数是4,如果是double数据,则对齐模数是8。如果将结构s1的对齐模数设为2:
#pragmapack(2)structs3{char c1;// c1存放在偏移量0处int i1;// int大小为4字节,对齐模数为2,i1会存放在地址2处,c1和i1之间只有1字节的0填充char c2;// c2存放在偏移量6处int i2;// int大小为4字节,对齐模数为2,i1会存放在地址8处,c2和i2之间只有1字节的0填充char c3;// c3存放在偏移量12处// c3后需要将结构体大小补齐到对齐模数的整数倍处,未补齐时结构体大小为13字节,因此结构体最后需要补1字节的0到14字节};
注意
#pragma pack(n)
的目的是让结构体更紧凑,如果n比默认应该空余的字节数要大,则按应该空余的字节数来计算,s3的结构体最后填补了1个字节而非3个字节就是这个原因,如:
#pragmapack(8)structs4{char c;// c存放在偏移量0处int i;// i如果按pack的参数来计算,应该存在偏移量8处,会填充7个0字节,超出了默认填充3个0字节,因此会填充3个0字节};
以上结构体的大小为8而非16。
版权归原作者 吃着火锅x唱着歌 所有, 如有侵权,请联系我们删除。