在 for 循环中使用 break 是一种不好的做法吗?

for循环中使用 break语句是一个坏习惯吗?

例如,我正在数组中搜索一个值。比较 for 循环内部,当找到值时,break;退出 for 循环。

这是个坏习惯吗?我已经看到了替代方法: 定义一个变量 vFound,并在找到该值时将其设置为 true,然后在 for语句条件中检查 vFound。但是,是否有必要为此创建一个新变量呢?

我是在一个普通的 C 或 C + + for 循环的上下文中问这个问题的。

附注: MISRA 编码准则建议不要使用中断。

225844 次浏览

for循环中使用 breakcontinue是非常好的。

它简化了代码并提高了可读性。

这取决于语言,你可以在这里检查一个布尔变量:

for (int i = 0; i < 100 && stayInLoop; i++) { ... }

在对数组进行迭代时不可能这样做:

for element in bigList: ...

无论如何,break会使两个代码更易读。

在您的示例中,您不知道 为了循环的迭代次数。为什么不使用 同时循环,它允许迭代次数在开始时是不确定的?

因此,通常不需要使用 打破陈述,因为循环可以更好地表述为 同时循环。

不,休息是正确的解决办法。

添加布尔变量会使代码更难阅读,并且会增加潜在的错误源。

你可以找到各种各样的带有“ break”语句的专业代码。必要的时候使用它是完全有意义的。在您的情况下,这个选项比仅仅为了从循环中出来而创建一个单独的变量要好。

当然,break;是停止 for 循环或 foreach 循环的解决方案。我在 foreach 和 for loop 中使用了 php,发现它起作用了。

取决于你的用例。在一些应用程序中,for 循环的运行时间需要保持不变(例如,为了满足一些时间约束,或者为了隐藏数据内部以防止基于时间的攻击)。

在这些情况下,甚至可以设置一个标志,只在所有 for循环迭代实际运行之后检查标志值。当然,所有 for 循环迭代都需要运行仍然需要大约相同时间的代码。

如果您不关心运行时... 使用 break;continue;使代码更容易阅读。

关于 米斯拉98规则,这是用在我的公司在 C 开发,破坏声明不得使用..。

编辑: 在 MISRA’04允许休息

Break 是完全可以使用的语句(顺便说一句,继续也是如此)。关键在于代码的可读性——只要没有过于复杂的循环之类的,就没问题。

他们和 Goto不是同一个联赛。 :)

我同意其他人建议使用 break。一个显而易见的重要问题是,为什么会有人推荐其他方法呢?嗯... 当您使用 break 时,您将跳过块中的其余代码和其余迭代。有时这会导致 bug,例如:

  • 在块的顶部获得的资源可能会在块的底部释放(即使对于 for循环中的块也是如此) ,但是当 break语句导致“过早”退出时,释放步骤可能会被意外地跳过(在“现代”C + + 中,“ RAII”被用来以一种可靠且异常安全的方式处理这个问题: 基本上,无论作用域如何退出,对象析构函数都可靠地释放资源)

  • 有人可能会改变 for语句中的条件测试,而没有注意到存在其他离域退出条件

  • Ndim 的答案指出,有些人可能会避免使用 break来维持一个相对一致的循环运行时,但是您将 break与使用布尔提前退出控制变量的情况进行了比较

时不时地,观察这些 bug 的人们意识到,这些 bug 可以通过“不中断”规则来预防或减轻... ... 事实上,有一个相关的“更安全”编程策略称为“结构化编程”,其中每个函数都应该有一个单一的入口和退出点(即没有 goto,没有提前返回)。它可能会消除一些 bug,但无疑会引入其他 bug。他们为什么要这么做?

  • 他们有一个开发框架,鼓励特定风格的编程/代码,他们有统计学证据表明,这在有限的框架中产生净效益,或者
  • 他们已经受到编程指导方针或在这样一个框架内的经验的影响,或者
  • 他们只是独裁的白痴,或者
  • 任何上述 + 的历史惯性(相关性在于,与现代 C + + 相比,这些论证更适用于 C 语言)。

这是完全有效的使用 break-正如其他人指出,它没有在同一联盟作为 goto

不过,当您想在循环外检查数组中是否找到该值时,可能需要使用 vFound变量。另外,从可维护性的角度来看,使用一个通用标志来指示退出标准可能是有用的。

Python (和其他语言?)远非不好的实践扩展了 for循环结构,因此如果循环 没有 break,它的一部分将被执行 只有

for n in range(5):
for m in range(3):
if m >= n:
print('stop!')
break
print(m, end=' ')
else:
print('finished.')

产出:

stop!
0 stop!
0 1 stop!
0 1 2 finished.
0 1 2 finished.

没有 break和那个方便的 else的等效代码:

for n in range(5):
aborted = False
for m in range(3):
if not aborted:
if m >= n:
print('stop!')
aborted = True
else:
print(m, end=' ')
if not aborted:
print('finished.')

在嵌入式世界中,有很多代码使用以下结构:

    while(1)
{
if (RCIF)
gx();
if (command_received == command_we_are_waiting_on)
break;
else if ((num_attempts > MAX_ATTEMPTS) || (TickGet() - BaseTick > MAX_TIMEOUT))
return ERROR;
num_attempts++;
}
if (call_some_bool_returning_function())
return TRUE;
else
return FALSE;

这是一个非常普通的例子,很多事情都发生在幕后,特别是中断。不要将它用作样板代码,我只是试图演示一个示例。

我个人的观点是,以这种方式编写循环没有什么错,只要采取适当的注意措施,防止无限期地停留在循环中。

一般规则: 如果遵循一条规则需要你做一些比违反规则更尴尬和难以阅读的事情,那么就违反规则。

在循环直到找到某些内容的情况下,当您出去时,会遇到区分已找到和未找到的问题。那就是:

for (int x=0;x<fooCount;++x)
{
Foo foo=getFooSomehow(x);
if (foo.bar==42)
break;
}
// So when we get here, did we find one, or did we fall out the bottom?

所以,可以设置一个标志,或者将“ found”值初始化为 null

这就是为什么一般情况下我更喜欢将搜索推送到函数中:

Foo findFoo(int wantBar)
{
for (int x=0;x<fooCount;++x)
{
Foo foo=getFooSomehow(x);
if (foo.bar==wantBar)
return foo;
}
// Not found
return null;
}

这也有助于整理代码。在主行中,“ find”变成了一个单独的语句,当条件复杂时,它们只写一次。

如果你想在那个时候完成停止处理,我看不出有什么理由说这是一个不好的做法。

我认为将您的检查放在 for 循环的顶部是有意义的,就像这样

for(int i = 0; i < myCollection.Length && myCollection[i].SomeValue != "Break Condition"; i++)
{
//loop body
}

或者如果需要首先处理该行

for(int i = 0; i < myCollection.Length && (i == 0 ? true : myCollection[i-1].SomeValue != "Break Condition"); i++)
{
//loop body
}

这样,你就可以有一个奇异的身体函数没有中断。

for(int i = 0; i < myCollection.Length && (i == 0 ? true : myCollection[i-1].SomeValue != "Break Condition"); i++)
{
PerformLogic(myCollection[i]);
}

它也可以被修改以移动到它自己的函数中。

for(int i = 0; ShouldContinueLooping(i, myCollection); i++)
{
PerformLogic(myCollection[i]);
}

使用 break 语句本身没有什么错误,但是嵌套循环可能会造成混淆。为了提高可读性,许多语言(至少 Java 可以)支持标签分割,这将大大提高可读性。

int[] iArray = new int[]{0,1,2,3,4,5,6,7,8,9};
int[] jArray = new int[]{0,1,2,3,4,5,6,7,8,9};


// label for i loop
iLoop: for (int i = 0; i < iArray.length; i++) {


// label for j loop
jLoop: for (int j = 0; j < jArray.length; j++) {


if(iArray[i] < jArray[j]){
// break i and j loops
break iLoop;
} else if (iArray[i] > jArray[j]){
// breaks only j loop
break jLoop;
} else {
// unclear which loop is ending
// (breaks only the j loop)
break;
}
}
}

我会说 break (和 return)语句经常增加 循环复杂度,这使得在所有情况下证明代码正在做正确的事情变得更加困难。

如果您正在考虑在对某个特定项目的序列进行迭代时使用中断,那么您可能需要重新考虑用于保存数据的数据结构。使用 Set 或 Map 可能会提供更好的结果。

这里有很多答案,但我还没看到有人提到过:

如果编写整洁、易读的循环,那么在 for 循环中使用 breakcontinue的大多数“危险”都会被消除。如果循环体跨越几个屏幕长度,并且有多个嵌套的子块,是的,您可能很容易忘记有些代码在中断后不会执行。但是,如果循环很短而且切中要点,那么 break 语句的用途应该是显而易见的。

如果循环变得太大,那么在循环中使用一个或多个命名良好的函数调用。避免这样做的唯一真正原因是处理瓶颈。

我对目前正在处理的代码库(40,000行 JavaScript)做了一些分析。

我发现只有22个 break语句,其中有:

  • switch语句中使用了19个(我们总共只有3个 switch 语句!)。
  • 2在 for循环中使用——我立即将这段代码分类为重构成单独的函数并用 return语句替换。
  • 至于 while循环中的最后一个 break... 我运行 git blame看看是谁写了这个垃圾!

所以根据我的统计: 如果在 ABC1之外使用 break,那么它是一种代码气味。

我还搜索了 continue语句,没找到。