一.结构体的内存管理
1.结构体的对齐规则
a.结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的位置处
b.其他成员需要对齐到是偏移量的整数倍的位置处
注意,偏移量对于不同的编译器来说不同,vs默认值为8,gcc为成员本身的字节数
c.对齐数:默认偏移量与成员大小中较小的一个数
d.结构体最终的大小应为最大对齐数的整数倍
struct S
{
char c1;
int i;
char c2;
};
以该代码为例,结构体s的存储可以下图表示
我们默认第一个成员s1位置存在0处,存下一个成员int i时需要从4的倍数处开始存,以此类推,结构体s的大小应为9个字节吗?事实并非如此,最终应为最大对齐数4的倍数,即12个字节。
e.如果嵌套了结构体,则嵌套的结构体成员对齐到自己成员的最大对齐数的整数倍处,结构体的大小为包含嵌套结构体在内成员的最大对齐数的整数倍。
下面举例说明。
struct S3
{
double d;
char c;
int i;
};
struct S4
{
char c1;
struct S3;
double d;
};
求结构体S4的大小,其结果为32,过程如图
包括嵌套strcut3成员在内,最大对齐数应为double类型,即8,因此嵌套结构体也从它成员最大对齐数的倍数开始存。
2.结构体内存对齐的必要性
a.平台原因:并不是所有平台都能够访问任意数据的任意地址,否则会抛出硬件异常
b.性能原因:若所有数据在内存中都连续存储,可能会产生下一个数据会存在上一个数据未使用完的空间中,导致读取数据时进行两次内存访问,浪费了运行时间
总的来说,结构体的内存对齐是用空间换时间。
3.既满足节省空间,又满足节省时间的方法
建议1:将较小空间的数据排在一块定义(无所谓前后)
建议2: 有一种方法可以做到同时满足节省空间和时间——修改默认对齐数
#include<stdio.h>
#pragma pack(1)//设置默认对齐数为1
struct S
{
char c1;
int i;
char c2;
};
#pragma pack()//恢复默认对齐数
这里修改的默认对齐数一般为2的次方数
二.结构体的传参
struct S
{
int arr[100];
int n;
double d;
};
void print1(struct S tmp)
{
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", tmp.arr);
}
printf("%d ", tmp.n);
printf("%lf \n", tmp.d);
}
void print2(struct S* ps)
{
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", ps->arr[i]);
}
printf("%d ", ps->n);
printf("%lf \n", ps->d);
}
int main()
{
struct S s = { {1,2,3,4,5},6,3.14 };
print1(s);
print2(&s);
return 0;
}
采用print1,print2打印结构体,哪种更好?
显然是print2.
当我们使用print1进行打印,结构体有多大,print1函数中传入的tmp就有多大,这样造成了时间和空间上的双重浪费。
当我们使用print2进行打印,将结构体的地址传入函数,节省了空间。
结论:结构体传参,最好传结构体的地址。
三.结构体的位段
1.什么是位段
a.位段的“位”指的是二进制位,冒号之后的数字代表的是二进制位的数目,如_a类型只有两个二进制位,只能表示0,1,2,3的十进制数字
b.结构体实现位段,位段和结构体的声明类似,其形式如下:
struct A
{
int _a : 2;
int _b : 5;
int _c : 10;
int _d : 30;
};
此时的A就是一个位段类型。
那A所占内存大小是多少呢?
答案是8个字节(64个bit位)
由此看来,位段是一种专门用来节省空间的方法。
2.位段的内存分配
a.位段成员的类型只能是int, unsigned int和signed int
b.位段在空间上是需要按照四个字节(int)或者一个字节(char)来开辟的
c.位段使用有一定风险,其跨平台属性较差
struct A
{
char _a : 2;
char _b : 4;
char _c : 5;
char _d : 4;
};
下面举个例子:
我们为上面的_a,_b类型开辟一个空间
可以看到在当前环境下数据是从右向左存储的。此时我们开辟的第一块空间,不足以存放_c的数据,是直接浪费,还是继续使用剩下的并重新开辟呢?
在vs上,此时选择浪费,重新开辟一块空间。以此类推。此时浪费掉的空间bit位会填入0.
在进行数据存储时,会发生数据截断。
3.位段的风险
a.在不同机器上,位段的最大数目并不固定,36位和64位机器上为32bit位,而在更早的16位机器上,则为16bit位
b.使用位段时内存从左向右还是从右向左分配并不确定
c.int位段是有符号还是无符号并不确定
d.当一个结构包含两个位段,第二个位段成员较大时,是继续使用上一个的空间还是直接浪费依然不确定
4.位段的应用
在网络协议中,IP数据报的格式,其中很多属性值只需要很小的几个bit位就可以描述,不仅能达到想要实现的效果,还能节省空间,对于数据报的传输有利,而且有利于网络的通畅。