Ch3.10: goto

The goto Statement

The goto statement jumps directly to a label in the same function. It does not check conditions and does not follow normal control flow.


goto label;

label:
    statement;

Labels are identifiers followed by a colon. They must appear in the same function as the goto.

Basic Example


print("A\n");
goto skip;
print("B\n");   // skipped

skip:
print("C\n");

This prints:


A
C

Forward and Backward Jumps

goto can jump forward or backward.

Forward jump


goto end;

print("never printed\n");

end:
print("done\n");

Backward jump (loop-like)


::std::uint_least32_t i{};

start:
println("i=", i);
++i;
if (i != 5) {
    goto start;   // jump back
}

Restrictions: No Jumping Into a Scope

A goto statement cannot jump into a scope where automatic variables would be skipped. This prevents using uninitialized variables.


goto label;

{
    ::std::int_least32_t x{42};   // initialization skipped
label:
    println(x);                   // illegal: x not initialized
}

Jumping out of a scope is allowed; destructors still run normally.

Restrictions: Not Allowed in constexpr Functions

goto cannot appear inside a constexpr or consteval function. These functions must have strictly structured control flow.


constexpr int f(int x) {
    if (x == 0)
        goto fail;   // error: goto not allowed in constexpr
    return x;
fail:
    return -1;
}

We will explain constexpr functions in detail in Ch7: Compile Time Programming.

This restriction is one of the motivations for the proposal to add labeled break and continue to C++: they provide structured multi-level exits that are allowed in constexpr contexts, unlike goto.

Valid Use Case: Breaking Multiple Loops

goto can exit several nested loops at once. This is sometimes clearer than using flags.


for (::std::uint_least32_t i{}; i != 3; ++i) {
    for (::std::uint_least32_t j{}; j != 3; ++j) {
        if (i == 1 && j == 1) {
            goto done;   // exit both loops
        }
        println("i=", i, ", j=", j);
    }
}

done:
println("finished");

Why goto Should Be Used Sparingly

goto makes control flow harder to follow. Modern C++ provides safer alternatives:

Use goto only when it improves clarity.

Pseudo-graph: goto


          ┌───────────────┐
          │   normal flow  │
          └───────┬───────┘
                  │
                  ├──▶ goto label ───────────────▶ label:
                  │
                  ▼
          ┌───────────────┐
          │   next stmt    │
          └───────────────┘
      

Historical Warning: Dijkstra Criticised goto

In a famous essay, computer scientist Edsger W. Dijkstra argued that goto makes programs harder to understand and reason about. His criticism was that arbitrary jumps break the structured flow of a program, making it difficult to see how control moves through the code.

This argument strongly influenced the design of modern programming languages, including C++, which encourages structured control flow using loops, if statements, and functions instead of unrestricted jumps.

Dijkstra’s critique remains relevant today: goto should be used only when it genuinely improves clarity, and avoided when structured alternatives express the intent more clearly.

Key takeaways