引言
前面我们了解了结构体的知识。了解了结构体类型的声明、结构体变量的定义与初始化、结构体传参与结构体内存对齐。
除了结构体外,还有位段、枚举、联合这样的自定义类型。
位段
信息的存储一般以字节为单位,但是一些时候我们的数据不需要一个字节的空间。例如int a = 10;
,10的二进制补码为01010,实际上只占用了不到一个字节,开辟一个整型的空间就显得有些浪费了。
借助结构体实现的位段就可以解决这个问题:
位段定义
位段(bit-field)是以位为单位来定义结构体中的成员变量所占的空间。
位段中的成员只能是int、unsigned int、signed int、char类型。
位段的成员名后有一个":"后跟一个数字,表示该结构体成员需要开辟几个比特位的空间。
例如:
struct A
{
int i : 2;
int j : 1;
int k : 20;
};
这里的struct A就是一个位段类型。它给i开辟2个比特位;给j开辟1个比特位;给k开辟20个比特位。
我们可以看一下struct A类型的大小:
struct A
{
int i : 2;
int j : 1;
int k : 20;
};
int main()
{
printf("%d\n", sizeof(struct A));
return 0;
}
为什么是4个字节呢?
我们需要了解位段类型是如何开辟空间的:
位段的内存分配
位段在分配内存时,每次开辟一定的空间给位段的成员,成员依次占用这些空间。当本次开辟的空间剩余不足时,才会再开辟一定的空间。直到存放位段中的所有成员。
这个一定的空间对于int类型是4个字节;对于char类型是1个字节。所以在成员后的表示比特位的数字若是int不能超过32;若是char不能超过8。
对于上例的位段类型struct A,里面的类型都是int型的,所以先开辟4个字节的空间。变量i需要2个比特位;变量j需要1个比特位;变量k需要20个比特位。一共只需要23个比特位,4个字节完全够用,所以不需要再次开辟:
虽然也存在一些浪费,不过相对来说已经节省很多了。
再比如这段代码:
struct B
{
int i : 2;
int j : 1;
int k : 20;
int x : 30;
};
int main()
{
printf("%d\n", sizeof(struct B));
return 0;
}
到成员k时,已经占用了23个比特位,本次的4个字节只剩9个比特位。显然不足以开辟30个比特位给成员x,需要再次开辟4字节的空间。
那么这剩余的9个比特位是浪费掉还是接着使用呢?
位段的可移植性问题
其实,不只上面的这个关于每次开辟剩余的空间的问题。还有一些问题是C语言标准没有定义的:
1、int是作为有符号数还是无符号数是没有定义的;
2、位段中最大位的数目是不确定的;
3、位段中的成员从低位到高位分配空间还是从高位到低位分配是不确定的。
所以位段与结构体的效果类似,可以很好的节省空间。但是有跨平台问题的存在。
枚举
在初识C语言部分,我们已经初步了解了枚举常量。
枚举就是一一列举,可以用来列举几个常量。例如星期、月份、性别等。这些常量的类型是枚举类型的。接下来就详细介绍一下枚举类型:
枚举类型的定义
enum Day
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sta,
Sun
};
enum是枚举关键字。Mon、Tues、Wed、Thur、Fri、Sta、Sun这些都是枚举常量,它们之间用","隔开。如果没有赋值,这些枚举常量的值是从0递增1;如果有赋值,赋值的那个成员开始所赋的值递增1:
enum Day
{
Mon,
Tues,
Wed,
Thur=10,
Fri,
Sta,
Sun
};
int main()
{
printf("%d\n", Mon);
printf("%d\n", Tues);
printf("%d\n", Wed);
printf("%d\n", Thur);
printf("%d\n", Fri);
printf("%d\n", Sta);
printf("%d\n", Sun);
return 0;
}
这些常量的类型都是enum Day。
枚举常量的使用
当我们需要将某个枚举常量的值用作某一个变量的初值是,这个变量的类型需要与枚举类型一致。即只能用枚举常量给枚举变量赋值:
enum Day
{
Mon,
Tues,
Wed,
Thur=10,
Fri,
Sta,
Sun
};
int main()
{
enum Day s = Mon;
printf("%d\n", s);
return 0;
}
枚举常量的优点
枚举常量在使用时有许多的优势:
1、枚举常量一次可以定义多个常量,而#define只能定义一种;
2、枚举常量是具有类型的,会更加严谨;
3、使用枚举常量可以提高代码的可读性与可维护性(在后面的通讯录中会做具体的说明);
4、将常量封装,防止了命名污染。
在后面的代码中,大家可以多尝试使用枚举常量,就会发现它的许多便利。
联合(共用体)
联合的定义
联合也是一种自定义类型。在联合中同样包括一系列成员。
联合关键字是union:
union A
{
char c;
int i;
};
但是这些成员共用一块空间,所以联合也称为共用体。
由于成员共用一块空间,所以联合的大小至少是最大成员的大小。
我们可以用sizeof来计算这个联合的大小:
union A
{
char c;
int i;
};
int main()
{
printf("%d\n", sizeof(union A));
return 0;
}
int型的成员i与char型的成员c共用4个字节。
但除此之外,关于联合大小的计算还有一些规定:
联合的大小
首先,联合的大小至少是其中最大成员的大小;
第二,最终的大小必须是联合中成员最大对齐数的整数倍(对齐数是成员大小与默认对齐数的较小值)。
在上面的例子中,最大成员是int型的,4个字节。成员最大对齐数也是4,所以这个联合union A的大小就是4字节。
再比如:
union B
{
char c;
int i;
short s[3];
};
int main()
{
printf("%d\n", sizeof(union B));
return 0;
}
这个union B联合中的最大成员的大小是6字节,但由于成员最大对齐数是4。联合的大小必须是成员最大对齐数的整数倍,所以就是8。
总结
在本文中,我们了解了位段、枚举、联合的相关知识。
这是虎年的最后一篇博客了,祝大家新年快乐!!!
最后,如果对本文有任何问题,欢迎在评论区进行讨论哦
希望与大家共同进步哦