MISRA C和MISRA C++应该怎么选,MISRA规则集在选型的时候先要去看哪些因素,这个问题不能只由团队对哪一种语言更熟悉来决定,在嵌入式工程里,一旦把规则集选错了,后面就会影响到代码的检查、问题的整改、供应商的交付以及工程的评审。MISRA C主要面向C语言的代码,它适合大量用到MCU、底层驱动、控制算法、通信协议栈的工程;MISRA C++则面向C++的代码,适合那些已经用上了类、对象、模板、构造与析构、命名空间等C++特性的工程,开发团队先把工程的主要语言看清楚,再去看软件风险、平台资源、工具支持,还有供应商代码的来源,这样选出来的规则集就不会偏得太远。
一、MISRA C和MISRA C++怎么选
在挑选MISRA C和MISRA C++的时候,先要回到工程代码本身上面去看,如果工程里大部分的代码都是C语言,团队就不应该为了让规则显得更“完整”,而去硬套MISRA C++;如果工程里已经大量使用了C++的特性,也不能只用MISRA C简单地查一遍就完事,因为很多C++特有的风险,是没有被MISRA C覆盖到的。
1、工程的主要语言是C语言时,要优先选MISRA C
很多嵌入式工程到现在还是以C语言为主,底层驱动、硬件寄存器读写、中断处理、通信协议、控制算法、实时任务这些工作,常常都是用C语言来完成的;如果工程里的代码主要由.c和.h文件构成,那么团队就应该优先去考虑MISRA C。MISRA C更加适合去处理C语言里面的常见毛病,比如类型转换写得含糊、指针用得太随意、宏定义堆得太多、数组的边界不清楚、表达式带有明显的副作用,这些问题在嵌入式C代码中经常出现;团队拿MISRA C去检查这些内容,规则和实际代码的场景会更加贴合。
2、工程的主要语言是C++时,要优先选MISRA C++
有些嵌入式工程已经开始使用C++,这类工程会用类来组织各个模块,用构造函数和析构函数去管理对象,也可能用到模板、继承、虚函数、命名空间这些语言特性;如果工程已经走到了这一步,团队就应该把MISRA C++放到优先考虑的位置。C++代码里面的问题,并不仅仅是C语言问题的延续,像对象的生命周期、构造的次序、隐式类型转换、重载函数、继承关系、动态内存分配、异常处理,这些内容都需要专门去查一查;假如团队只拿MISRA C去看C++工程,就容易漏掉这一些风险。
3、C和C++混合在一起的工程,要分层去处理
有的工程既不是纯粹的C,也不是纯粹的C++,它的底层驱动可能是用C写的,应用层框架可能是用C++搭的,第三方库又有可能把两种语言混在一起;团队碰到这种情形时,不能简单地选一套规则去覆盖全部代码。更合适的做法,是按照模块和语言分层次来处理,C语言模块照着MISRA C去检查,C++模块照着MISRA C++去检查;在接口的部分,要重点去看数据类型、调用关系、内存的归属和错误处理的方式,这样做虽然前期整理会多花一些时间,但到了后期,问题会更容易被定位。
4、不要因为迁移计划而提前把规则选错
有些团队在计划以后从C语言迁移到C++,于是就想先把MISRA C++放到工程里去,这种想法可以理解,但在执行的时候,还是要看代码眼下的情况;如果工程当前仍然以C语言为主,团队就直接套用MISRA C++,其实是不太合适的。团队可以先拿MISRA C把现有的C代码管住,等到新模块真正开始用上C++之后,再针对C++模块把MISRA C++的要求加上去;规则要跟着代码的实际状况走,不能只跟着技术上的规划跑。
二、MISRA规则集选型先看哪些因素
MISRA规则集在选型时,要先看哪些因素呢,团队不能只盯着语言的名称,工程的风险、运行平台、工具的能力、供应商的交付方式、客户的评审要求,这些都会对最终的选择产生影响;规则集选型的目的,并不是要把标准的名称写进文档里面去,而是要让代码检查真的能够落到工程的开发里头。
1、先看工程的风险等级
团队先要去判断软件出错之后会影响到哪些方面,假如代码的毛病只影响普通的显示,那工程对规则集的要求可能就不会太重;可要是代码的问题会波及到设备的动作、人员的安全、现场的生产或者客户的验收,团队就需要认认真真地去选择MISRA规则集。汽车电子、工业控制、医疗设备、轨道交通、航空航天配套设备等工程,通常对代码的稳定性有更高的要求,这一类工程不适合只靠开发人员个人的习惯去写代码;团队需要靠规则把危险的写法提前限制住,同时也要让后面的评审有一个检查的根据。
2、再看平台的资源和运行的平台环境
嵌入式工程的硬件资源差别很大,有的工程跑在小型MCU上,内存和算力都比较吃紧;有的工程则跑在性能更高的处理器上面,可以用上更复杂的软件架构,平台资源不一样,语言的选择和规则集的选择也会跟着不同。资源紧张、对实时性要求很强、底层控制又比较多的工程,通常会更加偏向C语言和MISRA C;平台资源相对宽松、模块结构比较复杂、应用层逻辑也较多的工程,就可能会用上C++和MISRA C++;团队要看工程实际运行在什么环境里,不要只盯着语言本身去看。
3、要看检查工具是不是支持
MISRA规则并不是写进计划里面就算完事了,团队还要用代码自动检查工具去扫描代码,要是工具对某一套规则支持得不好,后面就会出现大量的人工判断,执行的代价就会变高。团队在选型之前,应该先确认工具能不能识别目标语言,能不能覆盖主要规则,能不能把检查报告导出来,能不能管理问题的状态;工具给出的报告还要适合评审来用,到了工程后期,如果需要向客户或者第三方去说明检查的结果,工具的能力会直接影响到交付的效率。
4、要看供应商代码的来源
嵌入式工程常常会引入第三方的代码,芯片厂商会提供驱动,模块供应商会交付接口的代码,外包团队也有可能交付一部分业务代码,不同来源的代码,质量不一样,语言风格也不一样。团队在选择MISRA规则集时,要先看供应商交付的代码是用什么语言写的,如果供应商交过来的是C语言的代码,团队就要把MISRA C的检查要求明确下来;如果交过来的是C++代码,就要把MISRA C++的检查要求说清楚;对于历史代码和第三方库,团队也要提前说明是去整改、还是隔离起来,又或者只做风险记录。
5、要看客户和评审的要求
有一些工程在选规则集的时候,并不是团队内部把决定做了就可以收尾的,客户可能会指定要用什么编码规范,第三方的评审也可能会要求提交检查的报告;团队在工程的早期,就要把这些要求一项一项问清楚。如果客户明确要的是MISRA C,团队就不能只交一份普通的C语言检查结果;如果客户要求C++模块按照MISRA C++来检查,团队也不能拿MISRA C报告去替换。规则集的选型要和合同、开发计划、评审资料保持住一致,后期才不会出现返工的情况。
三、MISRA规则集怎样放进工程流程里
把MISRA规则集选好之后,团队还要想一想怎么去执行,很多工程并不是选错了规则,而是仅仅在文档里面写了规则,开发的过程里并没有真正地去用它;这样等到快要交付之前才集中扫描,问题就会堆在一起,开发人员也很难一次就把它们处理干净。
1、工程刚开始时就要把适用范围写清楚
团队要在工程刚启动的阶段,就把规则的适用范围写明白,哪些模块用MISRA C,哪些模块用MISRA C++,哪些代码属于第三方代码,哪些又属于自动生成的代码,这些内容全部要提前说明;范围清楚以后,后面的执行就会顺当很多,工具扫描出来的问题,团队也能够判断哪些是必须修改的,哪些是需要用隔离说明来处理的,哪些是可以当成已知风险先记录下来的。
2、开发过程里要定期去做检查
团队不能等到代码全部写完以后才去查MISRA的问题,开发人员每写完一个模块,就可以先做一次基础的检查;团队在把代码集成起来之前,可以再做一次完整的检查;工程发布之前,再去确认一下主要的问题是不是都已经被关掉了。这样做可以把问题分散在平时去处理,开发人员改起来的压力会小一些,工程负责人也能更早地看到代码质量的变化;如果检查只放在交付前,报告里的问题就会一下子冒出来,工程的节奏很容易就被打乱了。
3、特殊处理的情况要把原因写清楚
在嵌入式工程里,有些代码确实很难完全躲开特殊的写法,像硬件寄存器访问、底层地址映射、中断处理、编译器扩展、启动代码这些内容,都有可能触发MISRA的规则;团队碰到这一类情况的时候,不能简单地把问题忽略掉。开发人员要写清楚为什么必须这样写,影响的范围在哪里,有没有做一些隔离处理,测试是怎么去覆盖的,后期又由谁来维护;例外说明并不是为了应付文档,而是要让评审人员知道,这段代码为什么可以被保留下来。
4、规则的执行要和评审资料对应上
MISRA检查得到的结果,要能和工程的资料对应起来,开发计划里面写了哪一套规则,工具的报告里面就应该能看见对应的规则;代码评审记录里提到的问题,也要能和整改的记录连在一起。团队照着这样去做了以后,后期的评审就会轻松很多,评审人员不单单是看代码有没有毛病,也会看团队是不是一直在做检查,有没有把违规项处理好,有没有对保留项做出说明;规则的执行要是能形成一个闭环,MISRA的选型才算是真正落了地。
总结
MISRA C和MISRA C++应该怎么选,MISRA规则集的选型先看哪些因素,工程团队先要去看代码的主要语言,再去看工程风险、平台资源、工具支持、供应商代码以及客户的评审要求;C语言工程优先去考虑MISRA C,C++工程优先去考虑MISRA C++,混合在一起的工程则要按照模块分层次去处理。规则集不是选完了就算结束,团队还要把检查的范围、工具的扫描、例外的说明和评审的资料串在一起,这样一来,MISRA规则集才不会只停留在文档里面,代码的质量到了工程后期也更容易被讲清楚。