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:
while,for,do whilebreakandcontinueswitch- functions and early returns
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
- goto jumps to a label: no conditions, no checks.
- Labels must be in the same function.
- No jumping into a scope: cannot bypass variable initialization.
- goto is forbidden in constexpr: structured control flow required.
- Motivation for labeled break: constexpr-safe multi-level exits.
- Use sparingly: prefer structured control flow.
- Dijkstra’s warning: unrestricted jumps harm readability and make reasoning about code harder.