文/Edward

接下来再回到我们结构体的话题中来,我们之前讲结构体的时候,都是用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 charTIM_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语言之强大的结构体,【C语言资料更新】结构体的“卫浴”(位域)相关推荐

  1. C语言结构体学生基本资料,用结构体定义10个学生基本信息

    用结构体定义10个学生基本信息,用到结构体.子函数.文件等的相关知识. /* ************************************************* 2.1.用结构体定义1 ...

  2. 为何C语言如此强大?到底可以做什么?

    C语言几乎是所有语言的实现基础,所以不存在做不了的事情,只是相对来说谁做更合适,编写游戏和软件都不是问题. 为何C语言如此强大? 1.C语言是许多高级计算机语言的基础,学好C语言能更好的学习其他高级语 ...

  3. C语言循环语句实训项目,C语言程序设计与项目实训

    C语言程序设计与项目实训 编辑 锁定 讨论 上传视频 <C语言程序设计与项目实训>是2013年清华大学出版社出版的图书,作者是吴艳平.徐海燕. 书    名 C语言程序设计与项目实训 作  ...

  4. c语言 输入职工工资资料,通过结构体的数组来进行操作 报告,c语言课程设计报告-职工工资管理系统...

    <c语言课程设计报告-职工工资管理系统>由会员分享,可在线阅读,更多相关<c语言课程设计报告-职工工资管理系统(33页珍藏版)>请在人人文库网上搜索. 1.c 语言课程设计报告 ...

  5. c语言用typedef定义结构体,C语言结构体定义 typedef struct

    c语言规范,定义结构体: typedef struct ANSWER_HEADER { u8 u8Type; u8 u8Code; u32 u32TimeStamp; struct ANSWER_HE ...

  6. c语言结构体与共同体课件,《结构体与共同体》PPT课件.ppt

    <<结构体与共同体>PPT课件.ppt>由会员分享,可在线阅读,更多相关<<结构体与共同体>PPT课件.ppt(44页珍藏版)>请在装配图网上搜索. 1 ...

  7. 一文攻破结构体-C语言

    一文攻破结构体-C语言 1 什么是结构体 1.1 结构体类型 结构体(structure)是由不同类型数据组成的组合型的数据结构,可以看做变量的集合.结构体也是一个数据类型,就像和 int.char一 ...

  8. c++ new一个结构体_C语言结构体,又一个纸老虎,纯干货讲解(附代码)

    来源:网络,排版整理:晓宇 微信公众号:芯片之家(ID:chiphome-dy)结构体的定义结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合,也叫结构.结构体和其他类型基础数 ...

  9. c语言sqlist结构体,c语言里 sqlist

    满意答案 cielkong 2018.08.12 采纳率:43%    等级:9 已帮助:463人 c语言里 sqlist?//定义顺序表L的结构体 typedef struct { Elemtype ...

  10. 【精华文】C语言结构体特殊情况分析:结构体指针 / 基本数据类型指针,指向其他结构体

    参考链接:Structure pointer pointing to different structure instance 注:可以查看此篇的问题和唯一的回复,那是相对正确的,不要看comment ...

最新文章

  1. 资料分享:送你一本《机器学习实战》电子书!
  2. C#程序员的春天之从零开始学习unity3D游戏开发入门教程二(创建项目及基本面板介绍)...
  3. kmalloc、vmalloc、malloc、calloc的区别
  4. 关于实现无限循环的做法
  5. 【转】.NET Core全面扫盲贴
  6. .Net Core中的Api版本控制
  7. [Err] 1064 - You have an error in your SQL syntax check the manual that corresponds to 之Mysql报错
  8. 一些不起眼但非常有用的 Vim 命令
  9. 基于JAVA+Servlet+JSP+MYSQL的高校社团管理系统
  10. r语言中paste函数_R中的paste()函数-简要指南
  11. 为什么要打jar_为什么海带要打一个结?
  12. [Error] expected primary-expression before '' token是什么意思
  13. 《天长地久》写照真实生活为何被影迷埋没不被欣赏
  14. 五行代码实现图像识别(深入版)
  15. web前端HTML和CSS3常见面试题
  16. Docker自定义镜像上传阿里云
  17. 【arm】ARM32和AARCH64的几点区别
  18. python判断路径是文件还是文件夹_python 判断文件还是文件夹的简单实例
  19. 局域网网上邻居无法访问问题的解决
  20. iris解决跨域问题

热门文章

  1. Allegro制作4层PCBA板的练习-非接触式传感器
  2. 【K8S】整体原理-什么是K8S K8S能做什么
  3. pandas dropna()函数
  4. 最小二乘法求解多项式系数
  5. Golang 安装 Golang
  6. R语言绘图—热图绘制
  7. java Long 转Integer
  8. 汽车汽配行业经销商管理平台实时掌握一线状态,提升企业决策效率
  9. python-opencv车牌检测和定位
  10. 10款最优秀的开源移动开发工具