我可以使用break退出多个嵌套'for'循环?

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

如果是,你会怎么做呢?你还能控制break退出多少个循环吗?

329953 次浏览

break语句终止了它出现的最近的封闭doforswitchwhile语句的执行。控制权转移到终止语句后面的语句。

msdn

中止for循环对我来说有点奇怪,因为for循环的语义通常表明它将执行指定的次数。然而,并不是在所有情况下都是坏事;如果您在集合中搜索某个东西,并且在找到它之后想要中断它,那么它是有用的。然而,打破嵌套循环在c++中是不可能的;在其他语言中,它是通过使用有标记的间断符来实现的。你可以使用标签和goto,但这可能会让你在晚上胃灼热。这似乎是最好的选择。

c++不支持命名循环,就像Java和其他语言一样。您可以使用goto,或创建您使用的标志值。在每个循环结束时检查标志值。如果它被设置为true,那么您可以跳出该迭代。

打破将只退出包含它的最里面的循环。

你可以使用转到来跳出任意数量的循环。

当然,转到通常是被认为是有害的

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

使用break和goto会使判断程序的正确性变得更加困难。有关Dijkstra并不是疯了的讨论请参见这里。

其他语言如PHP接受break参数(即break 2;)来指定你想要跳出的嵌套循环级别的数量,但c++不接受。你必须使用一个在循环之前设置为false的布尔值,如果你想中断,在循环中设置为true,在嵌套循环之后加上一个条件中断,检查布尔值是否设置为true,如果是则中断。

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

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

当然,这会引起另一个争论,即是否应该从函数的任何地方显式地return,而不是在结束处。

虽然这个答案已经提出了,但我认为一个很好的方法是这样做:

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;
}
}


}

这个怎么样?

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;
}
}
}
}

你可以用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

如果必须同时跳出几个循环,通常会出现异常。

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

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;
}
}
}
}

使用goto和一个标签来打破嵌套循环的代码示例:

for (;;)
for (;;)
goto theEnd;
theEnd:

只是使用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,但我认为它非常有用。

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

要模拟break/continue,你需要:

打破

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

继续

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

我知道这是老帖子了。但我想提出一个更合理、更简单的答案。

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;
}
}
}

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
  while (i<n) {
bool shouldBreakOuter = false;
for (int j=i + 1; j<n; ++j) {
if (someCondition) {
shouldBreakOuter = true;
}
}


if (shouldBreakOuter == true)
break;


}

通过一个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;所有的循环。

我不确定这样做是否值得,但你可以用一些简单的宏来模拟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);
}

我知道这是一个老帖子,但我觉得这真的需要说,没有其他地方可以说。对于这里的每个人,使用转到。,我刚刚用了它。

像几乎所有的事情一样,goto也不是百分之百的“坏”。或“好。至少有两种用法,我想说,如果你使用goto,而不使用它,你不仅应该是100%的好,而且你的程序会比没有它更可读,因为它让你的意图更加清晰(有一些方法可以避免它,但我发现它们都更笨拙):

  1. 打破嵌套循环,和
  2. 错误处理(即在函数的末尾跳转到清理例程,以返回失败代码并释放内存。)

与其教条地接受诸如“一般般”是“邪恶的”这样的规则,不如理解情感是被声称的,并遵循“为什么”,情感的字母。不知道这一点也给我带来了很多麻烦,以至于我认为教条地称事物为“邪恶”。可能比东西本身更有害。在最坏的情况下,你只是得到了糟糕的代码——然后你知道你没有正确地使用它,只要你听说要小心,但如果你正在努力满足教条主义,我会说这是更糟糕的。

为什么“goto"被称为“邪恶”;是因为你不应该用它来代替普通的if、for和while。为什么呢?试试吧,试试用“goto”;而不是普通的控制逻辑语句,然后尝试用控制逻辑再写一遍相同的代码,告诉我哪个看起来更好更容易理解,哪个看起来更乱。好了。(奖励:尝试现在添加一个新功能到goto-only代码。)这就是为什么它是“邪恶”,在“邪恶”周围有合适的范围限定。用它来短路C的“break"命令是一个有问题的用法,只要你从代码中清楚地表明你的goto应该完成什么(例如使用像"nestedBreak"之类的)。打破一个嵌套循环是非常自然的。

(或者更简单地说:使用goto来跳出循环。我想说那更可取。使用goto来创建循环。这是“evil")。

你怎么知道你是不是太教条了?如果遵循“xyz是邪恶的”;rule导致你的代码可理解,因为你扭曲自己试图绕过它(例如通过在每个循环中添加额外的条件,或一些标志变量,或其他类似的技巧),那么你很可能是教条主义的。

没有什么比学习良好的思考习惯更能替代的了。前者先于后者,一旦采用前者,后者往往紧随其后。然而,问题是,我经常发现,后者不够阐述。太多的人只是简单地说“这很糟糕”;“这需要更多的思考”;不用说什么去思考,思考什么关于,和为什么。这是一个巨大的耻辱。

(就其价值而言,在c++中,需要打破的嵌套循环仍然存在,但需要错误代码并不:在这种情况下,总是使用异常处理错误代码,从来没有回报他们,除非它是如此频繁,引发异常,将会导致性能问题,例如在紧密循环在高需求服务器代码,也许(有些人可能会说,“例外”应该使用很少,但这是另一个恶意的教条主义的一部分:不,至少在我的经验中,在违背教条之后,我发现它们让事情变得更加清晰——只是不要滥用它们来做一些除了错误处理,比如使用它们作为控制流;实际上与“;goto"”相同。如果你把它们都用于错误处理,那就是它们存在的目的。)

 bool found = false;
    

for(int i=0; i < m; ++i){
for(int j=0; j < n; ++j)
if(grid[i][j] == '*'){
q.push(make_pair(i,j));
found = true;
break;
}
if(found)
break;
}
我的建议是使用一个检查变量来打破一个期望的循环。结果代码可能不那么令人愉快。
您可以使用预处理器,以便在引擎盖下进行所需的破坏。这种方法可以隐藏丑陋的代码和额外的复杂性。
例如,我创建了我的自定义中断机制如下:

想要的代码:

for (int i = 0; i < 100; i++) {
for (int j = 0; j < 100; j++) {
for (int k = 0; k < 100; k++) {
//do something
if (desiredCondition) {
breakToLevel = 0;
}
if (breakToLevel < 3) {
break;
}
}
if (breakToLevel < 2) {
break;
}
}
if (breakToLevel < 1) {
break;
}
}

宏定义:

#define BREAK_TO(L) breakToLevel = (L);
#define CHECK_BREAK(L) if (breakToLevel < (L)) break;

和结果:

for (int i = 0; i < 100; i++) {
for (int j = 0; j < 100; j++) {
for (int k = 0; k < 100; k++) {
//do something
if (desiredCondition) {
BREAK_TO(0)
}
CHECK_BREAK(3)
}
CHECK_BREAK(2)
}
CHECK_BREAK(1)
}