3.6. Loop Interruption: break And continue

Time: 00:02:29 | Download: Large Small | Streaming | Slides (PDF)

When we create an infinite loop - for (;;) or while (true) - we must provide an easy, efficient way to end it. (There are exceptions, but they are beyond the scope of an introductory course.) Furthermore, it's also sometimes convenient, if not necessary, to end non-infinite loops early. While we can use the exit function and return operator to interrupt loops, they have behaviors that are not always desirable. C, C++, Java, and C# all provide two operators, break; and continue;, that interrupt loops without additional, undesired behaviors.

loop
{
	loop body part 1;
	if (test)
		break or continue;
	loop body part 2;
}
Organizing loops with break and continue operators. When we use the break and continue operators in a loop, we always nest them in a branch, typically an if-statement; otherwise, any following statements, illustrated by the "loop body part 2," become unreachable. Running either operator interrupts the program's "normal" sequence of instruction execution (i.e., its flow of control). The interruption skips the statements between the operator and the end of the loop.
The logic diagram adds an if-statement and break operator to the for- and while-loop diagrams. When the if-statement test is true, and the break operator runs, the program skips any code following the break, ending the loop.
Loops and the break operator. When the program executes the break operator, it leaves the loop and skips all statements between the operator and the end of the loop. Control resumes with the first statement following the loop. Operations colored green only apply to for-loops.
The logic diagram adds an if-statement and continue operator to the for- and while-loop diagrams. When the if-statement test becomes true and the continue operator runs, the program skips the code between the continue operator and the end of the loop. The program evaluates the loop test and begins the next iteration if it is true.
Loops and the continue operator. When the program executes the continue operator, it skips the statements between the operator and the end of the loop and begins the next loop iteration, including the update statement (in the case of a for-loop) and the loop test. Operations colored green only apply to for-loops.

 

Nested Loops

The break and continue operators only affect one loop. If one loop is nested inside of another, consider two possibilities:

The break or continue is inside the outer loop but not the inner loop
In this case, the break or continue does not affect the inner loop but operates on the outer loop as described above.
The break or continue is inside the inner loop
In this case, the break or continue will affect the inner loop directly and the outer loop only indirectly. When the program runs them, they leave the inner loop, but control remains in the outer loop.

Simplifying Logic

Programmers can nest control statements (branches and loops) as deeply as needed. However, after a few levels of nesting, the indentation begins to obscure the code. Before the advent of text editor windows that could scroll horizontally, many indentation levels left little space for programmers to write code. Even with a scrolling window, the code becomes difficult to read when indented too much. More significantly, the logic of overly nested code is challenging to understand and is error-prone.

There isn't a universally accepted rule telling us how deeply we can nest logic. However, experience suggests nesting beyond three or four levels makes the code difficult to understand and maintain. When code becomes too deeply nested, programmers must find ways of simplifying or "flattening" it. Moving some of the most deeply nested code to a function is one way of "flattening" the logic, and if the code is very deeply nested, we can move more of it to other functions. But if the code isn't too deeply nested, we can sometimes use the break and continue operators to simplify it just a little.

loop
{
	...
	if (condition)
	{
		xyzzy;
	}
}
loop
{
	...
	if (!condition)
		break/continue;
	xyzzy;
}

(a)(b)
Simplifying logic. In the pseudo-code of this example, the statement xyzzy; represents an arbitrarily long and complex block of code.
  1. If the xyzzy; block of code is short and has little indentation, then this version is no worse than the next.
  2. Alternatively, if the xyzzy; block of code is long, deeply nested, or both, then this version reduces the complexity by one level.

Examples

The code fragments in the following figures don't solve any significant problem, but they do demonstrate the behavior of the break and continue operators. Examine each code fragment and the output it produces. Try to construct for yourself an explanation of how the code operates. Although the examples demonstrate the break and continue with for-loops, they work in the same way with other loops as well.

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

	cout << i << " ";
}
for (int i = 0; i < 10; i++)
{
	if (i == 5)
		continue;

	cout << i << " ";
}
0 1 2 3 4  0 1 2 3 4 6 7 8 9 
(a)(b)
Single loop interruption. In the absence of break and continue, the for-loop counts from 0 to 9 and prints the numbers separated by a space.
  1. For i = 0 through 4, i == 5 is false and the break statement does nothing. When i becomes 5, the break statement runs, interrupting the loop before the output statement. After the loop is interrupted, the program runs any code following the for-loop.
  2. This loop is similar to the previous one: For i = 0 through 4, i == 5 is false and the continue statement does nothing. When i becomes 5, the break statement runs, interrupting the loop before the output statement. However, control remains in the for-loop. The loop continues counting, only skipping 5.

 

s
for (int i = 0; i < 3; i++)
{
	if (i == 0)
		break;

	for (int j = 0; j < 4; j++)
		cout << i << "," << j << " ";
}
for (int i = 0; i < 3; i++)
{
	if (i == 0)
		continue;

	for (int j = 0; j < 4; j++)
		cout << i << "," << j << " ";
}
No output 1,0 1,1 1,2 1,3 2,0 2,1 2,2 2,3 
(a)(b)
Nested loops: interrupting the outer loop. These examples place the interrupting break and continue statements inside the outer loop, not inside the inner loop. In the absence of interrupting statements, the outer loop iterates from 0 to 2; for each iteration of the outer loop, the inner loop iterates from 0 to 3. So, without interruption, the output statement would run 3×4=12 times.
  1. The outer for-loop begins at i = 0, so i == 0 is true, and the break statement runs in the first iteration of the outer loop before the inner loop has a chance to run.
  2. As in (a), i == 0 is true during the first iteration of the outer loop, and the continue statement interrupts the outer loop before the inner loop can run and produce output. But the program continues with the next iteration of the outer loop. The outer loop completes two iterations, and for each iteration of the outer loop, the inner loop runs four times, for a total of 8 complete iterations of the inner loop.

 

for (int i = 0; i < 3; i++)
{
	cout << i << ":";
	for (int j = 0; j < 4; j++)
	{
		if (j == 2)
			break;
		cout << "," << j << " ";
	}
	cout << endl;
}
for (int i = 0; i < 3; i++)
{
	cout << i << ":";
	for (int j = 0; j < 4; j++)
	{
		if (j == 2)
			continue;
		cout << "," << j << " ";
	}
	cout << endl;
}
0:,0,1
1:,0,1
2:,0,1
0:,0,1,3
1:,0,1,3
2:,0,1,3
(a)(b)
Nested loops: interrupting the inner loop. These examples move the interrupting break and continue statements inside the inner loop. Both examples also have two output statements, one each in the outer and inner loops. In the absence of the break and continue operators, the outer loop runs from 0 to 2 and the inner loop from 0 to 3, for a total of 3*4=12 iterations of the inner loop.
  1. The outer loop runs for i = 0, which is printed to the console. The inner loop runs for j = 0 and j = 1, both of which are printed to the console. But when j = 2 the break statement interrupts the inner loop, skipping following output statement for j = 2 and j = 3. After the break statement runs, control returns to the outer loop and the process repeats for i = 1 and i = 2.
  2. This example is similar to (a), but the program skips the output statement only when j == 2, and then control returns to the iteration of the inner loop.