c语言 枚举类型 uint32_浅谈C语言枚举类型 | 附自创用法分享

经济学家说过,路边是不会有100元的;但如果有,你还是要捡起来。

同理,在貌似万物免费的网络时代,你是很难找到有针对性的好资料;但是如果有,希望你能认真学习吸收。

比如笔者今天写的这一篇f3c111fa33b392c901ae357d7e48c03d.png

今天这篇文章要分享两个案例,第一个案例关于枚举,第二个案例也是关于枚举。

照旧例,先来几句简单的照本宣科。C语言枚举类型用于针对某一类对象定义一个集合,根据该类对象的实际意义将集合中的元素逐一列举出来,然后用实际取值为整数(枚举值)的文本式变量描述这些元素。

这些枚举值相当于一种助记符,可以提供对某一类对象更加贴近实际的描述,所以不仅能够增加程序的可读性,还能帮助码农们分别并记忆它们。当然,在具体的编程活动中,枚举类型也会暂时把码农从枯燥的计算机世界解脱出来,找回一点人间烟火的感觉。

科普完毕,大家可能开始纳闷了。既然从数学概念上来理解,枚举定义了一个“集合”,用整型取值来表示集合中的“元素”,逻辑上如此清晰而且简单,这还可能出什么问题?

你想,平地里可以起惊雷,阴沟里也会翻了船,编程写出个bug来,难道不是意料之外、情理之中的事情吗?

6e81648455b6e0ec6e88ec800102a70a.png

只不过,我始终搞不清楚,编程时,到底一帆风顺无惊无喜是幸福的,还是遇到问题百转千回更幸福?

说到幸福,我不禁想起范伟的一段经典台词,脑袋大脖子粗的范伟端着个大脸盘子,无神的眼睛里透露着看破红尘的沧桑,慢条斯理地回答:“什么是幸福?幸福就是我饿了,看别人拿个肉包子,那他就比我幸福;我冷了,看别人穿了一件厚棉袄,他就比我幸福;我想上茅房,就一个坑,你蹲那了,你就比我幸福。”

同样是简单的枚举,你用时没碰到问题,而我碰上了,你说咱俩到底谁比谁幸福?

道家有一句很玄妙的话:天下本无事,庸人自扰之!

坚定地秉持唯物主义的四有青年们对这句话当然是嗤之以鼻孔兼鼻毛的。

你见或者不见,事儿就在那里,不来不去,但是按照老庄的思想,合着是我们自己没事找事了?

对此等断语,笔者只能微微一笑很倾城,接着苦笑很悲情了。因为我遇到的枚举问题就是自己瞎搞出来的。

8ce935581f073d04428e198dd100e22f.png

本来,同事小周给我的代码里有这么两段代码:

void SendI2cAck(void)

{

   SetSdaDir(IO_DIR_OUTPUT);

   SetSdaLow();

   ToogleScl();

}

void SendI2cNak(void)

{

   SetSdaDir(IO_DIR_OUTPUT);

   SetSdaHigh();

   ToogleScl();

}

明眼人一眼就看出来了,尽管每段代码都很简单,完全没有必要改写,但是由于这两段代码的重复度很高,它们完全可以改写成一个带参量的函数。

尤其对我们这种对代码清理和重构有着偏执型冲动的人来说,让我们不重构简直比杀了我们还难受,此时不改,更待可时?

于是我三下五除二,把代码改成了下面的样子:

voidi2c_ack(e_I2cAck ack)

{

   SetSdaDir(IO_DIR_OUTPUT);

   if(I2C_ACK == ack){

       SetSdaLow();

   }else{

       SetSdaHigh();    }

   ToogleScl();

}

在这里,笔者定义了一个枚举类型:

typedefenum{

   I2C_ACK = 0,

   I2C_NAK = 1

}e_I2cAck;

然后,因为鬼才知道的原因,笔者给出了如下函数声明,也在不经意间埋下了一颗炸弹:

void i2c_ack(uint8_t ack);

看到这里,大咖们可能在捏着下巴上唏嘘的胡茬子会心一笑了,但是小白们也许还是不知所以。

尽管函数的声明误写成了i2c_ack(uint8_tack),但是它的定义i2c_ack(e_I2cAckack)还是对的;在调用函数传递函数参量的过程中,传进去的I2C_ACK难道不还是0,I2C_NAK不还是1吧?

笔者也是这么想的,当然,刚开始的时候,我根本没有发现把声明写错的“笔误”。

不过,埋下的炸弹终会暴雷。由于重构后的程序运行不正常,我很快发现了声明和定义不一致,但是,so what?我依然不得要领,于是只好架上仿真器单步调试,看看到底会发生什么。

我追踪调试到调用i2c_ack的地方,眼见着把I2C_ACK=0传了进去,到了函数里面后,竟然没有执行if(I2C_ACK == ack)这个分支。于是我试着添加了一个uint16_t型的临时变量,将函数参量赋值给它。

不看不知道,一看吓一跳,传递进来的参量竟然成了0x5A00。

追踪到这里,又查阅了相关资料后,我似乎有些开窍了。

尽管8位整型便可以涵盖这次枚举定义中的最大值,但是枚举类型的尺寸是16位,而非所想象的8位。

这样一来,如果函数声明中的参量是16位,那么,在参量传递时,传递进来的枚举类型的I2C_ACK会被处理成16位整型的‘0’,函数会按照‘0’分支进行正确的处理。但是,由于函数声明中的参量是8位,所以,实际上传递进来的枚举类型的I2C_ACK只取了1个8位整型的‘0’;进入函数内部后,它又会被扩展成16位整型;而函数内部的变量是局部变量,地址空间都在stack里面,它扩展时会采用相邻的高位地址来填充该16位整型的高8位。这样,在传递0时,数据低八位依然是0,但是高八位就不一定了。

本来不改程序,还不会遇到这些问题,看看,是不是天下本无事,庸人自扰之?

千百年来,多少人苦苦思索,到底是什么力量,掌握着我们的命运,让我们经历痛苦和欢乐?

现在我明白了,生命不息,折腾不止,正是这种没事找事瞎折腾的力量主宰了我们的喜怒哀乐呀!

笔者分享的第二个关于枚举类型的案例,是更加便利地使用枚举类型进行数组索引的一种新用法,不敢藏私,与诸君共享之。

如前所述,枚举的一个重要作用是增加程序的可读性,以助记符的形式帮助程序员记忆和理解代码。比如,笔者在实现软件定时器时(见文章《如何用单个定时器统一地实现多种定时应用》)就曾经以枚举类型定义了软件定时器的ID或者说软件定时器的名称。

为了让读者更加便于理解,还是要花开两朵各表一枝,叨咕叨咕软件定时器。

一个嵌入式产品中会有很多定时逻辑,最好也是最通用的处理方式便是设计一种结构体形式的软件定时器,令一个软件定时器对应一种定时逻辑,所有软件定时器构成一个结构体数组,各种定时逻辑的实现时便是在结构体数组中的成员变量上进行处理。

在这里,以可读性较强的枚举类型定义软件定时器的ID,枚举值根据各个定时应用的具体逻辑命名。比如说:

  • 检测输入信号的周期性定时器INPUT_DETECT_PTMR;

  • 喂看门狗的周期性定时器FEED_WATCHDOG_PTMR;

  • 监测系统状态的周期性定时器SYS_MONITOR_PTMR;

  • 蜂鸣器报警的多次定时器BEEPTWEET_TTMR;

  • 总线busoff后恢复通信的单次定时器BUSOFF_TTMR等。

高智商的程序猿们打眼一看,就能从枚举值的命名上看出定时器背后的逻辑来,枚举增强程序可读性的功能可见一斑。但是,问题是,您老人家看明白了,单片机呢?

d2c8ec370f07a0b0de7291dc4092ca9c.png

这么说吧,我们在用Timer[INPUT_DETECT_PTMR]处理定时逻辑时,怎么保证这个定时器节点就能具体对应到检测输入信号的周期性定时器?

智商在线的你肯定不会因为INPUT_DETECT_PTMR这个文本化的枚举写得如此得昭彰,就想当然地认为单片机也能“心同此心”的。实际上,如果你不做一些特殊的处理,单片机肯定不知道Timer[INPUT_DETECT_PTMR]就可以表征检测输入信号的周期性定时器的。

愿你三冬暖,愿你春不寒,愿你天黑有灯,下雨有伞。程序猿想和单片机结下此等心心相映的缘,需要做点编程工作,主动手拉手线牵线。

显然,INPUT_DETECT_PTMR此类软件定时器节点ID想在数组中充当下标使用,下标和枚举之间要具有天然的一致性。

所幸,数组Timer[N]的下标范围是[0,N-1]间的正整数,而整型取值正是枚举类型的天然属性。所以,第一步是要保证定时器枚举也从0开始取值,然后取值依次加一,在[0,N-1]间一一占位。

第二步,在定时器数组的初始化阶段,要用整数型下标进行一次for循环,将各个软件定时器节点的ID初始化为对应的数组成员的下标,即Timer[i].timer_id = i。这里的i有三个作用,一是for循环体中的循环变量,二是数组成员下标,三是赋值给定时器ID。

在系统运行阶段,引用某个软件定时器时,以该软件定时器对应的枚举类型常量做为数组下标,引用以该ID标识的软件定时器节点,即用Timer[timer_id]直接寻址具体的软件定时器。这里有一个好处是,避免了以整型变量为下标引用定时器时,需要查找该定时器节点在软件定时器数组中对应的下标的繁琐,而且提高了程序的可读性。

其中妙处,你品,你仔细品!

561658831c191a3475dcd3a0598f115b.gif61b19423bbba5fd0aaf968f4d4190f0d.png扫码入群扫码添加管理员微信

加入“电子产品世界”粉丝交流群

↓↓↓↓点击,查看更多新闻

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/532240.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux在bin下加入ssh,移植 ssh 到开发板

2》编译/home/arm下新建目录sshwork,并且将源码复制到该目录下mkdir /home/arm/sshworkcp zlib-1.2.3.tar.gz openssl-0.9.8d.tar.gz openssh-4.6p1.tar.gz/home/arm/sshwork/home/arm/sshwork下新建目录lib,用来保存生成的库文件。mkdir /home/arm/sshw…

java pdf增删改查_如何利用Java代码操作索引库?

今天是刘小爱自学Java的第161天。感谢你的观看,谢谢你。学习计划安排如下:学了几天的Elasticserch,但都是它本身的知识点,如何通过Java语言去操作它呢?这就好比以前学数据库,在数据库工具中通过sql语句也能…

linux shell 第几行,Linux shell 获得字符串所在行数及位置

shell 获得字符串所在行数及位置01 获取字符串所在的行数方式一:用grep -n[rootroot]# cat testapplebitcreatedelectexeflowgood[rootroot]# cat test | grep -n exe5:exe[rootroot]# cat test | grep -n exe | awk -F ":" {print $1}5方式二&#xff1a…

sublime text3 怎么配置、运行python_SublimeText3按ctrl+b执行python无反应

最后更新时间:2017-09-14 现象: 在Sublime中打开.py文件,按”ctrlb”执行时无反应。点击工具->编译系统中已经有且识别到Python,但执行”run(ctrlshiftb)”时无反应,Sublime左下角提示”No B…

linux 火锅平台,“定制版火锅”来袭,持续创新才能永葆活力

原标题:“定制版火锅”来袭,持续创新才能永葆活力5月1日,重庆涪陵红酒小镇的一家转转火锅店,推出“五一”定制版火锅免费请游客品尝。广西的螺蛳粉、贵州的折耳根、湖南臭豆腐、福建乌龙茶、重庆榨菜、河南胡辣汤、陕西老陈醋、海…

internetreadfile读取数据长度为0_YOLOV3的TensorFlow2.0实现,支持在自己的数据集上训练...

GitHub链接:calmisential/YOLOv3_TensorFlow2​github.com我主要参考了yolov3的一个keras实现版本:qqwweee/keras-yolo3​github.com目前支持在PASCAL VOC 2012数据集上训练和自定义数据集上训练,具体的训练过程可参考项目仓库中的README文档…

c语言用链表对学生成绩排序,学生成绩排序和平均分计算利用c语言链表的创建插入删除.doc...

#define NULL 0#define LEN sizeof(struct student)struct student{long num;float score;struct student *next;};int n;struct student *creat(void)//创建链表{struct student *head;struct student *p1,*p2;n0;p1p2(struct student*)malloc(LEN);scanf("%ld,%f",…

深井软岩巷道群支护技术与应用_深井软岩巷道深浅孔帷幕注浆技术

一、成果内容1.基本原理对失修巷道进行刷扩、支护,满足使用断面后进行帮顶喷浆、底板整平,先底板注浆,然后帮、顶注浆。锚架充支护巷道直接底板整平后,先底板后帮、顶注浆。通过全断面深浅孔联合注水泥浆进行巷道加固,…

店铺咨询系统c语言,课内资源

1 题目介绍1.1 问题描述出于不同目的的旅客对交通工具和交通路径有不同的要求。例如,因公出差的旅客希望在旅途中的时间尽可能短,出门旅游的游客则期望旅费尽可能省,而老年旅客则要求中转次数最少。编制一个交通咨询系统程序,为旅客提供最优决策的交通咨询。1.2 需求分析提供对…

f分布表完整图_【教育统计答疑】如何理解正态分布、均值分布、^2分布、t分布和F分布...

许多教育统计的初学者都表示这几个分布感到学起来非常吃力,结合最近上课的体会以及答疑的情况,觉得很有必要在这里简单地对这部分内容进行澄清和梳理,以助理解。首先,“为什么要学习这几个分布”可能是许多人纠结的问题&#xff0…

c语言第一章考试题及答案,C语言考试题库及答案整理版.doc

C语言考试题库及答案整理版.docC语言理论上机考试选择题部分(共200题)1、下面程序的输出是___D______#includevoid main(){ int k11;printf("k%d,k%o,k%x\n",k,k,k);}A) k11,k12,k11 B) k11,k13,k13C) k11,k013,k0xb D) k11,k13,kb2、在下列选项中,不正确的赋值语句是…

python每天定时9点执行_python每天定时运行某程序代码

思路:利用time函数返回的时间字符串与指定时间字符串做比较,相等的时候执行对应的操作。不知道大家的思路是什么,感觉这样比较耗CPU。。。。 此处设置为15:30:10 输出相应内容,需要执行什么,就修改什么。 import time …

新颖的c语言题目,新颖版c语言经典习题100例(全面面)

新颖版c语言经典习题100例(全面面) (66页)本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦!19.90 积分实用文档C语言习题100例(最新整理版)习题目录:(按住Ctrl点击可以快速跳转到…

c语言jt808协议库,平台如何应答——关于JT/T808协议

前两篇也说明了一些应答的相关内容,对于刚接触的开发者来说恐怕还是不太容易理解,这里专门列举一个篇幅来讲解如何针对终端设备上报的信息进行应答。严格来讲,如果不应答,终端设备会判别为服务平台断开连接,就会重复发…

熊猫的python小课账号_学习python中的pandas有没有好的教程推荐?

之前好多人私信我python数据分析怎么快速入门,我在这里直接介绍一下自己的心得经验吧。 要学习pandas,我并不建议看大量的教程,等看完教程,天都黑了,一觉醒来热情都凉了。 我的建议是,首先放平心态&#xf…

rssi定位算法 c语言,【论文※】An RSSI Gradient-based AP Localization Algorithm 基于RSSI梯度的AP定位算法...

摘要:Recent rapid rise of indoor location based services for smartphones has further increased the importance of precise localization of Wi-Fi Access Point(AP).However,most existing AP localization algorithms either exhibit high errors or need s…

arm体系结构与编程_教程:如何学习嵌入式系统(基于ARM平台)

一、嵌入式系统的概念 着重理解“嵌入”的概念 主要从三个方面上来理解。 1、从硬件上,将基于CPU的处围器件,整合到CPU芯片内部,比如早期基于X86体系结构下的计算机,CPU只是有运算器和累加器的功能,一切芯片要造外部…

ubuntu nfs linux,Ubuntu的NFS功能配置

环境:Ubuntu 10.04步骤:1.sudo apt-get install portmap2.sudo apt-get install nfs-kernel-server注意:第2步我安装失败,换源后仍然如此,最后我在新立得里面搜索到nfs-kernel-server,安装成功3.在更目录下新建共享目录:mkdir /forlinux4.gedit /etc/exprots,在后面添加/forlin…

python import 路径_python import 上级目录的导入

python import 上级目录的导入,路径,上级,文件,目录,递归 python import 上级目录的导入 易采站长站,站长之家为您整理了python import 上级目录的导入的相关内容。 有时候我们可能需要import另一个路径下的python文件,例如下面这个目录结构,…

一个android工程生成两个aar,android studio生成aar包并在其他工程引用aar包(示例代码)...

1.aar包是android studio下打包android工程中src、res、lib后生成的aar文件,aar包导入其他android studio 工程后,其他工程可以方便引用源码和资源文件2.生成aar包步骤:①.用android studio打开一个工程,然后新建一个Module&#…