我可以使用break退出多个嵌套的“ for”循环吗?


305

是否可以使用该break函数退出几个嵌套for循环?

如果是这样,您将如何进行?您还可以控制break出口有多少个循环吗?


1
无需使用break或goto退出多个嵌套循环,您可以将该特定逻辑封装在一个函数中,然后使用return退出多个嵌套循环。这将保持代码的美感,并阻止您使用goto,这是不好的编程习惯。
Rishab Shinghal '19年

Answers:


239

AFAIK,C ++不像Java和其他语言那样支持命名循环。您可以使用goto或创建使用的标志值。在每个循环结束时,检查标志值。如果将其设置为true,则可以中断该迭代。


317
goto如果这是最好的选择,请不要害怕。
jkeys

18
因此,我是一名新的C ++程序员(并且没有经过任何正式的编程培训),因此在阅读了有关goto的人们的抱怨之后。我在使用它时犹豫不决,因为我的程序可能会突然爆炸并杀死我。除此之外,当我以前在ti-83上编写程序(当然是在无聊的数学课上)时,基本编辑器提供的功能需要使用goto。
Faken

26
@Faken:使用两种类型的程序员goto:不良程序员和务实的程序员。前者是不言自明的。如果选择使用得当,则会适合后者;当它是(两种)邪恶中的较小者时,则使用所谓的“邪恶”概念。请阅读以下内容,以更好地理解您可能需要不时使用的一些C ++概念(宏,goto,预处理器,数组):parashift.com/c++
jkeys 09年

41
@Faken:使用goto没什么错。它滥用 goto语句是麻烦。
大家

28
@Hooked:是的,除了goto很少使用是最好的选择。为什么不将循环放入自己的函数中(inline如果您担心速度的话),然后return从中开始呢?
09年

265

不,不要用破坏它break。这是使用的最后一个据点goto


19
MISRA C ++编码标准允许使用goto来涵盖这种确切的情况。
理查德·科登

11
真正的程序员不怕使用goto。完成了25年-不后悔-节省了大量的开发时间。
Doug Null

1
我同意。如果您的像素不超过8K,并且必须调用30个Windows OS调用序列,则必须是gotos。
麦克罗伊

或通过将代码放入函数中进行简单的“返回”。
塔拉

68

只是使用lambdas添加一个明确的答案:

  for (int i = 0; i < n1; ++i) {
    [&] {
      for (int j = 0; j < n2; ++j) {
        for (int k = 0; k < n3; ++k) {
          return; // yay we're breaking out of 2 loops here
        }
      }
    }();
  }

当然,这种模式有一定的局限性,显然只有C ++ 11,但我认为它非常有用。


7
这可能会使读者迷惑,认为返回会导致lambda所返回的函数而不是lambda本身返回。
旭妮

3
@Xunie:如果循环是如此复杂,以至于您忘记了自己处于lambda中,那么现在是时候将它们置于不同的函数中了,但是对于简单的情况,这应该可以很好地工作。
MikeMB

17
我认为此解决方案很
不错

您可以在RIIA模板中捕获该lambda,为它命名并在dtor(aka scope gaurd)中调用它。这不会增加任何性能,但是可以提高可读性并减少错过函数调用括号的风险。
Red.Wave

56

打破嵌套循环的另一种方法是将两个循环分解为一个单独的函数,并return在需要退出时从该函数中分解出来。

当然,这提出了另一个论点,即是否应该return从函数的末尾以外的任何地方显式地进行。


7
那是一个C问题。使用RIAA,早归并不是问题,因为与早归相关的所有问题都得到了正确处理。
马丁·约克

4
我知道正确使用RIAA可以解决C ++中的资源清理问题,但是我已经看到了反对提早归还的哲学观点在其他环境和语言中仍然存在。我研究的一个系统中,编码标准禁止提前返回,该函数中散布着布尔变量(名称类似continue_processing),这些布尔变量控制该函数后面的代码块的执行。
Greg Hewgill

21
什么是RIAA?像RAII一样吗?= D
jkeys,2009年

1
取决于他有多少个for循环以及巢的深度如何...您想要蓝色药丸还是红色药丸?
马特2014年

34

break将仅退出包含它的最里面的循环。

您可以使用goto中断任何数量的循环。

当然goto通常被认为是有害的

使用break函数是否合适[...]?

使用break和goto会使推理程序的正确性变得更加困难。有关此问题的讨论,请参见此处: Dijkstra并不疯狂


16
一个很好的答案是,它解释了“ goto是有害的”模因与更为笼统的“控制流中断是有害的”陈述紧密相关。说“ goto有害”,然后转身建议使用break或,这是没有意义的return
帕维尔·米纳夫

6
@Pavel:breakreturn有过的优势goto,你并不需要寻找一个标签,以便找到他们到何处去。是的,它们下面是某种goto,但非常受限制。与不受限制的模式相比,它们更容易被程序员的模式匹配大脑所破译goto。因此,IMO是可取的。
09年

@sbi:是的,但是break仍然不是结构化编程的一部分。忍受比忍受更好goto
jkeys

2
@KarlVoigtland Dijkstra链接已过期;这似乎正在起作用:blog.plover.com/2009/07
Aaron Brager 2013年

3
在这种情况下使用goto绝对没有错。定位良好的goto比其他建议的扭曲解决方案更好,更易读。
詹姆斯

22

尽管已经提出了这个问题,但我认为一种好的方法是执行以下操作:

for(unsigned int z = 0; z < z_max; z++)
{
    bool gotoMainLoop = false;
    for(unsigned int y = 0; y < y_max && !gotoMainLoop; y++)
    {
        for(unsigned int x = 0; x < x_max && !gotoMainLoop; x++)
        {
                          //do your stuff
                          if(condition)
                            gotoMainLoop = true;
        }
    }

}

5
这是很好的,但还不是那么可读,我宁愿转到在这种情况下
ПетърПетров

2
这会使您的代码“相当”慢,因为gotoMainLoop每个周期都会检查一次
Thomas Thomas,2002年

1
在这种情况下,使用实数goto会使核心更具可读性和更好的性能。
ПетърПетров

20

这个怎么样?

for(unsigned int i=0; i < 50; i++)
{
    for(unsigned int j=0; j < 50; j++)
    {
        for(unsigned int k=0; k < 50; k++)
        {
            //Some statement
            if (condition)
            {
                j=50;
                k=50;
            }
        }
    }
}

2
有趣的方法,但我绝对喜欢ered @ inf.ig.sh的处理方式。for(无符号整数y = 0; y <y_max &&!gotoMainLoop; y ++)。
安迪


11

打破几个嵌套循环的一种好方法是将代码重构为一个函数:

void foo()
{
    for(unsigned int i=0; i < 50; i++)
    {
        for(unsigned int j=0; j < 50; j++)
        {
            for(unsigned int k=0; k < 50; k++)
            {
                // If condition is true
                return;
            }
        }
    }
}

4
...这不是一个选择,如果我们必须传递10-20个变量以使该函数堆栈起来。
ПетърПетров

1
@ПетърПетров然后选择一个lambda,它也更好,因为您可以在需要的地方准确定义它。
DarioP

λ为+1,但对游戏引擎核心进行了大修,即使是一个堆栈帧仍然是瓶颈。抱歉,但至少在MSVC 2010中,lambda并不是那么轻量级。
ПетърПетров2014年

@ПетърПетров然后将一对函数更改为一个类,并将堆栈变量更改为私有成员。
亚瑟塔卡

这只会使已经很复杂的代码变得过于复杂:)有时,goto是唯一的解决方案。或者,如果您使用“ goto state X”文档编写复杂的自动机,则goto实际上使代码按文档中的说明进行读取。同样,C#和go都具有goto的目的:没有goto的语言是无法完成的,而goto通常是编写中间翻译器或类似汇编代码的最佳工具。
ПетърПетров

5

goto对于打破嵌套循环可能非常有帮助

for (i = 0; i < 1000; i++) {
    for (j = 0; j < 1000; j++) {
        for (k = 0; k < 1000; k++) {
             for (l = 0; l < 1000; l++){
                ....
                if (condition)
                    goto break_me_here;
                ....
            }
        }
    }
}

break_me_here:
// Statements to be executed after code breaks at if condition

3

break语句终止最近的封闭的执行doforswitch,或while在其出现的语句。控制权传递给终止语句之后的语句。

来自msdn


3

我确实认为goto在这种情况下a 是有效的:

要模拟break/ continue,您需要:

打破

for ( ;  ;  ) {
    for ( ;  ;  ) {
        /*Code here*/
        if (condition) {
            goto theEnd;
        }
    }
}
theEnd:

继续

for ( ;  ; ) {
    for ( ;  ;  ) {
        /*Code here*/
        if (condition) {
            i++;
            goto multiCont;
        }
    }
    multiCont:
}

“继续”将在此处不起作用,因为将不会执行第一个循环的迭代表达式
Fabio A.

我假设第一个循环的迭代器是i。因此i++,在跳转之前
JuliusAlphonso '18

0

其他语言(例如PHP)接受break参数(即break 2;)来指定要突破的嵌套循环级别的数量,而C ++则不行。您必须使用在循环之前设置为false的布尔值(如果要中断,在循环中设置为true),以及在嵌套循环后使用条件中断,检查布尔值是否设置为true,来解决此问题。如果是的话就打破。


0

我知道这是旧帖子。但是我建议一个合理且简单的答案。

for(unsigned int i=0; i < 50; i++)
    {
        for(unsigned int j=0; j < conditionj; j++)
        {
            for(unsigned int k=0; k< conditionk ; k++)
            {
                // If condition is true

                j= conditionj;
               break;
            }
        }
    }

3
这不是一个可扩展性很强的解决方案,因为j = conditionj如果您使用而不是复杂的谓词,则该解决方案将不起作用j < conditionj
谢尔盖

0

只需一个bool变量即可中断任意数量的循环,如下所示:

bool check = true;

for (unsigned int i = 0; i < 50; i++)
{
    for (unsigned int j = 0; j < 50; j++)
    {
        for (unsigned int k = 0; k < 50; k++)
        {
            //Some statement
            if (condition)
            {
                check = false;
                break;
            }
        }
        if (!check)
        {
            break;
        }
    }
    if (!check)
    {
        break;
    }
}

在这段代码中,我们break;所有的循环。


0

我不确定是否值得,但是您可以使用一些简单的宏来模拟Java的命名循环:

#define LOOP_NAME(name) \
    if ([[maybe_unused]] constexpr bool _namedloop_InvalidBreakOrContinue = false) \
    { \
        [[maybe_unused]] CAT(_namedloop_break_,name): break; \
        [[maybe_unused]] CAT(_namedloop_continue_,name): continue; \
    } \
    else

#define BREAK(name) goto CAT(_namedloop_break_,name)
#define CONTINUE(name) goto CAT(_namedloop_continue_,name)

#define CAT(x,y) CAT_(x,y)
#define CAT_(x,y) x##y

用法示例:

#include <iostream>

int main()
{
    // Prints:
    // 0 0
    // 0 1
    // 0 2
    // 1 0
    // 1 1

    for (int i = 0; i < 3; i++) LOOP_NAME(foo)
    {
        for (int j = 0; j < 3; j++)
        {
            std::cout << i << ' ' << j << '\n';
            if (i == 1 && j == 1)
                BREAK(foo);
        }
    }
}

另一个例子:

#include <iostream>

int main()
{
    // Prints: 
    // 0
    // 1
    // 0
    // 1
    // 0
    // 1

    int count = 3;
    do LOOP_NAME(foo)
    {
        for (int j = 0; j < 3; j++)
        {
            std::cout << ' ' << j << '\n';
            if (j == 1)
                CONTINUE(foo);
        }
    }
    while(count-- > 1);
}

-1
  while (i<n) {
    bool shouldBreakOuter = false;
    for (int j=i + 1; j<n; ++j) {
      if (someCondition) {
          shouldBreakOuter = true;
      }
    }

    if (shouldBreakOuter == true)
      break;

  }

-3

您可以使用try ... catch。

try {
    for(int i=0; i<10; ++i) {
        for(int j=0; j<10; ++j) {
            if(i*j == 42)
                throw 0; // this is something like "break 2"
        }
    }
}
catch(int e) {} // just do nothing
// just continue with other code

如果必须一次跳出几个循环,无论如何通常都是一个例外。


1
我想知道这个答案之所以如此之差的原因。
hkBattousai 2015年

6
@hkBattousai该解决方案投了反对票,因为它使用异常控制执行流程。顾名思义,例外仅应用于例外情况。
Helio Santos

4
@HelioSantos这不是语言无法提供适当解决方案的特殊情况吗?
hkBattousai 2015年

8
例外很慢。
哥顿

2
对于并非在99%的时间中都无法恢复的错误,抛出的性能影响是巨大的。
麦克罗伊

-4

打破for循环对我来说有点奇怪,因为for循环的语义通常表明它将执行指定的次数。但是,这在所有情况下都还不错。如果您要在集合中搜索某些东西,并且想在找到之后打破它,这很有用。但是,在C ++中不可能脱离嵌套循环。通过使用带标记的中断可以使用其他语言。您可以使用标签和goto,但这可能会在晚上使您胃灼热。似乎是最好的选择。


11
一点也不奇怪。如果您要遍历某个集合以查找某些内容(并且没有更快的搜索方式),则没有必要完成循环。(例如)
Joe
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.