Misra-C编码规范

前言

为了解决嵌入式 C 语言开发中常见的安全性和可靠性问题,汽车产业软件可靠性协会 (MISRA) 发布了 MISRA C 编码标准。

MISRA C (Motor Industry Software Reliability Association C) 是汽车工业 C 编码标准的缩写,由 MISRA 协会发布。其目标是为嵌入式系统中的 C 语言开发提供一套严格的编码规范,旨在:

  • 提升代码可靠性 (Reliability): 减少因编码错误导致的程序缺陷,提高系统运行的稳定性。
  • 提升代码可读性 (Readability): 统一代码风格,使代码更易于理解和维护,降低维护成本。
  • 提升代码可移植性 (Portability): 减少对特定编译器或硬件平台的依赖,增强代码在不同环境下的适应性。
  • 提升代码可维护性 (Maintainability): 规范的代码结构和风格,降低代码维护和升级的难度。
  • 提升代码安全性 (Safety): 避免潜在的安全漏洞,保障系统运行的安全。

一、指令 (Directives)

Dir1.1 实现定义行为文档化

1
2
Any implementation-defined behavior on which the output of the program depends shall be documented and understood
应记录并理解程序输出所依赖的任何执行定义行为

要知道程序这么运作和输出是有意的,而不是偶然产生了。就是要让所有的程序动作都能被程序员所理解,让我们的箱子白化。对嵌入式程序而言,这点尤为重要。

  • 要求: 应该用文档记录并了解程序输出依赖的任何实现定义行为。

  • 解释: C 语言标准中有一些行为是“实现定义”的 (implementation-defined),这意味着这些行为的具体实现由编译器或平台决定。不同的编译器或平台可能会对这些行为有不同的实现,导致代码的行为在不同环境下不一致。为了确保代码的可移植性和可预测性,应该将程序依赖的任何实现定义行为记录在文档中,并确保开发团队充分理解这些行为。

  • 示例:

    • 整数类型的大小 (int, long 等) 在不同平台上可能是不同的。
    • 有符号整数的右移操作可能是算术右移 (保留符号位) 或逻辑右移 (不保留符号位)。
    • 结构体成员的对齐方式可能因编译器和平台而异。

Dir 2.1 零编译错误:

Dir2.1 所有的源文件不应该有任何编译错误

  • 要求: 所有源文件必须没有任何编译错误。
  • 解释: 这是代码质量最基本的要求。存在编译错误的代码无法生成可执行程序,更谈不上功能和性能。在开发过程中,应该及时修复所有编译错误,确保代码始终处于可编译状态。

Dir 3.1 需求可追溯性

Dir3.1 所有的代码都应该可追溯到需求文档

代码需要按照需求开发,并且代码需要与需求文档一一匹配。如果有代码不在需求文档中,那么要么改代码,要么改需求文档。

  • 要求: 所有代码应该可以追溯至文件化的需求。
  • 解释: 可追溯性是指代码和需求之间建立清晰的对应关系。良好的可追溯性可以帮助开发人员理解代码的设计意图,验证代码是否满足需求,并在需求变更时快速定位受影响的代码。
  • 示例:
    • 可以在代码注释中注明该代码段对应的需求编号或需求描述。
    • 可以使用需求管理工具来维护代码和需求之间的映射关系。

Dir 4.1 运行时故障最小化

  • 要求: 运行时故障必须最小化。
  • 解释: 运行时故障是指程序在运行过程中发生的错误,例如空指针解引用、数组越界、除零错误等。这些故障会导致程序崩溃或产生不可预测的结果。在代码设计阶段就应考虑各种可能的错误情况,并采取措施预防运行时故障的发生,例如进行输入验证、边界检查、错误处理等

Dir 4.2 所有汇编语言的使用应当用文档记录

  • 要求: 建议所有汇编的使用应当用文档记录。
  • 解释: 虽然在某些情况下, 为了提高性能或访问底层硬件, 可能需要使用汇编语言。 但是, 汇编语言的可读性, 可维护性和可移植性都比较差。 因此,MISRA C建议详细记录汇编代码的用途, 功能和接口, 以便其他开发人员理解和维护。

Dir 4.3 汇编语言必须封装、隔离

  • 要求: 汇编语言必须封装、隔离。
  • 解释: 为了减少汇编语言对代码可维护性和可移植性的影响, 应该将汇编代码封装在独立的函数或模块中, 并与 C 代码隔离。 这样可以限制汇编代码的作用范围, 降低代码的复杂性, 并方便后续的维护和移植。

Dir 4.4 建议代码部分不应当注释掉

  • 要求: 建议代码部分不应当注释掉。

  • 解释:

    • 不合规代码示例:

      1
      2
      3
      /*
      x = y + z; // 这段代码暂时不需要
      */
    • 合规代码示例:

      1
      2
      3
      #if 0
      x = y + z; // 使用预处理指令
      #endif

    解释:不应该用 // ... 或者 /* ... */

    注释代码。而应该用#ifdef ...#endif等预编译指令。

二、Misra-C 规则