76

I have two for loops nested like this:

for(...) {
    for(...) {

    }
}

I know that there is a break statement. But I am confused about if it breaks both loops or just the one in which it was called? I need to break both ones as soon as I see that it doesn't make sense to iterate more times over.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Thanks
  • 40,109
  • 71
  • 208
  • 322

13 Answers13

109

If using goto simplifies the code, then it would be appropriate.

for (;;) 
{
    for (;;) 
    {
        break; /* breaks inner loop */
    } 
    for (;;) 
    {
        goto outer; /* breaks outer loop */
    }
} 
outer:;
Ori Pessach
  • 6,777
  • 6
  • 36
  • 51
95

break breaks out of one loop, but you can add a check to the outer loop which breaks when the inner breaks.

bool dobreak = false;
for ( ..; !dobreak && ..; .. ) {
   for ( ... ) {
      if (...) {
         dobreak = true;
         break;
      }
   }
}
jbasko
  • 7,028
  • 1
  • 38
  • 51
  • 56
    IMHO, using goto is much cleaner. – sigjuice May 14 '09 at 19:03
  • 4
    `!dobreak` is in the wrong place; it should go in the conditional (for's second part) rather than the increment step (for's third part); I would also use `!dobreak && ..`, so that the other conditions do not need to be evaluated when breaking. I agree with sigjuice: `goto` is not evil when used properly, and this is a case where `goto` *does* make for better code. – ephemient May 15 '09 at 16:45
  • 2
    This would still execute possible code in the outer loop that would come after the inner loop – Martijn Sep 01 '10 at 20:04
  • @Martijn - this can be addressed by replacing the `break` with a `continue` statement. Still, it's just more non-obvious stuff to worry about. – Ori Pessach Oct 25 '11 at 17:54
14

The break statement only gets you out of the innermost loop. If you don't want the added overhead in code, memory and performance of a dedicated state variable, I recommend refactoring the code out into a function or method of its own, and using return to get out of all the loops:

void do_lots_of_work(void)
{
  int i, j;

  for(i=0; i<10 ; i++)
  {
    for(j=0;j< 10; j++)
    {
     ..
     ..
     if(disaster_struck())
      return; /* Gets us out of the loops, and the function too. */
    }
  }
}
unwind
  • 391,730
  • 64
  • 469
  • 606
  • I'd be very surprised if a function call in a loop managed to perform as well as a dedicated state variable, especially if you need to pass arguments in order to have access to variables in the outer function. Maybe if the compiler copies the function inline... – ephemient May 14 '09 at 17:36
  • 6
    What's worse, in my opinion, is the loss of the ability to understand the algorithm by reading it in one place. All this to avoid a goto? – Ori Pessach May 15 '09 at 14:35
  • I've run into this break/continue problem as well, sometimes wanting to continue the loop 2-3 levels up. Best thing to do IMHO is to refactor the nested loops into a method so that method can return whether to continue to parent loop. – Jason Feb 02 '10 at 20:33
  • what if the user wants to exit only the loop and not the function? – Akshay J Jun 29 '11 at 16:38
  • This is the best solution, in my opinion. It's unambiguous what happens, it's easy to control, it's easy to call, it doesn't allocate & evaluate an unnecessary boolean, and it doesn't use dangerous old practices like `goto`. Performance concerns should also be non-notable in any real-world use. – Ky - Nov 27 '17 at 21:24
  • @OriPessach if you name this method very well, then it's easy to understand what's happening. Your argument places raw assembly above Objective-C as a better language (since it puts everything in one place), which goes against the point of the question. – Ky - Nov 27 '17 at 21:25
  • @AkshayJ Then place the after-outer-loop code after this method is called – Ky - Nov 27 '17 at 21:26
9

Other than the already mentioned flag variable or goto you could throw an Objective-C exception:

@try {
  for() {
    for() {
       @throw ...
    }
  }
}
@catch{
  ...
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
lothar
  • 19,853
  • 5
  • 45
  • 59
  • 5
    Wow, just as I was about to modify my answer to point out how people will suggest every single convoluted solution except using exceptions for flow control just to avoid a goto... – Ori Pessach May 15 '09 at 14:37
  • 4
    @Ori Pessach Well Exceptions are the modern OO versions of goto ;-) – lothar May 15 '09 at 15:43
  • 1
    They do clean up the stack nicely, at least. – Ori Pessach May 15 '09 at 16:05
  • 2
    In my experiences exceptions are very very slow and should not be used to handle execution flow. – Natalie Adams May 25 '13 at 03:04
  • 3
    Do some research on exceptions in ObjC. In the Apple docs they specifically that performance hit is big and they say, "The Cocoa frameworks are generally not exception-safe. The general pattern is that exceptions are reserved for programmer error only, and the program catching such an exception should quit soon afterwards." – jpswain Sep 17 '13 at 03:28
7

Others have mentioned how you can set a flag or use a goto, but I'd recommend refactoring your code so that the inner loop is turned into a separate method. That method can then return some flag to indicate that the outer loop should break. If you name your methods appropriately, this is much more readable.

for (int i = 0; i < 10; i++) {
   if (timeToStop(i)) break;
}

-(bool) timeToStop: (int) i {
    for (int j = 0; j < 10; j++) {
        if (somethingBadHappens) return true;
    }

    return false;
}

Pseudocode, not tested, but you get the idea.

George Armhold
  • 30,824
  • 50
  • 153
  • 232
4

The break statement will only break out of the loop in scope, which is the parent loop. If you want to break out of the second loop as well you could use a boolean variable which is in scope of both loops

bool isTerminated = false;

for (...)
{
    if (!isTerminated)
    {
        for(...)
        {
            ...

            isTerminated = true;
            break;
        }
    }
    else
    {
        break;
    }
}
Nick Allen
  • 11,970
  • 11
  • 45
  • 58
2

Another solution is to factor out the second loop in a function:

int i;

for(i=0; i<10 ; i++){
    if !innerLoop(i) {
        break;
    }
}

bool innerLoop(int i)
    int j;
    for(j=0;j< 10; j++){
        doSomthing(i,j);
        if(endcondtion){
            return false;
        }
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Martijn
  • 11,964
  • 12
  • 50
  • 96
2

Probably the easiest way is to use a "flag" variable

for(i=0; i<10 && (done==false); i++)
  for(j=0;j< 10; j++){
     ..
     ..
     if(...){done=true; break;}
  }
rpetrich
  • 32,196
  • 6
  • 66
  • 89
Yogi
  • 2,460
  • 2
  • 18
  • 17
2

Change top loop's counter before break

for(i=0; i<10 ; i++)
  for(j=0;j< 10; j++){
     ..
     ..
     i = 10; 
     break;
  }
oxigen
  • 6,263
  • 3
  • 28
  • 37
1

The break statement breaks out of the innermost loop. An additional test and break statement would be needed to break out of the outer loop.

Lance Richardson
  • 4,610
  • 23
  • 30
0

Exactly like the last ones are, generally like this:

for(i=0;i<a;i++){  
 for(j=0;j<a;j++){
  if(Something_goes_wrong){
   i=a;
   break;
   }
 }
}
Martin
  • 11
0

Just for grins, how about changing this true/false check into a method and using return statements:

- (bool) checkTrueFalse: parameters{

   for ( ...) {
      for ( ... ) {

         if (...) {
            return true;
         }

      }
   }
   return false;
}
Tom Howard
  • 4,672
  • 2
  • 43
  • 48
0

If a break is executed from within a set of nested loops, only the innermost loop in which the break is executed is terminated. (Just like standard C)

Mitch Wheat
  • 295,962
  • 43
  • 465
  • 541