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.pswp.cn/news/532240.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/532240.shtml
英文地址,請注明出處:http://en.pswp.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&#…