"); //-->
俗语讲,无规矩不成方圆,有敬畏才知行止。
煌煌人世间,这边是周吴郑王,那边厢何吕施张,大家遵守同一个规矩才能你来我往。
国有国法,行有行规,每一行都要定出个规范和章程,才不至于天下熙熙,母鸭对公鸡,天下攘攘,你推我也搡。
猫有猫的道,夜里不睡觉,狗有狗的道,见树就撒尿。电子工程师写代码,耶耶切克闹,编程规范就有好几套。
比如被美国佬视为眼中钉、怎么打也打不死的华为,有一套自己的C语言编程规范,开篇便是大家风范:清晰第一,简洁为美,颇有几分道可道、非常道的玄妙。
再比如被美国佬看做肉中刺,嚷嚷着连着Jack Ma一块儿起诉的阿里,也有自己的一套编码规范,虽然不局限于C语言,也妥妥的“风清扬”气场,起范儿。
这两家都是中国IT行业的翘楚,搞一套自家的编程规范,有点振臂高呼,号召大家卷上一卷的意思。
不过,没有金刚钻,就不揽那瓷器活。小公司固然没能力,却也没意愿搞一套自家的编程规范,因为Misra C自有现成,做个善假于物也的君子,不亦乐乎?
1、Misra C是啥
“Misra C”是The Motor Industry Software Reliability Association C Coding Standard(汽车工业软件可靠性联合会制定的C编码标准)的首字母缩写,最早的版本问世于1998,是专门针对汽车工业软件安全性的一种C语言编码规范。
想要进宫,必先自宫,马要拉车,得先阉割。也可以认为,为了提高程序的可靠性和健壮性,Misra C这把手术刀对天马行空的C语言特性进行了无情的阉割。
说到这里,为了避免老表们产生不必要的误解,不得不提上两句。
华为和阿里编程规范的侧重点实则与Misra C有所不同,它们的关注点并不在程序的健壮性这种“里子”,而在于统一代码风格这个“面子”。为的是将程序员代码私产转化为公产,以代码的可读性提高人员更迭对应的可维护性和可重用性。
里子很重要,面子当然也很重要。因为,代码是软件的最终体现形式,是将人的设计思维转换为机器语言的桥梁。它不仅要实现功能,被机器所读懂,更重要的是,依照熟悉的代码风格和良好的可读性,它可以清晰地向阅读它的码农们表达设计思想。
就跟现在上海的同胞既要捅嗓子又要捅鼻子一样,代码也得两头通!
这些代码风格如何,给大家几个例子,自行体会一下吧。
比如说华为规定每行代码不能超过120个字,阿里规定每行代码不能超过80个字,再比如每个函数不能超过80行。。。
这些根据显示器上能够容纳的字符数和行数而针对代码风格进行的规范,正是Misra C所不曾涉及的。
Misra C不重形式,更重实质,它所关注的是代码的安全性、稳定性、鲁棒性。比如说所有switch语句里都要有break,以防止意外发生时找不到处理分支。
再比如说,虽然使用递归能够非常‘优雅’地实现某个功能,但是Misra C规定:一个函数不能直接或者间接地调用自己!
Anyway,华为阿里编程规范和Misra C的终极目的都是为了让程序员写出一手好代码,方向是一致的,道路是不同的。也可以说:
皓月当空,以手指月,华为和阿里在乎的是那只手,而Misra C在乎的是那轮月。
2、Misra C的背景
黑格尔说:凡是存在的,都是合理的。那么,诞生于上个世纪70年代初的C语言,怎么就在上个世纪末的时候,迎来了Misra C这么个婆婆?
或者说,Misra C的合理性在哪儿?这个婆婆,看不上C大姐哪一点了?
C语言身材苗条(结构紧凑)、能说会道(表达能力强)、温顺体贴(灵活性强),在嵌入式码农这个圈圈里,很是招人喜欢。
码农们喜欢换着花样摆弄C语言,有时还会故意使用一些花里胡哨的特性。但是,正如张无忌他妈说过‘越是漂亮的女人越会骗人’一样,越是灵活的特性,越会导致粗心大意的编码人员写出危险的代码。
梅西说:“我不是天生强大,我只是天生要强”。洒家说:“C语言并非天生安全,它只是天生就带着缺陷”。
所以,为了对治C语言先天的固有缺陷,Misra C横空出世,针砭C弊。
金庸老爷子说:万物相生相克,凡是毒物,七步之内必有解****。C语言先天带毒,自然有Misra C来跟它相爱相杀一番。
最初,Misra C只面向汽车行业,针对的是汽车行业的嵌入式开发,后来,从MISRA-C:2004开始,它便扩大了覆盖范围,面向其它高安全性系统了。
毕竟,都是嵌入式,都在搞C代码开发,大哥能比二哥好哪去?所以,Misra C的触角迅速延伸到安全性至关重要的许多其他应用领域,搞起了独乐乐不如众乐乐的大生态。
周公制礼,天下归心。规矩,是让人心安的,Misra C,也可以让嵌入式行业的一众用户心安。
3、Misra C规则原因解读
Misra C的规则干巴巴,看官看着打哈哈。
网上有不少对Misra C规则的解读,无需洒家再置喙。洒家是觉得,规则解读固然重要,但更重要的是,搞清楚Misra C制定这些规则的原因,咱不仅要知其然,更要知其所以然。
3.1 灵活是陷阱
C语言的灵活性是让码农对之爱不释手的原因之一,但是,汝之蜜糖,彼之砒霜。对高手而言,灵活的编程方式和语法规则是他们发挥高超水平的蜜糖,对菜鸟来说,便成了处处挖坑的砒霜。
比如if(val++)和if(++val) 这两种写法里,它可以同时把if语句和val的累加操作结合在一起,对高手而言,能省掉一行代码,确实很灵活,但有时却会违背了菜鸟码农的本意。
因为这里面有两个陷阱。
其一,前种写法是先用当前val进行判断,再让val累加,后一种写法则是先让val累加,再用累加后的val判断。
此外,如果val是个指针,菜鸟又可知,这些累加并非加一,而是加上指针本身的长度呢?
所以,灵活性是一把双刃剑,刀刃向内还是向外全凭本事。
正所谓:二八佳人体似酥,你要是没那金刚杵,她就暗里教君骨髓枯。
3.2 C语言的运行时检查能力较弱
洒家曾经写过一段时间的Java,刚开始写时,便对它能动态检查数组越界赞不绝口,啥时候C代码也能这样该有多好?就拿下面来说,
uint8_t Val[10];
…
Val[idx] = 0;
若是运行时idx =10,Val[idx] = 0这个操作便会直接越界到和Val相邻的变量(假设为Ino)那里。
假设您在别的地方给Ino赋了值,根据其值执行不同的动作逻辑,本来一切岁月静好,结果神不知、鬼不觉,Ino好端端地突变成了0,您还不得直呼见鬼了!
这种bug,来无影去无踪,一下就挖个大深坑。你左看看,右瞧瞧,怎么也想不到是邻居甩的刀!
3.3 电脑的行为同程序员预期的不同
被baby甩掉的黄晓明曾经说过:“我不要你觉得,我要我觉得。。。”
在C代码里,也经常出现程序员以为这样,而电脑却以为那样的情况。
比如说您写了下面三行代码:
uint8_t Pres;
uint16_t Ev_pres;
Ev_pres = (uint16_t)((uint32_t)(Pres * 350) / 255);
您自认并非菜鸟,自以为然地在心中嘀咕着:“菜鸟们应该想不到,这里面的奥妙之处在于Pres * 350的结果可能会超出uint16_t的上限65535,数据会发生截断,所以,把这个结果加个(uint32_t)就万无一失了。”
可是,你以为这样,老板就给你加鸡腿了?加个棒槌!
因为,如果Pres=200,你以为Ev_pres=200*350/255=274,但电脑运行后的结果却是Ev_pres=17。
为什么呢?聪明的读者可能看出来了,问题出在(uint32_t)(Pres * 350)这里,Pres * 350在数据截断之后再(uint32_t)已经没有意义了,应该改成下面这种写法:
Ev_pres = (uint16_t)((uint32_t)Pres * 350 / 255);
这个例子也说明,要想和电脑哥俩好,括号的位置很重要!
总结:
搞电子的都知道,代码写得好,调试的活就能省不少,代码写得糟,调试时踩的坑少不了。
所以,按捺住那颗蠢蠢欲动的心,老老实实地遵循规范标准,就能少埋坑,少踩雷,切切实实地码出质量,码出稳定性来。
筒子们,加油!
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。