本篇内容主要讲解“C语言结构体的位域是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C语言结构体的位域是什么”吧!
我们之前讲结构体的时候,都是用int,char之类的数据类型来定义结构体的成员变量的,这些成员变量都有一个共性,就是他们的长度都是一个字节,或者一个的偶数倍。然而我们在存储某些信息时,并不需要一个完整的,而是只需要让这个变量占据一个或者几个二进制位。
可能有些读者会想,那我直接使用一个字节的长度来存储这些几位的变量即可,虽然会浪费一些存储空间,但是这些位的浪费对于现在的一些计算机或者单片机来说都是无关紧要的。确实,当我们定义一个变量的时候,对于目前计算机强大的硬件来说,定义以一个几个位的变量和定义一个几个字节的变量确实没有任何影响,反而字节单位变量比位变量有更大的存储空间,可以有效地防止长度溢出。
但是,当面对下面这种应用时,字节单位变量不仅没有任何好处,反而会大大增加我们程序的操作难度。如,在一个单片机系统中有一个寄存器。假设这个寄存器的长度为一个字节,它的功能是用来控制一个单片机的定时器以及反应一个定时器的状态,这个寄存器的第7,6位表示定时器的状态位TIM_STAT[1:0],第5,4,3,2位表示定时器时钟源的分频系数TIM_DIV[3:0],第1位表示定时器的溢出标志TIM_OVERFLOW,第0位表示定时器的工作开关TIM_START/STOP。具体这个假设的寄存器如图1所示。
图1 某寄存器
面对上面的这种应用,我们一般的做法就是定义一个unsigned char类型的变量Tim_Ctrl,然后进行位操作,比如要将TIM_STAT赋值状态0b11,那我们就可以使用位操作语句,“Tim_Ctrl |= 0b11000000;”,如果要将TIM_STST赋值状态0b00,就使用位操作语句“Tim_Ctrl &= ~(b11000000);”,这种微操作的方式非常繁琐,而且直观性很差。
那是否有一种数据类型可以支持这种位数比较少的变量呢?比如直接可以定义一个两位的变量,然后赋值状态0b11即可。在C语言中,常规的变量明显是不支持这种操作的,但是在结构体中却支持。这种C语言结构体中支持位操作的方式被称为“位域”,或者“位段”。
位段(或称“位域”,Bit field)为一种数据结构,可以把数据以位的形式紧凑的储存,并允许程序员对此结构的位进行操作。这种数据结构的好处:
可以使数据单元节省储存空间,当程序需要成千上万个数据单元时,这种方法就显得尤为重要。
位段可以很方便的访问一个整数值的部分内容从而可以简化程序源代码。
而位域这种数据结构的缺点在于,其内存分配与内存对齐的实现方式依赖于具体的机器和系统,在不同的平台可能有不同的结果,这导致了位段在本质上是不可移植的。
位域的定义是在结构体中定义的时候完成的,其定义方式如下:
struct
{
数据类型 变量名 :位长度;
数据类型 变量名 :位长度;
} status;
比如,对于上面这个定时器的寄存器,我们可以定义如下:
struct
{
unsigned char TIM_START_STOP : 1;
unsigned char TIM_OVERFLOW : 1;
unsigned char TIM_DIV : 4;
unsigned char TIM_STAT : 2;
} register;
注意,这样定义好之后,整个结构体的位域定义时都是从低地址开始的。因此,当一个位域被定义好之后,其内存的分布如图2所示。
图2 位域内存分配
一旦当位域定义好之后,比如图2中的TIM_DIV,它所占用的比特数为4bits,因此虽然我们定义它的时候是用unsigned char类型去定义的,但是它最多能表示的二进制数只有4位,即范围为:0x00~0x0F。一旦当我们赋值超过了次范围,这个变量就会将多余的高位数据舍弃。
我们可以写一个程序来论证,按照图1所示的寄存器位分布,定义结构体位域变量,接着给它赋一个超出它长度的值,然后打印出来看看输出。如图3所示。
图3 结构体位域成员超出范围
从图3中我们可以看出,一旦当某个位域成员超出其位数大小之后,编译器先会抛出一个警告,然后将这个变量打印出来的值也是不对。那么为什么我们赋值20,却输出一个4呢?这是因为20的二进制数是0b00010100,而由于TIM_DIV变量只占有4个bit的存储空间,因此超出的部分会被舍弃,最终只保留低4位0b0100,因此这个变量打印出来的值为4。
由于这个结构体变量是占一个字节的存储空间,因此我们可以用一个指针打印出这个存储空间的全部内容。操作也很简单,我们只需定义一个unsignedchar类型的指针,并且使结构体的地址强行转换为一个“unsigned char *”类型,然后用指针指向它,最后引用指针将这个地址打印出来,就可以看到这个结构体全貌了。具体操作如图4所示。
图4 结构体数据
为什么最后结果是0xA7呢?因为整个结构体按照位域赋值之后如图5所示,最后转换成十六进制就是0xA7了。
图5 位域赋值之后
到此,相信大家对“C语言结构体的位域是什么”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。