新聞中心

EEPW首頁 > 牛人業話 > 寫代碼要小心地盤 陰溝里也能翻了船

寫代碼要小心地盤 陰溝里也能翻了船

作者:三昧道人時間:2019-06-21來源:電子產品世界收藏

筆者的同事陽春君春節期間喝大了,樂極生悲摔傷了手。在做完手術后的復健階段,醫生讓他盤核桃,幫助恢復肌腱。

本文引用地址:http://www.rycdhb.live/article/201906/401791.htm

可是,陽春君的手既肥且小,普通大小的核桃抓一個綽綽有余,抓兩個就頗有些力不從心。而且,“盤”核桃這活兒大有技術含量,揉、捏、捻、擼、搓,各種手法,各有各的門道。所以,這只平時敲鍵盤、寫的手剛開始“盤”核桃時,動作生硬,毫無章法。

玩家們盤上一段時間的核桃,就會變得既紅且圓潤。可核桃到了他的手里,過了一段時間,就好像受了十大酷刑一般,體無完膚,斑駁丑陋。讓人奇怪的是,雖然盤核桃接二連三地失敗,他的手竟而漸漸好了起來,又能噼里啪啦地敲了。

這段盤核桃的經歷在陽春君的身上打下了深深的烙印。他經常把我拉過去,幫著一起找運行出錯的中的bug。看著他那縱橫交錯的代碼,我經常提醒他把代碼再重構一下,這樣方便找問題。每每這時,他就會盯著屏幕上的代碼,對著不知藏在何處的bug咬牙切齒地說:盤它!

timg (1).jpg

1

萬物皆可盤,核桃如是,代碼亦然。

核桃自不必待言,渾然天成的疙疙瘩瘩,自然而言的天然丑,一副迫切求“盤”的樣子,求仁得仁,不盤它盤誰?

u=3566979592,4253186926&fm=26&gp=0.jpg

代碼呢?基督說,在神面前,人生而不完美。人猿同祖同宗,不完美的程序“猿”寫出來的代碼自然也不會盡善盡美了,故而同樣存在被“盤”的需求。

在被盤的過程中,核桃被千般揉捏,萬般搓捻,終于洗盡鉛華,文藝氣息盡顯。代碼則被一次次地重構,抽筋換骨,改頭換面,最終臻于穩定可靠、好看好用。

盤核桃的過程是美妙的。十全老人曾作詩表達對盤核桃的喜愛:掌上旋日月,時光欲倒流。周身氣血涌,何年是白頭。盤得如癡如醉,忘了歲月之憂。

盤代碼的過程卻是苦樂夾雜的。倘若小心翼翼,技術精湛,代碼溫順如牛,在自己的手下一步步變得完美、和諧,碼農一樣可以樂而忘憂。可如果粗心大意,或盤功很爛,搞得代碼猶如脫韁之馬,攜bug之威只是發出那冷笑,碼農就只能周身氣血涌,早日白了頭了。

有那么幾次,筆者就因為不小心,代碼沒有好好地盤,在陰溝里翻了船。

2

這個世界上最遙遠的距離,不是相隔千山萬里,而是你站在我的面前,我就是搞不懂你。

對于代碼,對于程序,我經常生出這種“你為什么就是不懂我?!”的哀怨。

1.jpg

比如我曾經寫過下面這句代碼:

Ev_ia=Can_bms.ial+(uint16_t)Can_bms.iah<<8;

背景很簡單,就是根據BMS(電池管理系統)這個CAN節點發來的電流高字節(iah)、低字節(ial)計算出當前電動汽車的電流大小。計算方法為:高字節左移8位,再和低字節相加。

看,計算多么簡單,簡直是把心都剖開了給你看。但是,歲月劍拔弩張,這世界并非你想的那樣。

調試過程中,錯誤在第一時間就跳了出來,因為我發現,BMS送來的低字節電流為0x64,高字節電流為0時,運算結果居然成了0x6400。

錯誤是顯然的,原因也是簡單的。我立馬敏銳地意識到是“運算符的優先級”問題。查表一看,果然如此。

左移運算符(<<)的優先級低于相加運算符(+),故而,在計算機的世界中,真正的運算過程為:

0x64+(uint16_t)0 = 0x64;0x64 << 8 = 0x6400

而不是自己心中的想當然:0x64 + (0 << 8)=0x64

細究起來,這可以被認為是人-機之間存在誤會,而誤會無處不在。

宇宙黑暗森林中的各個文明之間充滿了猜疑,隨時準備發起黑暗打擊。地球文明中的超級大國美國和中國之間充滿了猜忌,貿易戰打得如火如荼。就是朝朝暮暮的情侶之間也各種誤會,隨時捕捉著對方的不信任。

碼農和他鐘愛的程序之間吶,也被這宿命般的誤會搞得不能你儂我儂,地久天長到那山無棱。

3

上面這個bug還算是有情可原,畢竟世間之事千般萬種,被世事搞得體力虛弱、腦力孱弱的碼農們,很難在編程語言的語法上掌握地非常全面。

世道艱難,人生無常,程序猿過得很累,我們要原諒他。

但是還有一種本來不該出現的bug,它不僅出現了,竟然還長時間地呆在那里,直到你把它捉走時,才會發覺這種bug出現得多么不可理喻。

筆者就在一款產品的小批量試產階段發現了一個本不該出現、出現后也不該活過半天的bug。這只bug通過了功能確認,扛過了車廠的路試,活到了小批量試產階段。

這款產品中有個車速檢測功能,通過ABS發來的兩個字節的車速數據計算出當前車速,根據車速的變化自動對車門上鎖。

兩個高低字節經過移位、相加得到一個雙字節數據,似曾相識吧。沒錯,和上面那個計算電動車當前消耗電流的方法一樣。

吃一塹長一智的筆者肯定不會犯同樣的錯誤,這一次,灑家犯了新的錯誤。

2.jpg

我居然把存放一個中間車速的變量定義成了8位單字節類型!把一個本該是16位雙字節類型的數字塞到單字節里,你可以想象那是怎樣一種荒唐的bug。

uint16_t Speed_abs;

//Uint8_t Speed_abs;//錯誤就在這里!!!

Speed_abs=can_ABS330.msg_data.sig.vehicle_speed_l+ (can_ABS330.msg_data.sig.vehicle_speed_h << 8);

if(Speed_abs <= 0x12c0){

Speed_quant = Speed_abs;

CheckSpeed();

}

出現這種錯誤,當然是不可原諒的,但是,人有失算,馬有失蹄,偶爾腦袋短路,似乎也無法全然避免。事情的詭異當然不在這里,它的神奇之處在于,這種低級別的bug居然一路通關,活到了最后。

事后想來,整件事都透露著神奇和詭異。

人生充滿了陰差陽錯,各種說不清道不明的東西,會讓我們陷入莫名其妙的境地。這個神奇的bug活了這么久,將這種陰差陽錯詮釋地淋漓盡致。

4

這段錯誤的代碼是在產品開發的最后階段引入的。最后階段的主要工作是查缺補漏,盡可能地堵住各種可能的隱患。

本來的車速計算和判斷程序里沒有和最高有效值0x12c0的判斷,直接從ABS數據計算出當前車速,賦值給Speed_quant。不知怎么的,我覺得最好是在這里加上對最高有效值的判斷,于是就定義了一個中間變量Speed_abs,當它小于等于0x12c0時,再把它賦值給Speed_quant。

沒用的好心,結果卻變成了壞事。本來取值區間在0-0x12c0之間的車速值,被硬生生地限制在了單字節的取值區間(0-0xff)里。想一下吧,有一個神力把三維的你給拍扁了,放進了二維的畫里面,憋屈不憋屈?

本來,這樣的bug不該活過半分鐘,結果,因為我們這個產品是在車廠的試驗車上進行驗證,試驗車的油箱基本上空了,車廠工作人員也不允許我們開起車來跑,于是,這個判斷車速、賦值車速、根據車速自動閉鎖的程序分支一直沒有得到驗證。Bug就這樣活過了第一關。

盡管如此,在路試階段,由于這個bug的存在,加速閉鎖功能就失效了。那么,跑車的路試員怎么竟而沒有發現這么明顯的錯誤呢?

難道路試員每次開車之前,都會先中控閉鎖一下?或者我們的合作伙伴給車廠送的樣件里燒錄的居然不是我給他們的最后一版程序?

實情如何,已無歷史可考了,這樁事件終于成了令我百思不得其解的懸案。只有它的詭異,時常浮現在我的心頭,讓我在敲代碼時更存了一分小心翼翼和戰戰兢兢。

戰戰兢兢,如履薄冰,如此,君子可不立于危地。

5

對于一個原始狀態的代碼,一位好的碼農會耐心地在之上精耕細作,仔細地捉蟲,耐心地施肥,小心地呵護,務求盡善盡美,這樣子,才能搞出健壯、耐看的果實來,這樣子盤出來的代碼才能經受住時間的考驗。

相反,有的碼農寫了一段代碼后就把它扔在那里,就好像只管生不管養的無良父母一樣,急火火地又去寫下一段代碼。沒有對代碼的耐心呵護,缺乏對它的修剪、完善,最終,搞出的程序總是既不穩定,又不耐看。

u=3790028067,1002618127&fm=26&gp=0.jpg

不愿意耐心找bug改代碼的人,實際上犯的是貪功冒進的錯誤,總以為量變才能質變,多寫代碼才能把水平提升上去。其實這種認識非常片面,這種方式也很不可取,對自我能力的提升非常不利。

因為,只有在一次次的量變中有小的質變,才能最終練成編碼神功。每次都馬馬虎虎,這樣的編碼經歷就像掰玉米的狗熊一樣,一邊得到,一邊丟棄,到最后其實剩不下多少有用的東西。

不愿意下功夫盤代碼,也是一種懶惰。好逸惡勞是人之通病,所以好的程序猿總是難尋。

對待代碼,需要確立不求盡善盡美、但求問心無愧的心態。要仔細地盤,耐心地盤。

想必,我的經歷也能給大家一些啟發:代碼一定要耐心地盤,陰溝里也能翻了船!



關鍵詞: 汽車電子 代碼

評論


相關推薦

技術專區

關閉
澳客网彩票