新闻  |   论坛  |   博客  |   在线研讨会
编程规范哪家强 Misra C姊妹篇再上场
三德子 | 2022-05-23 09:39:11    阅读:6873   发布文章

据说,在中国的演艺界,有两个圈最讲究规矩了。 

一个是小品圈,以东北王为代表,一个头磕到地上,便入了赵家门,得守赵氏家法。 

还有一个便是相声界,长幼尊卑更是秩序森严,跟师父闹掰便是叛出师门,在相声界基本上便没有了立足之地。 

还有便是军队,更是纪律严明,讲究个战前且莫议论纷纷,开战便要万众一心。因为,主帅有令,将士效命,才能攻无不克,战无不胜。您不信?君不见,杨主簿啃着鸡肋上断头台否? 

杨修.jpg

编程这种“小事”,同样也得守规矩!

1、编程规范的重要性

之前,洒家写过一篇《编程规范哪家强 我把Misra C讲一讲》,大致讲了Misra C诞生的背景及其规则背后的原因。今天,洒家不惜笔墨,苦口婆心,跟大家谈一谈编程规范的重要性。 

孔老夫子总结自己一生时曾曰:吾十有五而志于学,三十而立,四十而不惑,五十而知天命,六十而耳顺,七十而从心所欲,不逾矩。 

吾十有五而志于学.jpg

西方人秉承Follow your heart,搞得毒品泛滥,小姐满大街。孔老夫子却能做到“从心所欲,不逾矩”,为什么? 

因为,当持戒成为一种习惯,不逾矩便成了从心所欲的自然。持守戒律、遵守规矩非但不会处处束缚你,反而让你在道德准则社会秩序允许的范围内得到最大程度的自由。那些浅薄的西方人,是把这个逻辑搞反了呀! 

风筝是自由的,是因为有根线,让它既能翱翔于九天,又能随时随刻被拉牵。火车是自由的,是因为有一双铁轨,让它既能风驰电掣越千山,又能平平稳稳进车站。代码是自由的,是因为有规范,让它既能把您的所思所想付诸实践,又能避免翻车,给你瞎捣乱。 

对于写代码来说,编程规范不会限制自由,它只会限制滋生混乱 

一个不遵循规范的代码不仅仅虱子多了真犯愁,bug多了直挠头,还会在阅读、修改、维护上会遇到理解上的障碍 

对于刚上手不久的菜鸟级码农来说,遵循规范意味着严格的自律、小心翼翼和偏执,但这能减少bug和隐患而且,一旦养成了习惯,习惯就变成了自然,这时候,不符合规则的代码一入你的法眼,你就不由自主地要把它改掉,让这些不规矩的代码快滚蛋。 

对一个组织而言,遵守统一的编程规范意味着书同文、车同轨,就像月老的红丝线,把大家伙的心儿紧相连,从而大幅提高协作效率,真正做到团队作战。

2、面向可靠性/安全的设计规范

这世上有一个神奇的二八定律,比方说20%的人掌握着世界上80%的财富,20%的人干着80%的活。。。 

干过大批量量产电子产品的攻城狮们还发现,20%的时间能干成80%的活,接下来要用80%的时间,把剩下20%的细节好好打磨。 

之所以如此,是因为这些产品在上市前需要经过研发团队非常严苛的反复测试。为什么要反复测试?不要问,问就是不懂!难不成,出了问题,你能牛得像特斯拉那样非但拒绝认错,还要教育教育用户? 

那么,“严苛”在哪里?“反复测试”些什么呢?除了正常逻辑、常规条件,还需要反复测试Corner case(边界条件/边角案例)! 

佛说,万事万物,因缘和合而生,因缘别离而灭。大部分条件下,因缘和合,产品稳定运行,但一旦超过了某个临界点,满足了边界条件,如果处理失当,就可能因缘别离,功能失常了。 

何谓边界条件?其定义并不统一,从实际应用来讲,一般是指很少发生的情景、参数异常、噪声或者极端情况。开发产品需要充分考虑边界条件,或施加约束,针对处理,或围住捞净,避免漏网,它会直接关乎到产品的可靠性和功能安全。 

但是,产品的可靠性是设计出来的,而非靠测试测出来。在最初的设计阶段就纳入功能安全的理念,才能强本固基,扎下可靠性的底盘。 

对于内嵌C代码的电子产品而言,可靠性/功能安全和代码的灵活性却往往是一体两面,王不见王,二龙不相见C语言的灵活性,可以助力高手鬼斧神工,天外飞仙,也能让菜鸟处处挖坑,埋下风险。 

凡事皆是破坏容易建设难,在灵活的语言特性下,要把程序写糟很容易,写好很难如果没有一个严格、安全的设计规范来把关,很容易把代码写烂 

Misra C,正是为了对治C语言的灵活性、提高代码的可靠性横空出世的尚方宝剑。 

Misra C规则大多来自一线工程师/专家多年的编程实践提炼于多年软件设计的教训经验一时理解不了,也是正常表现,您只需多多编程实战,切不可自以为是,两眼朝天。 

毕竟,法律条文都会看,但只有罗老师的粉丝过千万!

罗翔.jpg

3MISRA-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不到这种方式的缺点。想想一个场景,如果这是你从别人手中接手的代码,看着怪不怪?是不是怀疑哪个逗号搞错了,应该改成分号来着?

程序员恶趣味.jpg 

这背后,是律己从宽、料敌从严的人性啊! 

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); 

 

不知不觉,文章就变得又臭又长了。收工!

 

文:三少爷

 

 


*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
推荐文章
最近访客