我是否可以选择禁用这两个 MISRA 规则:每个函数一个语句和强制函数原型?

Am I allowed to choose to disable these two MISRA rules: one statement per function and mandatory function prototypes?

我们公司现在是 ISO-13485(医疗设备)并希望使用 MISRAC2012。我阅读了标准,但我不知道是否允许我禁用某些规则,如果我认为它可以提高稳定性和可读性的话。

两个例子:

MISRA 只允许每个函数有 1 个 return 语句。这通常会导致嵌套的条件结构看起来像圣诞树。我真的不认为这条规则会增加安全性,因为它会降低代码的可读性和更容易出错。

MISRA 只接受有原型的函数,即使是静态函数。这允许程序员将他的函数放在文件中的任何位置,而无需考虑调用顺序。没有原型,主函数必须是文件中的最新函数,并且多功能递归是不可能的,因为函数只能调用上面声明的函数。

如果我想禁用这两个规则,可以吗?有客户会因此责怪我吗?

您可以遵循偏差过程,该过程的一部分是由合格的技术人员审查偏差。偏差可以涵盖不合规的个别情况,也可以是整个项目。

那么,您如何证明您所要求的项目范围内的偏差是合理的?

我最多会争辩说,每个违规行为都应该被审查。在某些情况下,您的推理,例如,深度嵌套可以是 unreadable/unmaintainable,必须权衡为什么它们首先如此深?换句话说,也许这个功能做的太多了,需要分解成更小的模块。
我假设您使用的是静态分析工具,该工具足够智能,可以提供一种方法,在偏差获得批准后不再继续报告违规行为。

MISRA-C:2012 年有 3 个类别,所有指令和规则都归入以下类别:

  • 必填项。您必须遵守这些规定,不得有任何偏差。
  • 必填。你必须遵守这些规则,但如果你提出正式的偏离规则,你可以违反它们。你需要一个很好的理由。
  • 咨询。建议遵循这些,但您可以在不引发正式偏差的情况下打破它们(尽管提出偏差是推荐的做法)。

偏差背后的想法是您的公司应该有处理它们的例程,例如内部质量差事或在代码审查会议期间提出的事情等。这个想法是除了您自己之外的其他人必须参与在创建偏差的过程中,最好是具有丰富 C 知识的人。 MISRA-C 5.4 中对此进行了描述,还有一个名为 MISRA Compliance:2016 的附加指南文档可能会有帮助。

关于如何实施偏差,我个人的建议是在 case-by-case 的基础上完全不允许它们。相反,应该为公司建立一个单独的编码标准文件——无论如何你都需要某种方式的文件来声明 MISRA 合规性。该文档应包含所有 company-wide 偏差的列表。如果需要偏离,必须更新 company-wide 文档。这实际上可以让您免于实施大量官僚程序,也可以让您免于各种经验不足的程序员提出奇怪的想法,只是因为他们不理解规则的 MISRA-C 基本原理。


至于每个函数一个 return 语句,在我看来是 known defect in MISRA-C inherited from IEC 61508(我想我是唯一一个真正费心研究需求来源的人)。你应该对规则提出永久性的偏差,因为它是无稽之谈。我个人将要求改写为 "functions should not have more than one return statement, unless several return statements leads to more readable code"。这涵盖了规则的真正意图,即避免意大利面条式编程。


MISRA only accept functions that have a prototype, even for static ones. This allows the programmer to place his functions anywhere in the file without respect of the calling order. Without prototype the main function has to be the latest function in the file and multi-function recursion is not possible because a function can only call the one declared above itself.

我认为这没有任何意义,您似乎在尝试解决一个不存在的问题。您应该通过 1) 实际上知道您在做什么以及 2) 按照 MISRA 的要求使用静态分析工具来避免意外递归。

如果您希望调用堆栈为 func1() -> func2() -> func3() 并阻止 func2() 或 func3() 调用 func1(),最好通过适当的程序设计来解决。给函数起直观的名字并使用常识会让你走得更远。

如果这还不够,那么您可以将翻译单元一分为二,并为内部构件创建一个单独的 h/c 文件对。如果您有很长的源文件,其中包含很多功能,那么您描述的风险主要是一个问题,以至于程序员无法跟踪它们。这也很好地表明文件(and/or 函数)应该分成几个。

至于这个 MISRA 规则背后的基本原理,它是一个非常合理的规则,即阻止旧的 C90 垃圾从 "inventing" 调用约定(隐式 int return 类型,组成参数等), 只是因为编译器找不到函数原型。你绝对不应该偏离这个规则。

规则 15.5(一个函数应该在末尾有一个出口点)是一个咨询规则。因此,如果您的内部流程如此记录,您可以不应用此规则。

规则 8.2(函数应采用原型形式)是 必需的 规则,出于(恕我直言)合理的原因 - 因为它确保您显式定义函数 return 类型以及所有参数的数量和类型,从而避免未定义的行为。

如果您愿意,可以偏离准则,但这需要您证明该决定的合理性!