1.什么是结构体的内存对齐?
我们在进行计算结构体的大小时,就需要考虑到结构体内存对齐。
结构体的内存对齐:
当cpu从内存中取数据的时候,数据总线位数是32为,则一次可以从内存中取出四个字节数据。
但是如果四个字节数据从一个存储颗粒中取出,则是串行的。
为了提高效率就多加几个存储颗粒,从每个存储颗粒取出一个字节,就可以并行同时取出四个字节。
因此有些硬件平台cpu只能从指定地址取出指定大小的数据。
2.为什么要进行结构体内存对齐?
首先这是一个结构体代码:
struct student_t
{
int sn; //4
char num; //1
int age; //4
}
原本我们理解的存储应该是这个样子:
当我们要访问age成员的时候,取出4个字节数据,摒弃不用的num一个字节后,age只取出三个字节,不够,再次取出四个字节,取出前一个字节,相当于age成员变量访问的时候,cpu需要对内存访问两次才可以。
但是实际上它考虑到了内存对齐:
结构体的内存对齐:
1.不是所有的硬件平台都支持随意地址访问
2.提高结构体成员的访问效率
总体来说:
结构体的内存对齐是拿空间来换取时间的做法。
3.内存对齐规则
1.在任何一个平台下都有一个默认对齐数.
2.结构体每个成员变量又有一个自己独立的对齐数,就是成员变量类型的大小。
3.实际成员变量的对齐数是成员自己独立的对齐数与默认对齐数中较小的那个
4.每个成员变量都应该对齐到这个对齐数的整数倍处。vs中,默认对齐数是8字节。
5.结构体的总大小,应该是实际最大对齐数的整 数倍大小,不足则补位
6.如果成员变量也是结构体变量,则这个结构体变量的对齐数,为自己结构内部的实际最大对齐数
例如:
struct student_t
{
int sn; //4
char num; //1
int age; //4
short score; //2
}
age自身对齐数是4,默认平台对齐数是8,取较小的作为对齐数,所以age的实际对齐数是4.
因此age应该存储在偏移量为4的整数倍处,(也就是从8的位置往后存储)。
score根据对齐规则计算,得到实际对齐数是2。
因为结构体的总大小,应该是实际最大对齐数的整 数倍大小。所以结构体的总大小为16.
由运行得出的结果可以看出,它们之间都相差的是四个字节的大小。
练习:
struct student_t
{
char a; //1+3补齐
int b; //对齐在4的整数倍 处 4~7
double c; //8
char d; //4+4+8+1 ,但是结构体大小是最大对齐数的整数倍 所以是24
};
struct S
{
double d; //8
char c; //1+补位3
int i; //4,对齐到4的整数倍 8+4+4=16
};
struct s1
{
char 1; //1+ 补位7=8
struct S s; //对齐数是8(按照S内部最大对齐数进行对齐) //大小16
double d;//8
}; 8+16+8=32
默认对齐数的修改:
#pragma pack(1) 表示将默认对齐数设置为1
#pragma pack 还原默认对齐数
为什么要修改默认对齐数:
涉及到的就是网络通信,将一个结构体变量(一块连续的内存数据)发送给了另一个主机,如果两个平台对齐数不同,在进行内存中数据解释的时候就会出问题
结构体位段:
一个结构体类型中可以有多个基础数据类型的成员
struct student_t
{
int sn;
char sex:1;
char pos:2;
float score;
};
int main()
{
struct student_t xs;
xs.sn=1;
xs.sex=1;
xs.pos=3;
xs.score=99;
}
这个结构体大小是12个字节,大佬们认为这样占用空间太多了
而性别,只有0或1,一个比特位其实就够了
而职位,只有0,1,2,3,两个比特位就够了
这时候使用位段,member:n 表示使用指定空间中n个比特位存储数据,并且多个位段可以合并占用同一个储存空间。
比如sex和pos成语那,共同使用一个字节的空间就够了,因此会合并使用一个字节。
多个位段,使用同一块内存空间,从低位开始向高位存储数据,如果比特位不够了则申请一个新的空间存储。
位段的类型必须是整形,不能是浮点型