MISRA C 2012规则15.4并将后藤替换为break

93ze6v8z  于 2023-08-03  发布在  其他
关注(0)|答案(7)|浏览(108)

关于MISRA C 2012规则15.4 -“用于终止任何迭代语句的break或后藤语句不应超过一个。”-此示例正确吗?有人能用一些工具(MISRA检查器)来确认这一点吗?

do {
    retval = do_smth();
    if (retval != OK) {
        break;
    }

    retval = do_smth2();
    if (retval != OK) {
        break;
    }

    retval = do_smth3();
} while (0u);

字符串
这只是一个概念,但我在这里尝试的是用break的cascade替换goto的cascade(不幸的是在这种情况下被禁止)。我的观点是do { } while(0u);不是一个迭代语句。
你觉得呢?

vecaoik1

vecaoik11#

首先,您的代码确实没有遵循规则15.4,因为您在迭代语句中有3个break。但这只是一个建议,只要代码可读且易于理解,像您这样使用多个break并没有什么错。
这些MISRA规则的主要理由是防止“复合语句意大利面条”,其中复杂的代码从多个嵌套的复合语句中分离出来。在盲目地遵循这些规则之前,理解其基本原理是很重要的。因此,在这种情况下,只需考虑保持代码不变-咨询规则不需要偏离。
否则,有几个选项,如下所示:
MISRA-C的一个问题是,它不允许从一个函数中多次返回,即使它使代码更具可读性。否则,最明显和最易读的解决方案将是使用函数:

type do_stuff (void);
{
  type retval;

  retval = do_smth();
  if (retval != OK) { return retval; }

  retval = do_smth2();
  if (retval != OK) { return retval; }

  retval = do_smth3();

  return retval;
}

字符串
我通常的解决方案是使MISRA-C永久偏离多重返回规则,并允许它在使代码更具可读性的情况下使用,就像在这种情况下一样。
否则,第二个最好的选择可能是旧的“on error后藤”-班宁goto的规则在MISRA-C:2012中被放宽,所以现在只是建议。

retval = do_smth();
  if (retval != OK) { goto error; }

  retval = do_smth2();
  if (retval != OK) { goto error; }

  retval = do_smth3();
  if (retval != OK) { goto error; }

  goto everything_ok;

  error:
    /* error handling */

  everything_ok:


如果以上两种形式都不合适,因为你对MISRA-C非常严格,那么第三种选择可能是这样的,我相信它100%符合MISRA-C:

typedef type do_stuff_t (void);

do_stuff_t* const do_stuff[N] = { do_smth, do_smth2, do_smth3 };
type retval = OK;

for(uint32_t i=0u; (i<N) && (retval==OK); i++)
{
  retval = do_stuff[i]();
}


我的观点是做{ } while(0 u);不是迭代语句。
C语言不同意你的观点。
1)C17:
6.8.5迭代语句
语法

  • iteration-statement:*

while( * 表达式 * ) * 语句 *
do * 语句 * while( * 表达式 * ) ;

oxiaedzo

oxiaedzo2#

我会把你的代码替换成这样:

retval = do_smth();
  if (retval == OK) {
    retval = do_smth2();
  } 
  if (retval == OK) {
    retval = do_smth3();
  }

字符串

  • 没有虚假的同时
  • 没有gotos伪装成break
  • 因此甚至没有一个后藤/break
  • 因此不再有MISRA问题
  • 奖励:行数是原始代码的一半

顺便说一句:最后一次突破(break; // <- 3rd)无论如何都是无用的

7hiiyaii

7hiiyaii3#

用于终止任何迭代语句的break或后藤语句不应超过一个。
你的例子在一个do-while(迭代语句)中有3个break,所以我认为它是不正确的。break是一个控制流/循环控制语句,仅在循环上下文中有效。我认为你的论点在这里站不住脚,尽管我明白你的意思。
TL;DR:do-while仍然是一个迭代语句,尽管它只运行一次。

do {
    retval = do_smth();
    if (retval != OK) {
        break; // <- 1st
    }

    retval = do_smth2();
    if (retval != OK) {
        break;  // <- 2nd
    }
    retval = do_smth3();
    if (retval != OK) {
        break; // <- 3rd
    }
} while (0u);

字符串

ix0qys7i

ix0qys7i4#

我不知道这个代码是否足够好。不过,这很管用。

switch(0) {
default:
    retval = do_smth();
    if (retval != OK) {
        break;
    }

    retval = do_smth2();
    if (retval != OK) {
        break;
    }

    retval = do_smth3();
    break;
}

字符串

vjrehmav

vjrehmav5#

我不是MISRA方面的Maven,所以这可能是因为其他原因而被禁止的,但在这种情况下,我可能会使用这样的东西:

if((retval = do_smth()) != OK)
    ;
else if((retval = do_smth2()) != OK)
    ;
else if((retval = do_smth3()) != OK)
    ;

字符串
goto、无break、无迭代、无重复测试。
空语句看起来很奇怪,这是真的,尽管在实践中,经常会有一些错误日志代码放在那里。如果不喜欢裸分号,可以使用{ }{ /* do nothing */ }
条件语句中的赋值(如经典的while((c = getchar()) != EOF)循环)可能会让新手感到困惑(我怀疑MISRA可能或多或少地因为这个原因而禁止它们),但在许多情况下,如果你能容忍它们,它们真的可以帮助消除各种其他丑陋。

0kjbasz6

0kjbasz66#

在过去的一个项目中,我引入了一个标准宏ER_CHK,在整个项目中使用
为清楚起见,将其简化为:

#define ER_CHK(st) if (st != OK) {goto  EXIT;} else ;

字符串
您可以按如下方式使用它:

status_t foo() {
   status_t  st;
   st = bar1(); ER_CHK(st);
   st = bar2(); ER_CHK(st);
   st = bar3(); ER_CHK(st);

   EXIT : ; // ER_CHK calls get here in case of non OK status
   if (st != OK) {
      // put exception handling code here
   }
}


这强制执行了一个单一的标准退出--使真实的的代码非常干净。
实际上,我定义了一个宏来包含发现故障的日志:例如,在

#define ER_CHK(st) if (st != OK) {log(st,__FILE__,__LINE__); goto  EXIT;} else ;


这在堆栈展开时提供日志“回溯”。
主要的一点是,这在整个项目中使用。它允许普通代码脱颖而出,并且有一种处理异常的标准方法,可以被视为一种习惯用法、模式或语言扩展。
它使正常(非异常)代码更具可读性,因为

  • 线路较少
  • 仅相关代码可见
  • 不需要区分正常“IF”和异常处理IF。

有关更完整的描述,请参阅https://forum.misra.org.uk/archive/index.php?thread-368.html和C/C++ Users Journal上的完整文章“Exception Handling in Embedded C Programs”。

mpbci0fu

mpbci0fu7#

我的职位:

type do_stuff (void)
{
    type retval;
  
    retval = do_smth();
    if (retval == OK) {
        retval = do_smth2();
    }
    if (retval == OK) {
        retval = do_smth3();
    }
    return retval;
}

字符串
在(retval = KO)的情况下似乎更慢,但是……今天的编译器非常聪明。我很确定在真实的的编译中函数会立即返回。此外,在MISRA中,不要使用后藤,每个函数中只有一个return。

相关问题