【C语言】自定义类型——位段、枚举、联合体(2)

在上篇文章中介绍了部分结构体的知识后,我们这次来了解一下结构体中关于位段的知识,以及剩下的两种自定义类型——枚举、联合体,希望通过这篇文章能给大家带来帮助。

目录

一、位段

1.1位段是什么

1.2位段的内存分配

1.3位段的跨平台

二、枚举

2.1枚举类型的定义

三、联合体(共用体)

3.1联合体类型的定义

3.2联合的特点和大小

3.3使用联合体判断大小端


一、位段

1.1位段是什么

所谓位段就是以比特位为单位的成员,就是最小的单位是比特位,而不是字节。像int,char,long,short等都是以字节为单位的。

位段的声明与结构与结构体类似,但是有两个不同:

1.位段的成员必须是char、unsigned char、int、unsigned int。

2.位段的成员名后面有一个冒号和一个数字

举个例子:

struct A
{
    int _a:2;
    int _b:5;
    int _c:10;
    int _d:30;
};

int main()
{
    struct A p1;
    return 0;
}

其实,我们在这样定义时,这个冒号就表示只分配a两个比特位,我们使用位段的这种方式,可以极好地使用空间(位段没有内存对齐)。

【C语言】自定义类型——位段、枚举、联合体(2)_第1张图片

我们可以使用sizeof来计算位段的大小,注意,位段的单位是比特位,一个整形有4个字节,一个字节等于8个比特位。等一会我们会探讨位段的大小为什么是这样的。

1.2位段的内存分配

1.位段的空间是按照需要以4个字节(int)或1个字节(char)来开辟的。

        在VS2019中,如果开辟的空间不足容纳这个成员的大小,则会重新开辟一个该成员类型的空间来容纳这个成员。

2.位段涉及很多不确定因素,所以位段是不夸平台的,不同编译器可能会产生不同的效果,注重可移植的程序应该避免使用位段。

以我们上面那个例子来说,他的大小是8,也就是2个int型数据的大小,明明是4个int型的位段,为什么只有两个int型数据的大小呢,我们来画图并分析一下:

【C语言】自定义类型——位段、枚举、联合体(2)_第2张图片

我们按照第一条规则,首先,a为2个比特、b为6个比特,他俩加起来刚好8个比特,可以共占一个字节,而c一个就占8个比特,但是他们的总数没有超过32,所以,c可以共用这个整形的下一个字节,如图所示。而d就比较特殊了,他一个大小就占了30个比特,a、b、c这个整形中已经容纳不下这个成员了,所以编译器给他开辟了下一个整形,让他一共控制30个比特位(还有俩个比特位不属于d管控)。

接下来我们来看看我们的推理是否正确,我们创建一个我们自定义A类型的变量p,并对他进行初始化和赋值,再观察内存中数据的是如何存储的。

【C语言】自定义类型——位段、枚举、联合体(2)_第3张图片

(注意,VS是小端存储哦!这虽与位段无关,位段的单位是比特位,而这中存储是以字节的方式存储,但是我们在内存中观察数据时要引起注意)

我们从内存中看到,p变量中数据的存放与我们上面推理的内容完全相符。 

接下来我们来看看char型的位段是是怎样使用内存的。我们依然使用一个变量s,并对他进行赋值来分析是如何存放数据的。

【C语言】自定义类型——位段、枚举、联合体(2)_第4张图片

位段,因为大小的限制,如果数据超过了我们定义的范围则会发生截断,如a所示。

注意,a+b共7个比特位,虽然有一个比特位剩余,但VS采取的方式是再开辟一个字节(因为c是char型数据)将c全部存放到该字节中,并没有让c干涉a+b的那块空间,这虽是允许的,但也是C语言标准未定义的。

 我们可以得到数据7F 04 03 62(小端存储)对应的10进制数,来检测改变位段是否会达到我们想要的值,当然,这是多余的一步,因为内存中已经存放了我们想要的数据。

【C语言】自定义类型——位段、枚举、联合体(2)_第5张图片

1.3位段的跨平台

这里我们来看一些C语言标准中未定义的一些问题:

1.int位段被当成有符号数还是无符号数不确定。

2.位段中最大位的数目不能确定。(16位机器最大为16,32位机器最大32)

3.位段中的成员在内存中从左到右分配,还是从右到左分配标准尚未定义(VS中是从右到左分配)。

4.当一个结构体包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这也是未定义的(VS中是直接省略)。

总结:位段可以达到与结构体相同的效果,但是可以更好的节约空间,可以在一些特殊的场景下使用,但是他存在跨平台的问题。


二、枚举

2.1枚举类型的定义

我们使用枚举来将所有可能出现的情况一一列举出来,比如颜色、星期、月份等,我们就可以将所有可能列举出来,定义一个枚举类型的类型。接下来我们来看看如何定义。

【C语言】自定义类型——位段、枚举、联合体(2)_第6张图片

 【C语言】自定义类型——位段、枚举、联合体(2)_第7张图片

以上定义的这些枚举类型中{}里的内容是枚举类型的可能取值,也叫枚举常量。

既然是常量,那必然是有值的,这些值默认是从0开始,一次递增1,在定义时我们也可以赋初始值。 

 【C语言】自定义类型——位段、枚举、联合体(2)_第8张图片

枚举类型的使用还是比较常见的,因为他可以增强我的我们代码可读性和可维护性,所以说在以后代码量多的地方,枚举的优势还是十分明显的。


三、联合体(共用体)

3.1联合体类型的定义

联合也是一种特殊的自定义类型

这种类型定义的变量包含一系列的成员,特征是这些成员公用一块空间(所以联合体也叫共用体)

【C语言】自定义类型——位段、枚举、联合体(2)_第9张图片

这里sizeof计算u1的大小为4,即使有一个整形和一个字符型的数据,但是该共用体的大小依然是4,因为c和i共用一块空间。

 【C语言】自定义类型——位段、枚举、联合体(2)_第10张图片

3.2联合的特点和大小

1.联合体Un中的元素共占一块空间

2.联合体的总大小最小为最大成员的大小

3.当最大成员大小不是最大最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

这时我们来看一个案例:

【C语言】自定义类型——位段、枚举、联合体(2)_第11张图片

联合体大小的计算其实和结构体的大小相似,只不过联合体的大小取决于最大成员的大小。

3.3使用联合体判断大小端

在之前我们使用使用char型指针判断当前编译器是大端还是小端,我们现在了解了联合体,便可以使用联合体来判断。

【C语言】自定义类型——位段、枚举、联合体(2)_第12张图片

好了,本篇文章就介绍到这里了,结构体的知识也到这就结束了,希望能对大家有帮助。

下篇文章我会介绍动态内存管理相关的知识,我们下次再见~

你可能感兴趣的:(c语言,开发语言,后端,c++)