10

I am preparing some code:

for(int a = 1; a <= 100; a++)    //loop a (main loop)
{
    for(int b = 1000; b <= 2000; b++)    //loop b
    {
       if(b == 1555)
          break;
    }

    for(int c = 2001; c <= 3000; c++)    //loop c
    {
       .
       .
       .
    }
}

I want to break the main loop (loop variable int a) by using a break; statement in the b loop (loop variable int b).

How can I do it?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
sundowatch
  • 3,012
  • 3
  • 38
  • 66
  • 3
    If I have been correctly informed, some languages support the construct `break N` where `N` is the number of nested loops to break out from. Am I right? – Andreas Rejbrand Aug 09 '10 at 17:16
  • @And http://download-llnw.oracle.com/javase/tutorial/java/nutsandbolts/branch.html – Anycorn Aug 09 '10 at 17:19
  • 1
    @Andreas: This isn't one of them. @sundowatch: Refactor your code, not only will you solve your problem but your code is cleaner and more maintainable. – GManNickG Aug 09 '10 at 17:20
  • 1
    @Andreas Rejbrand Java gives you the ability to name loops, and extends the break statement to specify the loop from which to break. C++ does not support this, and I do not believe this is being added with C++0x – ravibhagw Aug 09 '10 at 18:36
  • Related: http://stackoverflow.com/questions/1257744/using-break-in-nested-loops – H H Aug 10 '10 at 08:45

11 Answers11

34

Use a goto.

for(int a = 1; a <= 100; a++)    //loop a (main loop)
{
    for(int b = 1000; b <= 2000; b++)    //loop b
    {
       if(b == 1555)
          goto loopDone;
    }

    for(int c = 2001; c <= 3000; c++)    //loop c
    {
       .
       .
       .
    }
}
loopDone:
Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • 14
    +1: This is exactly the extordinary condition that `goto` was left in the language for. – James Curran Aug 09 '10 at 17:17
  • @sundowatch, until you have more nested loops.... That method also uses an extra variable and has more lines of code (which means more room for typo-related bugs). – Carl Norum Aug 09 '10 at 17:20
  • 9
    I'm grabbing a box of pop corn some soda and and leaning back in the chair to watch the upcoming flame war between goto proponents and goto opponents! – Laserallan Aug 09 '10 at 17:20
  • 1
    @Carl: You think flags are going to be a source of problems with a multi-level loop? Don't ignore the terrible code mess multi-level loops are in the first place. I consider both a flag and a goto hacks around the real problem: organization. – GManNickG Aug 09 '10 at 17:23
  • 1
    Owch, and now a downvote? This is clearly a correct solution, whatever your philosophical disagreements with the technique are... – Carl Norum Aug 09 '10 at 17:30
  • 1
    This is a good method but we don't need it. Because the method in Justin Ethier's comment is much easy. – sundowatch Aug 09 '10 at 17:31
  • 2
    @James Curran - +1 as well, but there is nothing really extordinary about needing to do this. Other languages support this without resorting to gotos. C is just badly designed. For instance, Ada allows loops to be named, and the equivalent to "break" in Ada allows you to specify the name of the loop being broken out of. – T.E.D. Aug 09 '10 at 17:32
  • @T.E.D. `goto` comes in handy for grouping error logic in one spot at the end of a function, too. – Carl Norum Aug 09 '10 at 17:35
  • 2
    @Carl Norum: There are better mechanisms for that in C++. One of which is exceptions. Your answer would have been fine for C (not su much for C++) – Martin York Aug 09 '10 at 18:03
  • 1
    @Carl Norum - Yup. But the real need for it is in the situations where you truly have unstructured control flow. One example would be the implementation of a state machine. This is why lex output is filled with gotos. – T.E.D. Aug 09 '10 at 22:12
  • 3
    Make little functions and returns. As others have said in other answers: Refactor! Intricated code is bad for readability and understanding. – Stephane Rolland Aug 10 '10 at 08:44
23

Either do one of four things: use goto, use throw, use a flag, or refactor.

Many will disagree with using goto, but sometimes it's a clean solution. (Most times, it isn't, but it exists for a reason.) However, I find the use of goto warrants a refactor.

The second solution is to throw some special exception and then catch it just outside the main loop. This is an abuse of the exception system and basically a worse goto; use a goto instead of this.

The third solution would be to use a flag of some sort. This is basically a "safer" goto, but some might argue it's a bit uglier. (Especially with multiple-levels. Though in such a case your concern is how ugly your code is.)

The solution I would recommend is refactor. Whatever you're doing, it's too much. You should move the inner loops into a function, and call that function. Returning to the main loop is simply a return from that function. (In other words "My work is done.")

Community
  • 1
  • 1
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • 1
    There is nothing wrong with goto (if used correctly) This is one of these places. Its the abuse of goto that should be feared and the ability to recognize the abuse is so lacking in our higher education facilities. – Martin York Aug 09 '10 at 18:05
  • @Martin of course... but if for-loops and intricated goto's make a function long, then indeed it's really a bad idea. In this example we have three intricated for-loops and a goto... I think it is too much. My personnal view. – Stephane Rolland Aug 10 '10 at 08:49
21

I recommend refactoring your code into a function. Then you can just return from that function instead of using break:

void myFunc() 
{
    for(int a = 1; a <= 100; a++)    //loop a (main loop)
    {
        for(int b = 1000; b <= 2000; b++)    //loop b
        {
           if(b == 1555) // Logic is just an example,
              return;    // since it will always return
        }

        .
        .
        .
    }
}

This - or perhaps even a more involved refactoring of your code - should lend itself to a clean, elegant solution. Alternatively, if you just want a quick fix you could use a condition variable:

for(int a = 1; a <= 100; a++)    //loop a (main loop)
{
    bool cond = false;

    for(int b = 1000; b <= 2000; b++)    //loop b
    {
       if(b == 1555){
          cond = true;
          break;
       }
    }

    if (cond) break;

    .
    .
    .
}

Others have suggested using goto. While that is another quick fix, I strongly recommend against it, especially if you are working in a rigorous environment where the code will be peer reviewed and used for years down the road.

In my opinion the goto approach is a bit harder to maintain than a function/return refactoring, especially later on when someone else makes changes to the code. Plus you will have to justify the goto to anyone else on the team that happens to stumble onto the code.

Justin Ethier
  • 131,333
  • 52
  • 229
  • 284
  • 9
    This works for two levels, but don't scale well beyond that. This is the case where you grit your teeth and use `goto`. – James Curran Aug 09 '10 at 17:18
  • 14
    @James: Or refactor your nasty multi-level code and do neither. – GManNickG Aug 09 '10 at 17:19
  • 3
    @GMan: Agreed. My typical solution would be to put the outer loop body into a subroutine, and use "return". – T.E.D. Aug 09 '10 at 17:34
  • 4
    If you're writing multiply nested loops then extracting to a function is a much more readable alternative to a `goto`. – JoeG Aug 09 '10 at 20:46
  • 1
    @Joe I agree. [Keep functions short and with limited indentation] : Just following this simple idioms resolves tons of problems. – Stephane Rolland Aug 10 '10 at 08:40
4
for(int a = 1; a <= 100; a++)    //loop a (main loop)
{
    for(int b = 1000; b <= 2000; b++)    //loop b
    {
       if(b == 1555)
          goto end;
    }

    for(int c = 2001; c <= 3000; c++)    //loop c
    {
       .
       .
       .
    }
}
end:
adamk
  • 45,184
  • 7
  • 50
  • 57
4

The only way to head out of two such loops at a time is a goto or a throw or a return, and throw and return may not be appropriate (particularly throw, if the condition isn't exceptional). Alternately, you could set some sort of condition (bool breakout;), and keep breaking if it's true.

David Thornley
  • 56,304
  • 9
  • 91
  • 158
  • 2
    You may also use `return` if the code is refactored. Also, since `throw` raises an exception this seems like a bad practice unless the break is being made for an "exceptional" reason such as corrupt data, etc. – Justin Ethier Aug 09 '10 at 17:18
  • @Justin: Thanks - incorporated that into my answer. – David Thornley Aug 09 '10 at 17:42
4

If it is appropriate, you could make a function who's contents are the a loop, and use return.

public void bigLoop()
{
    for(int a = 1; a <= 100; a++)
    {
        for(int b = 1000; b <= 2000; b++)
        {
            if(b == 1555)
                return;
        }

        for(int c = 2001; c <= 3000; c++)
        {
            .
            .
            .
        }
    }
}//bigLoop
WillfulWizard
  • 5,340
  • 2
  • 19
  • 15
4

\ (◕ ◡ ◕) /

[]() {
    for(int a = 1; a <= 100; a++)    //loop a (main loop)
    {
        for(int b = 1000; b <= 2000; b++)    //loop b
        {
           if(b == 1555)
              return;
        }

        for(int c = 2001; c <= 3000; c++)    //loop c
        {
           .
           .
           .
        }
    }
}();
Gunslinger47
  • 7,001
  • 2
  • 21
  • 29
  • Would optimizing compilers inline lambas in cases like this? – Gunslinger47 Aug 09 '10 at 18:53
  • 1
    Typically, any modern compiler will inline all but the complex/long functions, *especially* if the function definition is visible. (Link-time optimization is relatively new, and more difficult.) With that, note the lambda's are unique hidden structs, and the full definition is always available wherever it's used. So you're pretty much guaranteed lambda's will be inlined. – GManNickG Aug 11 '10 at 07:59
3
  1. Use a goto:

    for(int a = 1; a <= 100; a++)    //loop a (main loop)
    {
        for(int b = 1000; b <= 2000; b++)    //loop b
        {
           if(b == 1555)
              goto done;
        }
        for(int c = 2001; c <= 3000; c++)    //loop c
        {
           .
           .
           .
        }
    }
    done:
    
  2. set a sentinel value tested by each loop:

    bool sentinel = true ;
    for(int a = 1; a <= 100 && sentinel ; a++)    //loop a (main loop)
    {
        for(int b = 1000; b <= 2000 && sentinel; b++)    //loop b
        {
           if(b == 1555)
              sentinel = false;
        }
        for(int c = 2001; c <= 3000 && sentinel; c++)    //loop c
        {
           .
           .
           .
        }
    }
    
tpdi
  • 34,554
  • 11
  • 80
  • 120
2

One simple strategy is to put the loop in a separate function and do a return at the selected point:

void func()
{
    for(int a = 1; a <= 100; a++)    //loop a (main loop)
    {
        for(int b = 1000; b <= 2000; b++)    //loop b
        {
           if(b == 1555)
              return;
        }

        for(int c = 2001; c <= 3000; c++)    //loop c
        {
           .
           .
           .
        }
    }
}

Any kind of result may also be returned with a return value, or with a reference parameter for the function.

  • This would be my solution. The code (three loops, two embedded in the first) is nasty enough to justify abstracting off into a subroutine anyway. – T.E.D. Aug 09 '10 at 17:36
2

The ideal way would be to re-factor your code so that you no longer need such a complicated nested-loop structure. Depending on what the rest of your code looks like, your b and c loops may be candidates for becoming individual functions, if not the entire a loop.

Since it looks like loops b and c iterate over adjacent ranges, why not combine them and reduce your loop nesting a bit?

for (int a = 1; a <= 100; a++)    //loop a (main loop)
{
    int count = 1000;
    while (count <= 3000) // combined loops 'b' and 'c'
    {
        if (count <= 2000)
        {
            // Old loop 'b' code
            if (b == 1555)
                goto fullbreak;
        }
        else
        {
            // Old loop 'c' code
            ...
        }
        count++;
    }
}
fullbreak:

You can also use a condition variable instead of the goto. If you want to break out of the old b loop but still process the old c loop, simply set count = 2001 inside the old b loop code.

Ideally, you would at least be able to re-factor this to something more like

for (int a = 1; a <= 100; a++)    //loop a (main loop)
{
    if (process_inner_loop(pass, required, args))
        break;
}

where the function process_inner_loop wraps up your original two loops and returns non-zero if you want to break out of the enclosing loop. Now, instead of using goto or condition variables, you can simply return 1;.

bta
  • 43,959
  • 6
  • 69
  • 99
1

Use this kind of pattern

for(int a = 1; a <= 100; a++)
{
    int breakMain = 0;
    for(int b = 1000; b <= 2000; b++)
    {
       if(b == 1555)
       {
           breakMain = 1;
           break;
       }
    }

    if(breakMain)
         break;

    for(int c = 2001; c <= 3000; c++)
    {
       .
       .
       .
    }
}
Bang Dao
  • 5,091
  • 1
  • 24
  • 33