新闻中心

EEPW首页 > 牛人业话 > 写代码要小心地盘 阴沟里也能翻了船

写代码要小心地盘 阴沟里也能翻了船

作者:三昧道人时间:2019-06-21来源:电子产品世界收藏

笔者的同事阳春君春节期间喝大了,乐极生悲摔伤了手。在做完手术后的复健阶段,医生让他盘核桃,帮助恢复肌腱。

本文引用地址:http://www.amcfsurvey.com/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改代码的人,实际上犯的是贪功冒进的错误,总以为量变才能质变,多写代码才能把水平提升上去。其实这种认识非常片面,这种方式也很不可取,对自我能力的提升非常不利。

因为,只有在一次次的量变中有小的质变,才能最终练成编码神功。每次都马马虎虎,这样的编码经历就像掰玉米的狗熊一样,一边得到,一边丢弃,到最后其实剩不下多少有用的东西。

不愿意下功夫盘代码,也是一种懒惰。好逸恶劳是人之通病,所以好的程序猿总是难寻。

对待代码,需要确立不求尽善尽美、但求问心无愧的心态。要仔细地盘,耐心地盘。

想必,我的经历也能给大家一些启发:代码一定要耐心地盘,阴沟里也能翻了船!



关键词: 汽车电子 代码

评论


相关推荐

技术专区

关闭