134

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

说,我正在寻找一个数组中的值。在 for 循环内进行比较,当找到值时,break;退出 for 循环。

这是一个不好的做法吗?我已经看到使用的替代方法:定义一个变量并在vFound找到值时将其设置为 true 并检查语句条件。但是是否有必要为此创建一个新变量?vFoundfor

我在普通 C 或 C++ for 循环的上下文中询问。

PS:MISRA 编码指南建议不要使用 break。

4

19 回答 19

146

不,break 是正确的解决方案。

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

于 2010-10-13T10:27:30.303 回答
133

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

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

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

于 2010-10-13T17:16:54.437 回答
54

您可以找到各种带有“break”语句的专业代码。必要时使用它非常有意义。在您的情况下,此选项比仅出于退出循环的目的而创建单独的变量要好。

于 2010-10-13T10:40:21.130 回答
51

在循环中使用break以及使用非常好。continuefor

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

于 2010-10-13T10:18:06.647 回答
24

远非坏习惯,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.')
于 2010-10-13T13:54:02.843 回答
17

一般规则:如果遵守规则需要你做一些更尴尬和难以阅读的事情然后打破规则,那就打破规则。

在循环直到找到某些东西的情况下,当您退出时,您会遇到区分找到与未找到的问题。那是:

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?

好吧,您可以设置一个标志,或将“找到”值初始化为 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”变成了一条语句,当条件复杂时,它们只写一次。

于 2010-10-13T14:33:00.440 回答
15

使用 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 之类的东西可能会提供更好的结果。

于 2010-10-13T16:24:35.660 回答
14

break是一个完全可以接受的语句(顺便说一句, continue也是如此)。这都是关于代码可读性的——只要你没有过于复杂的循环等,就可以了。

这不像他们和goto是同一个联盟。:)

于 2010-10-13T11:09:34.103 回答
13

这取决于语言。虽然您可以在此处检查布尔变量:

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

遍历数组时不可能这样做:

for element in bigList: ...

无论如何,break将使这两个代码更具可读性。

于 2010-10-13T10:21:18.927 回答
8

我同意其他推荐使用break. 显而易见的后果性问题是,为什么会有人不推荐?好吧...当您使用 break 时,您会跳过块中的其余代码以及剩余的迭代。有时这会导致错误,例如:

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

  • 有人可能会更改for语句中的条件测试,而不会注意到还有其他非本地化的退出条件

  • ndim 的回答观察到,有些人可能会避免breaks 以保持相对一致的循环运行时,但您正在比较break不适用的布尔提前退出控制变量的使用

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

  • 他们有一个鼓励特定风格的编程/代码的开发框架,并且他们有统计证据表明这会在那个有限的框架中产生净收益,或者
  • 他们受到这样一个框架内的编程指南或经验的影响,或者
  • 他们只是独裁的白痴,或者
  • 任何上述 + 历史惯性(相关的理由是比现代 C++ 更适用于 C)。
于 2010-10-13T11:57:27.650 回答
6

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

因此通常不需要使用break 语句,因为循环可以更好地表述为while循环。

于 2010-10-13T10:21:44.090 回答
5

使用它是完全有效的break——正如其他人所指出的,它与goto.

vFound虽然当您想在循环外检查是否在数组中找到值时,您可能希望使用该变量。同样从可维护性的角度来看,有一个表示退出标准的通用标志可能是有用的。

于 2010-10-13T12:53:13.820 回答
5

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

我只找到了 22条break语句,其中:

  • 19 个用于内部switch语句(我们总共只有 3 个 switch 语句!)。
  • 2 在for循环内部使用 - 我立即将其归类为要重构为单独的函数并替换为return语句的代码。
  • 至于最后的breakwhile循环……我跑去git blame看看是谁写的这个废话!

所以根据我的统计:如果break在外面使用switch,就是代码异味。

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

于 2010-10-13T17:21:24.677 回答
4

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

    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;

这是一个非常普遍的例子,很多事情都在幕后发生,尤其是中断。不要将其用作样板代码,我只是想说明一个示例。

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

于 2010-10-13T14:11:23.853 回答
4

我看不出有任何理由说明这将是一种不好的做法,前提是您想在那时完成 STOP 处理。

于 2010-10-13T16:21:35.890 回答
3

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

for在这些情况下,设置标志并仅在所有循环迭代实际运行后检查标志值甚至是有意义的。当然,所有的 for 循环迭代都需要运行仍然需要大约相同时间的代码。

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

于 2010-10-13T11:02:17.367 回答
2

在我公司在 C 开发中使用的MISRA 98 规则中,不得使用 break 语句......

编辑:在 MISRA '04 中允许中断

于 2010-10-13T11:05:27.443 回答
0

当然,break;解决方法是停止for循环或foreach循环。我在 foreach 和 for 循环中的 php 中使用它并发现工作正常。

于 2010-10-13T11:01:05.573 回答
0

我认为像这样将检查放在 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]);
}

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

for(int i = 0; ShouldContinueLooping(i, myCollection); i++)
{
    PerformLogic(myCollection[i]);
}
于 2010-10-13T16:22:13.653 回答