我们之前学习到各类型的空间大小所占的字节:
那么我们定义的结构体类型所占的空间大小又是多少呢?
结构体内存对齐
对齐规则
1.结构体的第一个成员放在0偏移处。(就是存放结构体类型的第一个字节空间处)
2.从第二个成员开始,之后的每一个成员都要对齐到某个对齐数的整数倍处。
该成员对齐数是:成员自身大小和默认对齐数的较小值(VS的默认对齐数是8)(gcc无 默认对齐数)
3.当成员全部存放进去之后,结构体的总大小必须是所有成员中最大对齐数的整数倍。
(如果不够整数倍就浪费内存也要空间对齐)
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。(类似于把嵌套结构体 拆开)
struct test 1
//最终需要的八个字节正好是最大对齐数4的整数倍,即:已经对齐好了。
验证
// char a 的地址:0x00007ff7e86fc1e8,short b 的地址:0x00007ff7e86fc1ea之间正好差一个字节的空间,故满足。如果你还不确定就拿起你的“武器”开始调试看看吧!
//其实这里也可以用 offsetof 实现:计算一个结构体的成员相较于他的起始位置的偏移量。
struct test 2
//最终需要十个字节,并不是最大对齐数4的整数倍,即:花费两个字节的空间对齐 共十二个字节
struct test 3
//这里的最大对齐数是8,而16也恰好是较大对齐数8的倍数。
补充
struct tes
{
double a;
char b;
int i;
};
struct test
{
char a;
struct tes b;
double d;
};
int main()
{
printf("%zu\n", sizeof(struct test));
return 0;
}
//猜猜结果? 规则四喔
解析
结构体的整体大小就是所有成员最大对齐数(含嵌套结构体的对齐数)的整数倍。(类似于把嵌套结构体拆开)
//而这里的所有最大对齐数就是8,恰巧32是8的整数倍
修改默认对齐数
我们上面使用VS在计算中的默认对齐数是8,如果我没觉得8不合适的话也是可以自己进行修改的,按你的想法来
展示
//提一下哈,这个虽然好用,但我们最好别设出一个对齐数让自己尴尬哈
位段
百度百科:位段,C语言允许在一个 结构体 中以位为单位来指定其成员所占 内存 长度,这种以位为单位的成员称为“位段”或称“ 位域 ”
对象:一般是整型
功能: 能够减少储存数据的位数(比特位)节省空间
定义: 信息的存取一般以字节为单位
举例使用(VS平台)
typedef struct test
{
int a : 2;//占2个比特位
int b : 5;//占5个比特位
int c : 10;//占10个比特位
int d : 30;//占30个比特位
}test;
int main()
{
printf("%zu\n", sizeof(test));
return 0;
}
编译
你可能会说47个比特位,六个字节就可以放得下了呀,为什么是八个字节两个呢
那就请您在看看啦!
位段的内存分配
规则
1.位段的成员可以是 int ,unsigned int或者char类型
2.位段的空间是按照需要以一次提供 四个字节(int)或者一个字节(char)的方式来开辟的
3.位段涉及许多的不确定因素,不跨平台(不同的编译器使用位段可能不一样)
那么我们就以VS的编译器看看是如何分配的吧:
举例
typedef struct test
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
}test;
int main()
{
test k = { 0 };//初始化为全0
k.a = 9;
k.b = 8;
k.c = 7;
k.d = 6;
return 0;
}
解析
编译
以上仅仅只是在VS上的结果,在其他的平台上可能就不一样了。并且VS上是一次性开辟八个比特位(一个char)的空间,使用时多余的空间不足以存储下一个数据时,多余的空间就浪费掉,不再使用(之后也不用)
//位段相对于结构体而言就节省了很多的空间,但是位段不具有跨平台性。
位段的作用:可以使数据单元节省储存空间,当程序需要成千上万个数据单元时,这种方法就显得尤为重要。