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

时间:2010-10-13 10:14:44

标签: for-loop break

break循环中使用 for声明是不好的做法吗?

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

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

我在正常的C或C ++ for循环中询问。

P.S:MISRA coding guidelines建议不要使用break。

19 个答案:

答案 0 :(得分:130)

不,休息是正确的解决方案。

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

答案 1 :(得分:104)

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

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

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

答案 2 :(得分:49)

您可以在其中找到各种带有'break'语句的专业代码。在必要时使用它是完全有意义的。在您的情况下,此选项优于仅为了退出循环而创建单独的变量。

答案 3 :(得分:45)

break循环中使用continuefor完全没问题。

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

答案 4 :(得分:21)

Python(以及其他语言?)远非不好的做法,扩展了for loop结构,因此如果循环不仅会被执行> 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.')

答案 5 :(得分:14)

使用break语句没有任何内在错误,但嵌套循环可能会让人感到困惑。为了提高可读性,许多语言(at least Java does)支持打破标签,这将大大提高可读性。

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)语句经常会增加cyclomatic complexity,这使得在所有情况下都很难证明代码正在做正确的事情。

如果您正在考虑在迭代某个特定项目的序列时使用中断,则可能需要重新考虑用于保存数据的数据结构。使用Set或Map之类的东西可以提供更好的结果。

答案 6 :(得分:14)

一般规则:如果遵循规则要求您做一些更尴尬和难以阅读的事情然后违反规则,那么违反规则。

在循环直到你找到某些东西的情况下,你遇到了在你离开时区分发现与未找到的问题。那就是:

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”成为单个语句,当条件复杂时,它们只写一次。

答案 7 :(得分:13)

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

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

在数组上进行操作时无法执行此操作:

for element in bigList: ...

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

答案 8 :(得分:13)

中断是一个完全可以接受的声明(继续,顺便说一下)。这都是关于代码可读性的 - 只要你没有过于复杂的循环等等,它就没问题了。

这并不像他们和 goto 一样。 :)

答案 9 :(得分:7)

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

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

  • 有人可能会在for声明中更改条件测试,而不会注意到其他离域退出条件

  • ndim的回答观察到有些人可能会避免break来维持一个相对一致的循环运行时,但是你正在比较break与使用布尔提前退出控制变量的比较

每隔一段时间观察这些错误的人就会意识到可以通过这种“无中断”规则来防止/减轻这种错误......事实上,有一个完整的“安全”编程策略称为“结构化编程”,其中每个函数都是应该也有一个入口和出口点(即没有转到,没有提前返回)。它可能会消除一些错误,但无疑会引入其他错误。他们为什么这样做?

  • 他们有一个鼓励特定风格的编程/代码的开发框架,他们有统计证据证明这在有限的框架中产生了净收益,或者
  • 他们受到编程指南或此类框架中的经验的影响,或
  • 他们只是独裁的白痴,或者
  • 以上任何一种+历史惯性(相关的理由更适用于C而不是现代C ++)。

答案 10 :(得分:7)

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

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

答案 11 :(得分:5)

使用break完全有效 - 正如其他人所指出的那样,它与goto在同一个联盟中无处可去。

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

答案 12 :(得分: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;

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

我个人认为,只要采取适当的谨慎措施以防止无限期地留在循环中,以这种方式编写循环没有任何问题。

答案 13 :(得分:4)

我认为没有任何理由说明为什么你想在那时完成STOP处理。

答案 14 :(得分:4)

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

我发现只有22个break语句

  • 19在switch语句中使用(我们总共只有3个switch语句!)。
  • 2在for循环中使用 - 我立即将这些代码重新分类为单独的函数并替换为return语句。
  • 至于最后breakwhile循环......我跑git blame看看谁写了这个垃圾!

根据我的统计数据显示:如果在break之外使用switch,则会产生代码异味。

我还搜索了continue个语句。找不到。

答案 15 :(得分:3)

取决于您的使用案例。在某些应用中,for循环的运行时需要保持不变(例如,为了满足某些时序约束,或者将数据内部隐藏在基于时序的攻击中)。

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

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

答案 16 :(得分:2)

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

编辑:MISRA '04允许休息

答案 17 :(得分:1)

我不同意!

为什么要忽略for循环的内置功能来创建自己的?你不需要重新发明轮子。

我认为将你的支票放在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++)
{
    DoAllThatCrazyStuff(myCollection[i]);
}

或者如果您的情况很复杂,您也可以移出该代码!

for(int i = 0; i < myCollection.Length && BreakFunctionCheck(i, myCollection); i++)
{
    DoAllThatCrazyStuff(myCollection[i]);
}
充满休息的“专业代码”对我来说听起来并不像专业代码。这听起来像是懒惰的编码;)

答案 18 :(得分:0)

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