【C语言】使用结构体实现位段

发布于:2025-03-21 ⋅ 阅读:(10) ⋅ 点赞:(0)

一、位段

前面我们学习了结构体,位段的声明和结构体是一样的,其区别如下:

1、位段的成员必须是int 、unsigned int 、signed int 、在C99中位段的成员的类型也可以选择其他类型。

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

如下:

那么这几个变量后面的数字表示啥意思呢?会不会是表示其二进制的位数呢?

我们继续往下分析

二、位段的内存分配

1、位段的分配规则:

(1):位段的成员可以是int , unsigned  int  ,   signed  int ,char类型。

(2):位段在空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的,比如说               整型int的话,那么一次性就开辟4个字节,是字符型(char)那么一次性就开辟1个字节。

(3):如果开辟空间不足的话,那么才会再次开辟对应的空间,不会一次性就开辟完所有的空                   间 ,而是一个一个开辟的。

下面我们通过一个练习来了解位段

练习1:

那么位段在内存中的空间是如何开辟的呢?

我们先来看第一个成员,第一个成员是char型吗,那么首先会开辟一个字节的空间,然后将a存放进去,然后我们的a的后面是3,那么其为3个bit位,那么其是如何放进内存的呢?是从左边开始放还是从右边呢?

位段从开辟空间的左边还是右边开始存放,C语言中并没有规定,那么就是说其从左边和右边开始使用都是有可能的。

那么我们当前使用的vs2022的环境是从右边开始使用的, 那么我们按照这个逻辑去看这个位段是怎么存放。

首先我们先存入a,其占3个bit位,然后我们再看第二个成员,其占用4个bit位,然后我们现在还有5个bit的位置,那么足够其存放,那么其就从右边往左边接着存放。

然后我们看第三个成员,其占用5个bit位,但是现在只有一个bit位了,那么空间就不够了,那么此时就会开辟新的空间,然后又因为第三个成员是char字符型,那么就会开辟一个字节的空间。但是此时第三个成员是接着使用前面多的那个bit的空间使用,还是在新开辟的空间使用呢?

这个在C语言中也没有明确的规定。

所以两种可能完全取决于我们的编译器。

我们可以使用sizeof关键字查看当前的位段是多少字节的来判断我们的vs2022是那种方法:

1、如果是接着使用剩下的bit位的话那么这个位段的大小:
     第三个成员存放后,那么此时开辟的第二个字节的空间还有4个bit的空间。

     然后我们看第四个成员,其是4个bit位,那么剩下的刚刚好足够存放,那么此时的大小应该为2       个字节。

2、如果是从新开辟的位置开始

     那么其再第四个成员的时候,因为新开辟的空间还有3个bit位,那么我们就需要再开辟一个字         节那么此时的大小就为3字节了

最后我们看程序的运行结果:

可以看到其得到的结果是方法二的存放方式。

为了证明我们的猜想是正确的,我们进一步验证,我们可以对这个位段进行赋值。

如下:

 

我们首先创建了一位段S,然后将其初始化为0,然后我们对这个位段的成员进行赋值,首先就是第一个成员对其赋值10,那么其二进制为1010,然后其在位段中只是占了3个bit位,那么此时就会发生截断,然后其变成010,然后存进存储,然后我们看第二个成员,其赋值为4,其二进制为1100,然后其是4个bit位,那么接着存入,然后我们看第三个成员其是5个bit位,其二进制为 00011,此时需要开辟下一个字节,然后从下一字节开始存入,那么此时第一个字节存放的内容为:01100010,然后我们看第四个成员,其要使用4个bit位,那么需要再开辟一个字节的空间了,然后此时第二个字节中存放的内容为:00000011,第三个字节存放的内容为:00000100。

下面我们查看内容看看:

可以看到在内存中的存放确实如我们说的这样。

那要是遇到不同的平台,这时候该怎么办呢?下面我们来看看这个问题。

三、位段的跨平台问题

位段虽然可以省空间,但是其存在着很大的跨平台问题,可移植性低,这是因为在C语言中,位段没有一个标准,导致在不同的编译器中,其实现的结果可能是不一样的。下面我们看看其主要的问题:
1、int位段被当成有符号还是无符号是不确定的。

2、位段中最大位的数目不能确定,因为在32位的机器中,int的大小是2字节,而在64位机器中的大小是4字节,那么就 会出问题了。

3、位段中的成员在内存中的存入顺序可能不同,因为没有标准,其可能会从左边开始往右边存            入,这样也会导致数据的结果是不同的。

4、当一个结构包含两个位段,第二个位段成员比较大,无法容纳第一个位段成员比较,那么是舍       弃剩下的位还是,还是继续使用,这个也是没有一个标准的。

所以,我们在使用位段的时候,一定不要跨平台使用,不过其比较省空间。

四、位段的应用

下图是⽹络协议中,IP数据报的格式,我们可以看到其中很多的属性只需要⼏个bit位就能描述,这⾥ 使⽤位段,能够实现想要的效果,也节省了空间,这样⽹络传输的数据报⼤⼩也会较⼩⼀些,对⽹络 的畅通是有帮助的。

五、位段使用的注意事项

通过上面的学习,我们知道位段的几个成员是可能存在共有同一个字节的情况的,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位置是没有地址的,因为在内存中地址是以字节为单位的,然而bit是没有地址的。

所以我们不要对位段的成员使用取地址&。

那么我们就没办法直接对取进行输入值了,也就是没办法直接对其使用scanf关键字了,那么我们实在是需要从就键盘对其赋值呢?是不是就没办法了?有的,有的。

下面我们先看一个错误的示范:

可以看到此时编译器都直接报错了,这是因为这里b只使用了5个bit位,没有单独使用使用一个字节位,所以这个&sa.b的方法是错误的。

我们来看看正确的写法:
 

这里我们创建了一个整型变量b,那么我们可以对这个整型变量进行&了,那么就可以先对这个整型变量b进行赋值,然后再将这个值赋个位段的成员即可。