MISRA C 2012规则15.4用于终止任何迭代语句的break或goto语句不应超过一个

时间:2017-12-10 17:10:18

标签: c misra

我试图在我的代码中摆脱多个break和goto语句。 正如规则所示,在任何迭代语句中都不应该使用多个break或goto语句

示例代码:

 for(int32_t i = 0; i < 10; i++) { 
  if (i == number1) {                
      return_val = 2*number1 + number2; 
      break;
  }

  number1 += (5 * number2 + 2*number3);

  if (i == number2) {
      return_val = number1 + (3 * number3);
      break;                  
  }

  number1 += 2 * number3;

  if (i == number3) {
      return_val = number1 + (2 * number2);
  }
}

我尝试过使用嵌套的if语句,但它不是终止循环的解决方案。

而且不是休息,如果goto语句存在可能是什么修复。

带goto的示例代码:

for(int32_t i = 0; i < 10; i++) { 
  if (i == number1) {                
      return_val = 2*number1 + number2; 
      goto LABE1;
  }

  number1 += (5 * number2 + 2*number3);

  if (i == number2) {
      return_val = number1 + (3 * number3);
      goto LABE2;                  
  }

  number1 += 2 * number3;

  if (i == number3) {
      return_val = number1 + (2 * number2);
  }
}

2 个答案:

答案 0 :(得分:3)

我认为15.4和15.5是两个极具争议的MISRA建议。请记住,他们不是必需的,而是建议性的,然后您可以将他们(如果您的组织批准)归类为“拒绝”(tnx Andrew)。在我看来遵循这些规则,你会使你的代码更冗长,更难阅读,然后更容易出错。

正如我所说,这只是我的观点,让我们尝试一步一步的方法。我没有使用专有名称,但您必须从您的域名中选择好名字。

标记并保留一个break

您可以使用简单的_Bool标记。假设您包含stdbool.h

bool flag = false;

for(int32_t i = 0; i < 10; i++) { 
  if (i == number1) {                
      return_val = 2*number1 + number2; 
      flag = true;
  } else {
      number1 += (5 * number2 + 2*number3);

      if (i == number2) {
          return_val = number1 + (3 * number3);
          flag = true;
      } else {
          number1 += 2 * number3;

          if (i == number3) {
              return_val = number1 + (2 * number2);
          }
      }
    }

    if (flag)
        break;
}

请为flag选择一个更好的名称,理想情况下,您应该描述条件。正如您所看到的,我们只有一个break,但我们付出了巨大的可读性。

标记并将break替换为continue

我们能做什么?将break替换为continue并使用循环条件中的标志:

bool flag = true;

for(int32_t i = 0; flag && i < 10; i++) { 
  if (i == number1) {                
      return_val = 2*number1 + number2;
      flag = false;
      continue;
  }

  number1 += (5 * number2 + 2*number3);

  if (i == number2) {
      return_val = number1 + (3 * number3);
      flag = false;
      continue;
  }

  number1 += 2 * number3;

  if (i == number3) {
      return_val = number1 + (2 * number2);
  }
}

稍微好一点,但事实是我们没有解决此代码中的主要问题。

将其移至单独的功能

如果此代码段是更大功能的一部分,那么事实是我们正在解决错误的问题。 break 本身本身不会导致问题,但会在更大的环境中使用。在C中我们没有std::optional<T>然后我们可以使用 out 参数(但它不是实现此结果的唯一技术):

for(int32_t i = 0; < 10; i++) {
    if (foo(i, &number1, number2, number3, &return_value)) {
        break;
    }
}

使用与flag的第一次实现类似的单独功能。

bool foo(int32_t i, int32_t* number1, int32_t* return_val) {
  bool has_result = false;

  if (i == *number1) {                
      *return_val = 2 * *number1 + number2; 
      has_result = true;
  } else {
      // ...
  }

  return has_result;
}

更好的是,如果你可以忽略15.5那么这个函数将很容易编写和阅读:

if (i == *number1) {                
    *return_val = 2 * *number1 + number2; 
    return true;
}

// ...

不要忘记在适当的地方添加const。我很确定这可以被重构为更好(函数参数太多,混合值和指针,这两个函数之间的耦合太大)但是由于虚拟名称和缺少周围代码,很难提出更好的建议;我要强调的是将代码移动到单独的函数。

如果分支内的代码足够复杂,那么引入三个独立的函数甚至可能会带来更多好处。 2 * *number1 + number2是什么?它在计算什么?

随时在Code Review上发布您的完整代码(包括周围代码和真实姓名,解释其用途)。

答案 1 :(得分:0)

这是一个相当模糊的算法。在实践中,这通常是许多MISRA规则归结为的,它们阻止你编写奇怪的代码。虽然我无法确切地知道,但它有点像这个代码依赖于各种全局变量,并且可能有更好的方法来编写它。

然而,如果算法与您编写的算法完全相同,并且没有办法解决它,那就是它的本质。在这种情况下,我会用多个return语句重写代码,这违反了另一个(建议)MISRA规则。但是,偏离该规则比反对多次破解/转到更为合理的规则更好。

for(int32_t i = 0; i < 10; i++) { 
  if (i == number1) {                
      return 2*number1 + number2; 
  }

  number1 += (5 * number2 + 2*number3);

  if (i == number2) {
      return number1 + (3 * number3);
  }

  number1 += 2 * number3;

  if (i == number3) {
      return number1 + (2 * number2);
  }

这比问题中的任何替代方案都更具可读性。

(请注意,您必须u后缀所有整数常量以符合MISRA标准)