編程規(guī)范哪家強(qiáng) 我把Misra C講一講
俗語(yǔ)講,無(wú)規(guī)矩不成方圓,有敬畏才知行止。
煌煌人世間,這邊是周吳鄭王,那邊廂何呂施張,大家遵守同一個(gè)規(guī)矩才能你來(lái)我往。
國(guó)有國(guó)法,行有行規(guī),每一行都要定出個(gè)規(guī)范和章程,才不至于天下熙熙,母鴨對(duì)公雞,天下攘攘,你推我也搡。
貓有貓的道,夜里不睡覺(jué),狗有狗的道,見(jiàn)樹(shù)就撒尿。電子工程師寫(xiě)代碼,耶耶切克鬧,編程規(guī)范就有好幾套。
比如被美國(guó)佬視為眼中釘、怎么打也打不死的華為,有一套自己的C語(yǔ)言編程規(guī)范,開(kāi)篇便是大家風(fēng)范:清晰第一,簡(jiǎn)潔為美,頗有幾分道可道、非常道的玄妙。
再比如被美國(guó)佬看做肉中刺,嚷嚷著連著Jack Ma一塊兒起訴的阿里,也有自己的一套編碼規(guī)范,雖然不局限于C語(yǔ)言,也妥妥的“風(fēng)清揚(yáng)”氣場(chǎng),起范兒。
這兩家都是中國(guó)IT行業(yè)的翹楚,搞一套自家的編程規(guī)范,有點(diǎn)振臂高呼,號(hào)召大家卷上一卷的意思。
不過(guò),沒(méi)有金剛鉆,就不攬那瓷器活。小公司固然沒(méi)能力,卻也沒(méi)意愿搞一套自家的編程規(guī)范,因?yàn)?/span>Misra C自有現(xiàn)成,做個(gè)善假于物也的君子,不亦樂(lè)乎?
1、Misra C是啥
“Misra C”是The Motor Industry Software Reliability Association C Coding Standard(汽車(chē)工業(yè)軟件可靠性聯(lián)合會(huì)制定的C編碼標(biāo)準(zhǔn))的首字母縮寫(xiě),最早的版本問(wèn)世于1998,是專(zhuān)門(mén)針對(duì)汽車(chē)工業(yè)軟件安全性的一種C語(yǔ)言編碼規(guī)范。
想要進(jìn)宮,必先自宮,馬要拉車(chē),得先閹割。也可以認(rèn)為,為了提高程序的可靠性和健壯性,Misra C這把手術(shù)刀對(duì)天馬行空的C語(yǔ)言特性進(jìn)行了無(wú)情的閹割。
說(shuō)到這里,為了避免老表們產(chǎn)生不必要的誤解,不得不提上兩句。
華為和阿里編程規(guī)范的側(cè)重點(diǎn)實(shí)則與Misra C有所不同,它們的關(guān)注點(diǎn)并不在程序的健壯性這種“里子”,而在于統(tǒng)一代碼風(fēng)格這個(gè)“面子”。為的是將程序員代碼私產(chǎn)轉(zhuǎn)化為公產(chǎn),以代碼的可讀性提高人員更迭對(duì)應(yīng)的可維護(hù)性和可重用性。
里子很重要,面子當(dāng)然也很重要。因?yàn)椋?/span>代碼是軟件的最終體現(xiàn)形式,是將人的設(shè)計(jì)思維轉(zhuǎn)換為機(jī)器語(yǔ)言的橋梁。它不僅要實(shí)現(xiàn)功能,被機(jī)器所讀懂,更重要的是,依照熟悉的代碼風(fēng)格和良好的可讀性,它可以清晰地向閱讀它的碼農(nóng)們表達(dá)設(shè)計(jì)思想。
就跟現(xiàn)在上海的同胞既要捅嗓子又要捅鼻子一樣,代碼也得兩頭通!
這些代碼風(fēng)格如何,給大家?guī)讉€(gè)例子,自行體會(huì)一下吧。
比如說(shuō)華為規(guī)定每行代碼不能超過(guò)120個(gè)字,阿里規(guī)定每行代碼不能超過(guò)80個(gè)字,再比如每個(gè)函數(shù)不能超過(guò)80行。。。
這些根據(jù)顯示器上能夠容納的字符數(shù)和行數(shù)而針對(duì)代碼風(fēng)格進(jìn)行的規(guī)范,正是Misra C所不曾涉及的。
Misra C不重形式,更重實(shí)質(zhì),它所關(guān)注的是代碼的安全性、穩(wěn)定性、魯棒性。比如說(shuō)所有switch語(yǔ)句里都要有break,以防止意外發(fā)生時(shí)找不到處理分支。
再比如說(shuō),雖然使用遞歸能夠非常‘優(yōu)雅’地實(shí)現(xiàn)某個(gè)功能,但是Misra C規(guī)定:一個(gè)函數(shù)不能直接或者間接地調(diào)用自己!
Anyway,華為阿里編程規(guī)范和Misra C的終極目的都是為了讓程序員寫(xiě)出一手好代碼,方向是一致的,道路是不同的。也可以說(shuō):
皓月當(dāng)空,以手指月,華為和阿里在乎的是那只手,而Misra C在乎的是那輪月。
2、Misra C的背景
黑格爾說(shuō):凡是存在的,都是合理的。那么,誕生于上個(gè)世紀(jì)70年代初的C語(yǔ)言,怎么就在上個(gè)世紀(jì)末的時(shí)候,迎來(lái)了Misra C這么個(gè)婆婆?
或者說(shuō),Misra C的合理性在哪兒?這個(gè)婆婆,看不上C大姐哪一點(diǎn)了?
C語(yǔ)言身材苗條(結(jié)構(gòu)緊湊)、能說(shuō)會(huì)道(表達(dá)能力強(qiáng))、溫順體貼(靈活性強(qiáng)),在嵌入式碼農(nóng)這個(gè)圈圈里,很是招人喜歡。
碼農(nóng)們喜歡換著花樣擺弄C語(yǔ)言,有時(shí)還會(huì)故意使用一些花里胡哨的特性。但是,正如張無(wú)忌他媽說(shuō)過(guò)‘越是漂亮的女人越會(huì)騙人’一樣,越是靈活的特性,越會(huì)導(dǎo)致粗心大意的編碼人員寫(xiě)出危險(xiǎn)的代碼。
梅西說(shuō):“我不是天生強(qiáng)大,我只是天生要強(qiáng)”。灑家說(shuō):“C語(yǔ)言并非天生安全,它只是天生就帶著缺陷”。
所以,為了對(duì)治C語(yǔ)言先天的固有缺陷,Misra C橫空出世,針砭C弊。
金庸老爺子說(shuō):萬(wàn)物相生相克,凡是毒物,七步之內(nèi)必有解****。C語(yǔ)言先天帶毒,自然有Misra C來(lái)跟它相愛(ài)相殺一番。
最初,Misra C只面向汽車(chē)行業(yè),針對(duì)的是汽車(chē)行業(yè)的嵌入式開(kāi)發(fā),后來(lái),從MISRA-C:2004開(kāi)始,它便擴(kuò)大了覆蓋范圍,面向其它高安全性系統(tǒng)了。
畢竟,都是嵌入式,都在搞C代碼開(kāi)發(fā),大哥能比二哥好哪去?所以,Misra C的觸角迅速延伸到安全性至關(guān)重要的許多其他應(yīng)用領(lǐng)域,搞起了獨(dú)樂(lè)樂(lè)不如眾樂(lè)樂(lè)的大生態(tài)。
周公制禮,天下歸心。規(guī)矩,是讓人心安的,Misra C,也可以讓嵌入式行業(yè)的一眾用戶(hù)心安。
3、Misra C規(guī)則原因解讀
Misra C的規(guī)則干巴巴,看官看著打哈哈。
網(wǎng)上有不少對(duì)Misra C規(guī)則的解讀,無(wú)需灑家再置喙。灑家是覺(jué)得,規(guī)則解讀固然重要,但更重要的是,搞清楚Misra C制定這些規(guī)則的原因,咱不僅要知其然,更要知其所以然。
3.1 靈活是陷阱
C語(yǔ)言的靈活性是讓碼農(nóng)對(duì)之愛(ài)不釋手的原因之一,但是,汝之蜜糖,彼之砒霜。對(duì)高手而言,靈活的編程方式和語(yǔ)法規(guī)則是他們發(fā)揮高超水平的蜜糖,對(duì)菜鳥(niǎo)來(lái)說(shuō),便成了處處挖坑的砒霜。
比如if(val++)和if(++val) 這兩種寫(xiě)法里,它可以同時(shí)把if語(yǔ)句和val的累加操作結(jié)合在一起,對(duì)高手而言,能省掉一行代碼,確實(shí)很靈活,但有時(shí)卻會(huì)違背了菜鳥(niǎo)碼農(nóng)的本意。
因?yàn)檫@里面有兩個(gè)陷阱。
其一,前種寫(xiě)法是先用當(dāng)前val進(jìn)行判斷,再讓val累加,后一種寫(xiě)法則是先讓val累加,再用累加后的val判斷。
此外,如果val是個(gè)指針,菜鳥(niǎo)又可知,這些累加并非加一,而是加上指針本身的長(zhǎng)度呢?
所以,靈活性是一把雙刃劍,刀刃向內(nèi)還是向外全憑本事。
正所謂:二八佳人體似酥,你要是沒(méi)那金剛杵,她就暗里教君骨髓枯。
3.2 C語(yǔ)言的運(yùn)行時(shí)檢查能力較弱
灑家曾經(jīng)寫(xiě)過(guò)一段時(shí)間的Java,剛開(kāi)始寫(xiě)時(shí),便對(duì)它能動(dòng)態(tài)檢查數(shù)組越界贊不絕口,啥時(shí)候C代碼也能這樣該有多好?就拿下面來(lái)說(shuō),
uint8_t Val[10];
…
Val[idx] = 0;
若是運(yùn)行時(shí)idx =10,Val[idx] = 0這個(gè)操作便會(huì)直接越界到和Val相鄰的變量(假設(shè)為Ino)那里。
假設(shè)您在別的地方給Ino賦了值,根據(jù)其值執(zhí)行不同的動(dòng)作邏輯,本來(lái)一切歲月靜好,結(jié)果神不知、鬼不覺(jué),Ino好端端地突變成了0,您還不得直呼見(jiàn)鬼了!
這種bug,來(lái)無(wú)影去無(wú)蹤,一下就挖個(gè)大深坑。你左看看,右瞧瞧,怎么也想不到是鄰居甩的刀!
3.3 電腦的行為同程序員預(yù)期的不同
被baby甩掉的黃曉明曾經(jīng)說(shuō)過(guò):“我不要你覺(jué)得,我要我覺(jué)得。。?!?/span>
在C代碼里,也經(jīng)常出現(xiàn)程序員以為這樣,而電腦卻以為那樣的情況。
比如說(shuō)您寫(xiě)了下面三行代碼:
uint8_t Pres;
uint16_t Ev_pres;
Ev_pres = (uint16_t)((uint32_t)(Pres * 350) / 255);
您自認(rèn)并非菜鳥(niǎo),自以為然地在心中嘀咕著:“菜鳥(niǎo)們應(yīng)該想不到,這里面的奧妙之處在于Pres * 350的結(jié)果可能會(huì)超出uint16_t的上限65535,數(shù)據(jù)會(huì)發(fā)生截?cái)?,所以,把這個(gè)結(jié)果加個(gè)(uint32_t)就萬(wàn)無(wú)一失了。”
可是,你以為這樣,老板就給你加雞腿了?加個(gè)棒槌!
因?yàn)?/span>,如果Pres=200,你以為Ev_pres=200*350/255=274,但電腦運(yùn)行后的結(jié)果卻是Ev_pres=17。
為什么呢?聰明的讀者可能看出來(lái)了,問(wèn)題出在(uint32_t)(Pres * 350)這里,Pres * 350在數(shù)據(jù)截?cái)嘀笤?/span>(uint32_t)已經(jīng)沒(méi)有意義了,應(yīng)該改成下面這種寫(xiě)法:
Ev_pres = (uint16_t)((uint32_t)Pres * 350 / 255);
這個(gè)例子也說(shuō)明,要想和電腦哥倆好,括號(hào)的位置很重要!
總結(jié):
搞電子的都知道,代碼寫(xiě)得好,調(diào)試的活就能省不少,代碼寫(xiě)得糟,調(diào)試時(shí)踩的坑少不了。
所以,按捺住那顆蠢蠢欲動(dòng)的心,老老實(shí)實(shí)地遵循規(guī)范標(biāo)準(zhǔn),就能少埋坑,少踩雷,切切實(shí)實(shí)地碼出質(zhì)量,碼出穩(wěn)定性來(lái)。
筒子們,加油!
*博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀(guān)點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。