"); //-->
据说,在中国的演艺界,有两个圈最讲究规矩了。
一个是小品圈,以东北王为代表,一个头磕到地上,便入了赵家门,得守赵氏家法。
还有一个便是相声界,长幼尊卑更是秩序森严,跟师父闹掰便是叛出师门,在相声界基本上便没有了立足之地。
还有便是军队,更是纪律严明,讲究个战前且莫议论纷纷,开战便要万众一心。因为,主帅有令,将士效命,才能攻无不克,战无不胜。您不信?君不见,杨主簿啃着鸡肋上断头台否?
编程这种“小事”,同样也得守规矩!
1、编程规范的重要性
之前,洒家写过一篇《编程规范哪家强 我把Misra C讲一讲》,大致讲了Misra C诞生的背景及其规则背后的原因。今天,洒家不惜笔墨,苦口婆心,跟大家谈一谈编程规范的重要性。
孔老夫子总结自己一生时曾曰:“吾十有五而志于学,三十而立,四十而不惑,五十而知天命,六十而耳顺,七十而从心所欲,不逾矩。”
西方人秉承Follow your heart,搞得毒品泛滥,小姐满大街。孔老夫子却能做到“从心所欲,不逾矩”,为什么?
因为,当持戒成为一种习惯,不逾矩便成了从心所欲的自然。持守戒律、遵守规矩非但不会处处束缚你,反而让你在道德准则、社会秩序允许的范围内得到了最大程度的自由。那些浅薄的西方人,是把这个逻辑搞反了呀!
风筝是自由的,是因为有根线,让它既能翱翔于九天,又能随时随刻被拉牵。火车是自由的,是因为有一双铁轨,让它既能风驰电掣越千山,又能平平稳稳进车站。代码是自由的,是因为有规范,让它既能把您的所思所想付诸实践,又能避免翻车,给你瞎捣乱。
对于写代码来说,编程规范不会限制自由,它只会限制滋生的混乱。
一个不遵循规范的代码不仅仅虱子多了真犯愁,bug多了直挠头,还会在阅读、修改、维护上会遇到理解上的障碍。
对于刚上手不久的菜鸟级码农来说,遵循规范意味着严格的自律、小心翼翼和偏执,但这能减少bug和隐患。而且,一旦养成了习惯,习惯就变成了自然,这时候,不符合规则的代码一入你的法眼,你就不由自主地要把它改掉,让这些不规矩的代码快滚蛋。
对一个组织而言,遵守统一的编程规范意味着书同文、车同轨,就像月老的红丝线,把大家伙的心儿紧相连,从而大幅提高协作效率,真正做到团队作战。
2、面向可靠性/安全的设计规范
这世上有一个神奇的二八定律,比方说20%的人掌握着世界上80%的财富,20%的人干着80%的活。。。
干过大批量量产电子产品的攻城狮们还发现,20%的时间能干成80%的活,接下来要用80%的时间,把剩下20%的细节好好打磨。
之所以如此,是因为这些产品在上市前需要经过研发团队非常严苛的反复测试。为什么要反复测试?不要问,问就是不懂!难不成,出了问题,你能牛得像特斯拉那样非但拒绝认错,还要教育教育用户?
那么,“严苛”在哪里?“反复测试”些什么呢?除了正常逻辑、常规条件,还需要反复测试Corner case(边界条件/边角案例)!
佛说,万事万物,因缘和合而生,因缘别离而灭。大部分条件下,因缘和合,产品稳定运行,但一旦超过了某个临界点,满足了边界条件,如果处理失当,就可能因缘别离,功能失常了。
何谓边界条件?其定义并不统一,从实际应用来讲,一般是指很少发生的情景、参数异常、噪声或者极端情况。开发产品需要充分考虑边界条件,或施加约束,针对处理,或围住捞净,避免漏网,它会直接关乎到产品的可靠性和功能安全。
但是,产品的可靠性是设计出来的,而非靠测试测出来。在最初的设计阶段就纳入功能安全的理念,才能强本固基,扎下可靠性的底盘。
对于内嵌C代码的电子产品而言,可靠性/功能安全和代码的灵活性却往往是一体两面,王不见王,二龙不相见。C语言的灵活性,可以助力高手鬼斧神工,天外飞仙,也能让菜鸟处处挖坑,埋下风险。
凡事皆是破坏容易建设难,在灵活的语言特性下,要把程序写糟很容易,想写好却很难。如果没有一个严格、安全的设计规范来把关,很容易把代码写烂。
Misra C,正是为了对治C语言的灵活性、提高代码的可靠性横空出世的尚方宝剑。
Misra C的规则,大多来自一线工程师/专家多年的编程实践,提炼于多年软件设计的教训和经验。一时理解不了,也是正常表现,您只需多多编程实战,切不可自以为是,两眼朝天。
毕竟,法律条文都会看,但只有罗老师的粉丝过千万!
3、MISRA-C 规则分类及举例
务虚务实,相得益彰,上点干货,才见真章。下面跟大家分享,Misra C针对C语言的篱笆下的一些桩。
3.1 被误解的语言特性
3.1.1 char只用作字符
毕达哥拉斯学派有一句名言:万物皆数。生于俄罗斯的我国大诗仙李白对此深有体会,因为他曾喃喃自语:白发三千丈,缘愁似个长?也曾大声惊呼:飞流直下三千尺,疑是银河落九天。
诗仙何以不识数?因为,在他的慧眼里,数不是数,是带着诗情画意的字符。在计算机的世界里,也有这样一个独特的数据类型char,你也不要把它当成数,而要当成字符。
首先,这意味着它的取值区间是[-128,127],其次,char型应该只用做“字符”,不要当成“数字”来用。看看下面这个例子:
char i;
i = 131;
if(i > 130){
Fun();
}
会如你所愿地执行Fun()吗?当然,不会!
所以,Misra C规则5.1规定,“单纯的char类型只能用于存储和使用字符值”。
3.1.2自动变量不会自动初始化
先上个例子:
uint8_t Fun(void)
{
uint8_t i;
...
if(0 == i){
Fun2();
}
}
同样,不会执行Fun2()。
很多人会自以为是地想,科技在发展,时代在进步,现在的开发环境这么友好,它肯定会把变量i自动赋值为0的吧?
答案是No! 在C语言里,只有全局变量和静态局部变量会默认赋值为0,非静态的局部变量随机赋值,所以,Misra C规则9.1规定:所有自动变量在使用前都应该被赋值!
3.2 增强程序的清晰
3.2.1 定义清晰的数据类型
应该使用指示了大小和符号的typedef 以代替基本数据类型,洒家在16位单片机上定义的常用类型如下:
typedef signed char sint8_t;
typedef unsigned char uint8_t;
typedef signed int sint16_t;
typedef unsigned int uint16_t;
typedef float float32_t;
typedef unsigned long uint32_t;
3.2.2 不要使用逗号运算符
比如下面这段代码占了5行:
if(TRUE == Val){
motor->pointer = 0;
motor->pointer_old = 0;
motor->cmd_dir = CLKWISE;
}
有的偏执狂为了精简行数,改成下面这样的两行:
if(TRUE == Val)
motor->pointer = 0, motor->pointer_old = 0, motor->cmd_dir = CLKWISE;
好多人get不到这种方式的缺点。想想一个场景,如果这是你从别人手中接手的代码,看着怪不怪?是不是怀疑哪个逗号搞错了,应该改成分号来着?
这背后,是律己从宽、料敌从严的人性啊!
3.3杜绝奇技淫巧-笨拙则鲁棒
对工程师来说,天才的设计是真正的浪漫。在自己得意的作品中留下一些小心思,是码农们对庸常生活的一种反抗。
于是,各种花哨的小动作就出现在了本该严谨的编码实践中。举个例子:
uint8_t *** ptr;
指向xx的指针的指针的指针,多烧脑,多“美妙”。
可是,君子藏器于身,以钝示人,以锋策己。搞得这么花里胡哨,真以为过了一段时间自己能记牢?
3.4 不要依赖编译器
不要过分依赖C表达式中的运算符优先规则
比如下面这个程序:
uint8_t ial,iah;
uint16_t ia;
ia = ial + iah << 8;
初衷很简单,将两个八位数据连接成一个16位数据。上面的错误在哪里?‘+’的优先级大于‘<<’,所以应该改成下面这种形式:
ia = ial + (iah << 8);
不知不觉,文章就变得又臭又长了。收工!
文:三少爷
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。