MISRA C 2012 规则 15.4 用于终止任何迭代语句的 break 或 goto 语句不应超过一个
MISRA C 2012 Rule 15.4 There should be no more than one break or goto statement used to terminate an any iteration statement
我正试图摆脱代码中的多个 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 语句而不是 break,那么可以解决什么问题。
带有转到的示例代码:
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);
}
}
这是一个相当晦涩的算法。实际上,这通常是许多 MISRA 规则的归结点,它们会阻止您编写奇怪的代码。虽然我不能确定,但感觉这段代码依赖于各种全局变量,可能有更好的方法来编写它。
但是,如果算法与您编写的算法完全一样并且没有办法绕过它,那么它就是这样。在那种情况下,我会用多个 return 语句重写代码,这违反了另一个(建议的)MISRA 规则。但是,最好偏离该规则,而不是更合理的反对多个 break/goto.
的规则
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 标准)
我认为15.4和15.5是两个极具争议的MISRA建议。请记住,它们不是必需的,而是建议性的,然后您可以将它们分类(如果您的组织批准)为 "disapplied" ()。在我看来,遵循这些规则会让你的代码更冗长、更难阅读,也更容易出错。
正如我所说,这只是我的意见,让我们尝试一种循序渐进的方法。我没有使用专有名称,但您必须从您的域中选择合适的名称。
标记并保留一个 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
,但我们在易读性方面付出了巨大的代价。
标记并用 continue
替换 break
我们能做什么?将 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 上 post 您的完整代码(包括周围代码和真实姓名,解释其用途)。
我正试图摆脱代码中的多个 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 语句而不是 break,那么可以解决什么问题。
带有转到的示例代码:
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);
}
}
这是一个相当晦涩的算法。实际上,这通常是许多 MISRA 规则的归结点,它们会阻止您编写奇怪的代码。虽然我不能确定,但感觉这段代码依赖于各种全局变量,可能有更好的方法来编写它。
但是,如果算法与您编写的算法完全一样并且没有办法绕过它,那么它就是这样。在那种情况下,我会用多个 return 语句重写代码,这违反了另一个(建议的)MISRA 规则。但是,最好偏离该规则,而不是更合理的反对多个 break/goto.
的规则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 标准)
我认为15.4和15.5是两个极具争议的MISRA建议。请记住,它们不是必需的,而是建议性的,然后您可以将它们分类(如果您的组织批准)为 "disapplied" (
正如我所说,这只是我的意见,让我们尝试一种循序渐进的方法。我没有使用专有名称,但您必须从您的域中选择合适的名称。
标记并保留一个 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
,但我们在易读性方面付出了巨大的代价。
标记并用 continue
替换 break
我们能做什么?将 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 上 post 您的完整代码(包括周围代码和真实姓名,解释其用途)。