【问题标题】:Which is an alternative to nested if's in C safe coding? [duplicate]哪个是 C 安全编码中嵌套 if 的替代方案? [复制]
【发布时间】:2020-08-10 19:37:33
【问题描述】:

例如:

#define SUCCESS 1u
status_t status;

/* Initialize a peripheral */
status = start_Timer();

if(status == SUCCESS)
{
    /* Proceed */
    status = another_initialization();

    if(status == SUCCESS)
    {
        /* Proceed further */
    }

}

这会在几个连续的过程中产生很多缩进,从而留下 对于程序中的实际算法,线宽非常小。 C语言中的异常处理有什么替代方法吗?

【问题讨论】:

    标签: c


    【解决方案1】:

    灵感来自 CrisBD 答案,但在执行过程中没有返回(MISRA 不推荐)。

    #define SUCCESS 1u
    status_t status;
    
    /* Initialize a peripheral */
    status = start_Timer();
    
    if(status == SUCCESS)
    {
       /* Proceed */
       status = another_initialization();
    }
    
    if(status == SUCCESS)
    {
        /* Proceed further */
    }
    

    【讨论】:

    • 随着案例数量的增加,这变得相当混乱。最重要的是,关键任务系统的理念是在遇到错误时停止执行。不太重要的是,我认为这个版本也可能会给出更差的分支预测,因为你会得到许多可能毫无意义的检查。
    • 就我个人而言,在扩大规模时,我发现它与其他解决方案一样混乱。分支预测可能是一个问题,具体取决于 MCU。但最终编译器会像第一个嵌套示例一样优化代码。尝试一下会很有趣。
    【解决方案2】:

    我发现最好反转测试逻辑,而不是测试继续代码流,测试停止。

    #define SUCCESS 1u
    status_t status;
    
    /* Initialize a peripheral */
    status = start_Timer();
    
    if(status != SUCCESS)
    {
       return ;
    }
    /* Proceed */
    status = another_initialization();
    
    if(status != SUCCESS)
    {
        return;
    }
    /* Proceed further */
    

    【讨论】:

    • 我同意这可能是处理典型情况的合适方法,以运行一系列子功能,随时准备中止任何(严重)错误。但是,该模式会导致多个 return 语句,这违反了 MISRA 规则。我曾在不同的符合 MISRA 的开发团队中工作过,我注意到对于像现在这样的案例放宽该规则的意见非常不同。
    • 没错,一如既往,尽管它是有时难以理解的代码和可以遵循的东西之间的权衡。通常,尽管我会查看类似于初始化例程的东西来遵循某种状态机,以便它返回到已知状态。
    • 如果此代码所在的函数编写正确,您最好应该return status;。如果您这样做,那么我会说这是编写此类代码的正确方法。
    • 是的。这是我通常做的事情,尤其是在嵌入式系统中,只是没有在上面发布。
    【解决方案3】:

    对于问题中提供的简单代码示例,我看不到嵌套问题(有或没有大括号和缩进)。但我猜想代码 sn-p 已被选为 minimal reproducible example 来演示可能会因多个块嵌套而变得严重的问题。

    对于更复杂的例子,我想指出限制函数复杂度(通常是圈复杂度)的重要建议。 据我所知,MISRA 本身并没有规定任何复杂性度量的硬性限制。不过,最好将 McCabe 圈复杂度限制在 10-20 左右。

    这将可嵌套的ifs、fors、switches 等的最大数量限制为一个可以处理的少量数字,即使在任何地方都有相当大的缩进宽度和大括号。

    【讨论】:

    • 是的,所以通过遵循非科学的 hogwash MISRA/IEC 61508 单一返回语句规则(取自 1979 年编写的恐龙编程书籍),我们使代码 less 可读性并从根本上增加圈复杂度。换句话说,为了符合 MISRA/IEC 61508,使代码更危险。在某些情况下,多次返回是不好的,但不是在编写协议解码器时进行大量错误检查的情况, 解析器等,并且需要在多个地方返回错误。
    • @Lundin - 这有点太快了。首先,MISRA 和 IEC 61508 之间没有等式。只是 IEC 61508(及其子标准)要求使用 C 和 C++ 等编程语言进行软件开发,以将自己限制在可以是 MISRA 的语言子集。现在,MISRA 拥有并非全部强制的指令和规则,但有些只是必需。在 MISRA 术语中,这意味着组织单位可以决定应用或放宽这些规则(如 no-multi-return 规则)。即,您甚至可以决定跳过该规则而无需破坏 MISRA。
    • MISRA-C:2004 规则 14.7 (req) 和 MISRA-C:2012 规则 15.5 (adv) 列出了规则的以下来源:“[IEC 61508-3 表 B.9],[ ISO 26262-6 表 8]"。 MISRA 2012 中给出的规则的基本原理是“IEC 61508 和 ISO 26262 要求单点出口作为模块化方法要求的一部分。”如果您随后深入 IEC 61508 以了解该规则的基本原理,他们将这本书命名为恐龙书,如果没记错的话,这本书是 Edward Yourdon,1979 年的软件工程经典。毫无疑问,它教的是什么早在 1979 年就被认为是经典……
    • 是的,解决方案是永久偏离规则。但是您仍然必须反对 IEC 61508 “一个入口/出口点”,这是从 SIL 1 到 4 强烈推荐的。这正是标准强制工程师在安全关键系统中引入危险的方式,在标准实施之前不存在危险他们。
    【解决方案4】:

    除非该函数分配了在发生故障时必须清理的任何资源,否则我只会在出现错误时退出该函数。这是违反 MISRA 的,但您可以很容易地证明它是正当的。这背后的动机是防止资源泄漏并阻止变得不可读的复杂控制流。尽早使用明显的错误代码中断该功能既简单又干净,不这样做(在正式的基础上)就像在酒吧争吵中自欺欺人。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-02
      • 1970-01-01
      • 2015-07-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多