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

标签 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);
  }
}

最佳答案

我认为 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但我们在易读性方面付出了巨大的代价。

标记并替换 breakcontinue

我们能做什么?替换breakcontinue并在循环条件中使用该标志:

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 上发布您的完整代码(包括周围的代码并使用真实姓名,解释其用途)。

关于c - MISRA C 2012 规则 15.4 用于终止任何迭代语句的break或goto语句不应超过一个,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47741301/

相关文章:

c++ - 数组的地址 VS 指针到指针 : Not the same?

c++ - C++中的桶指针是什么?

C - 链表无法将节点**转换为节点*

c - 不应在指针类型和整数类型之间执行强制转换

c++ - 单元测试对实例变量函数的调用

在 C99+ 中结合指定的初始值设定项和 malloc?

python - 设置 Python 二进制模块构建的包含路径

c - 如何在不违反 MISRA 规则的情况下将位域变量分配给 uint8_t 变量?

c - 算术加法的 MISRA C 合规性

c++ - 米斯拉警告包括守卫