Skip to Content
Course content

25: break, continue, and goto

Click on the "Edit" button in the top corner of the screen to edit your slide content.

By now, you're comfortable with for and while loops. They work great when the logic is simple: "do this ten times" or "do this until the user hits Q." But in the real world, data is messy. You'll often find yourself in a loop where you need to skip a specific iteration because the data is corrupt, or kill the loop entirely because you've found what you were looking for and there's no point in wasting CPU cycles on the remaining ten thousand elements.

Fighting the indentation pyramid

Let's say we're processing a buffer of sensor readings. Some readings are valid, but others are "noise" (marked as -999) that we need to ignore. A beginner's instinct is to wrap the entire logic of the loop inside an if statement. It looks something like this:

for (int i = 0; i < 100; i++) {
    if (readings[i] != -999) {
        // Imagine 20 lines of complex math here
        double normalized = (readings[i] - offset) * scale;
        sum += normalized;
        count++;
        // ... more logic ...
    }
}

This isn't "wrong," but as your logic grows, you end up with a "pyramid of doom"—deeply indented code that's hard to read. This is where continue saves your sanity. Instead of saying "if this is valid, do everything," we say "if this is noise, skip it immediately."

for (int i = 0; i < 100; i++) {
    if (readings[i] == -999) continue;

    // The "happy path" stays at the top level of indentation
    double normalized = (readings[i] - offset) * scale;
    sum += normalized;
    count++;
}

I call this the "guard clause" pattern. You handle the edge cases or the "junk" data first and get them out of the way. It keeps the core logic of your loop clean and readable.

Killing the sentinel flag

Now, imagine you're searching that same array for a specific "critical" value. Once you find it, you're done. The naive approach is to add a boolean flag to your loop condition.

bool found = false;
int target_index = -1;

while (i < 100 && !found) {
    if (readings[i] == CRITICAL_VALUE) {
        target_index = i;
        found = true;
    }
    i++;
}

This works, but it's clunky. You're forcing the while loop to evaluate that !found expression on every single iteration, and you've introduced an extra variable that only exists to manage the loop. break is the surgical tool here. It tells the CPU: "I'm done here, get me out of this loop right now."

for (int i = 0; i < 100; i++) {
    if (readings[i] == CRITICAL_VALUE) {
        target_index = i;
        break; 
    }
}

It's more direct and more honest about what the code is doing. You aren't "waiting for a condition to become false"; you are actively terminating the process.

The necessary evil of the emergency exit

Finally, we have goto. You've probably been told since day one of programming that goto is evil and leads to "spaghetti code." For the most part, that's true. If you use it to jump backward or skip around randomly, you're creating a maintenance nightmare.

However, there is one specific scenario where goto is actually the cleanest tool in C: breaking out of deeply nested loops. Imagine you're searching a 2D grid for a specific coordinate. If you find it in the inner loop, a break only gets you out of the inner loop. You're still trapped in the outer loop.

The "safe" way is to use another flag, but then you're checking that flag in every single level of the nest. It gets ugly fast. In these cases, a goto to a label immediately following the nested structure is the professional's choice.

for (int x = 0; x < width; x++) {
    for (int y = 0; y < height; y++) {
        if (grid[x][y] == TARGET) {
            found_x = x;
            found_y = y;
            goto found_it; // The emergency exit
        }
    }
}

found_it:
printf("Target found at %d, %d\n", found_x, found_y);

I only use goto for two things: breaking out of nested loops and jumping to a centralized cleanup section at the end of a function (to avoid repeating free() calls). If you use it for anything else, you're playing with fire.




📋 Practical Task

Exercise: The Corrupt Packet Scanner

You are writing a driver for a network device. You receive a 2D array of bytes (representing multiple packets). Each packet is a row in the array.

Your goal is to scan the data and find the first occurrence of a "Magic Byte" (0xAA). However, there are rules:

  • If you encounter a "Noise Byte" (0x00), you must skip that specific byte and move to the next one.
  • If you encounter an "Error Byte" (0xFF), the entire packet is corrupted. You should stop scanning that packet immediately and move to the next packet.
  • The moment you find the "Magic Byte" (0xAA), you should stop scanning entirely (across all packets) and print the coordinates (row and column) where it was found.

Requirements: Implement this using a 2D array of unsigned char. You must use continue to handle noise, break to handle packet errors, and goto to exit the nested loops once the Magic Byte is found.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.