# Loop (statement)

A loop statement is a control-flow construct in a programming language that executes a statement or block repeatedly under the control of a condition, an iterator, or a counter, without the programmer writing the repeated text more than once.

| Key fact | Detail |
|---|---|
| Condition timing | A while loop tests its condition before each execution of the body; a do loop tests after each execution, so a do body always runs at least once<sup>[1](https://eel.is/c++draft/stmt.iter)</sup><sup> • </sup><sup>[2](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/iteration-statements)</sup> |
| Infinite loops in C | Since C11, an endless loop with no observable behavior (I/O, volatile accesses, atomic or synchronization operations) is undefined behavior, except when the controlling expression is a constant expression such as `while(true)`<sup>[3](https://cppreference.com/c/language/while)</sup> |
| C++26 change | A "trivial infinite loop" (a trivially empty iteration statement whose controlling expression is a constant expression evaluating to true) is defined; its statement is replaced with a call to `std::this_thread::yield`<sup>[1](https://eel.is/c++draft/stmt.iter)</sup> |
| Exit constructs | Early exits vary by language: break (C, C++, Java), exit (Ada), last (Perl), with labeled and multi-level forms such as Java's labeled break<sup>[4](https://xavierleroy.org/CdF/2023-2024/1.pdf)</sup><sup> • </sup><sup>[5](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration)</sup> |
| Compiler treatment | In one measured case, a C++ range-for loop compiled to exactly the same code as a hand-written pointer-based while loop<sup>[6](https://slides.xania.org/202512/08-going-loopy)</sup> |
| Measured optimization gains | Vectorization-aware unrolling reached up to 2x speedup over -O3 builds on 17 TSVC kernels and a 1.06x geometric-mean speedup on FreeBench and MiBench<sup>[7](https://dl.acm.org/doi/10.1145/3377555.3377890)</sup> |
| Verification requirements | Proving a loop totally correct requires an invariant (initiation and consecution) plus a loop variant, a value from a well-founded set that decreases each iteration, for termination<sup>[8](https://se.inf.ethz.ch/~meyer/publications/methodology/invariants.pdf)</sup> |

## Kinds of loop statements

Imperative languages offer a small family of loop forms that differ in when the continuation condition is evaluated. In the C++ while statement, the substatement is executed repeatedly until the condition becomes false, with the test taking place before each execution of the substatement<sup>[1](https://eel.is/c++draft/stmt.iter)</sup>; C defines the same pre-test semantics, with the controlling expression compared against zero<sup>[3](https://cppreference.com/c/language/while)</sup>. In the do statement, the substatement is executed repeatedly until the expression becomes false, tested after each execution<sup>[1](https://eel.is/c++draft/stmt.iter)</sup>. C# states the distinction directly: the do statement conditionally executes its body one or more times, while the while statement conditionally executes its body zero or more times<sup>[2](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/iteration-statements)</sup>.

The counted for loop places initialization, condition, and update in one header. In C, the cond-expression is evaluated before the loop body and the iteration-expression after it; if the cond-expression evaluates to zero the loop is exited immediately<sup>[9](https://cppreference.com/c/language/for)</sup>. Leaving out the cond-expression causes a non-zero integer constant to be substituted in its place, producing an infinite loop; `for(;;)` therefore never terminates.<sup>[9](https://cppreference.com/c/language/for)</sup>

Foreach-style loops iterate over collections rather than counters. Python's for statement iterates over the elements of a sequence or other iterable object, creating an iterator and assigning each item to the target list<sup>[10](https://docs.python.org/3/reference/compound_stmts.html)</sup>. Rust's for expression loops over elements provided by an implementation of `std::iter::IntoIterator`<sup>[11](https://doc.rust-lang.org/stable/reference/expressions/loop-expr.html)</sup>, and C#'s foreach enumerates the elements of a collection and executes its body for each element<sup>[2](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/iteration-statements)</sup>. Rust rounds out the family with four loop expressions: loop (infinite), while (predicate), for (iterator-based), and labeled block expressions<sup>[11](https://doc.rust-lang.org/stable/reference/expressions/loop-expr.html)</sup>.

The forms are not equally expressible. The do-while loop is not reducible to while-do without duplicating the body or adding a Boolean variable, but it is reducible to while-do plus break<sup>[4](https://xavierleroy.org/CdF/2023-2024/1.pdf)</sup>. In 1974, Kosaraju demonstrated that loops permitting early exits form a language that cannot be reduced to one using only ordinary loops, and likewise that a language allowing exits at level N+1 cannot be reduced to one permitting exits at level N or below.<sup>[12](https://xavierleroy.org/control-structures/book/main005.html)</sup>

## Loop control and exit mechanisms

In C, break terminates a while loop and continue shortcuts to the next iteration<sup>[3](https://cppreference.com/c/language/while)</sup>. In [JavaScript](https://www.edgepedia.org/javascript), break without a label terminates the innermost enclosing while, do-while, for, or switch immediately and transfers control to the following statement, while break with a label terminates the specified labeled statement<sup>[5](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration)</sup>. Rust's labeled break and continue can target an enclosing loop other than the innermost one<sup>[11](https://doc.rust-lang.org/stable/reference/expressions/loop-expr.html)</sup>.

Naming differs across languages: exit from the enclosing loop is break (C, C++, Java), exit (Ada), or last (Perl), with continue or next for skipping an iteration, and multi-level exits such as break N in Shell or labeled loops in Java<sup>[4](https://xavierleroy.org/CdF/2023-2024/1.pdf)</sup>.

Python adds a distinctive construct: the while statement's optional else clause runs when the expression becomes false, and a break statement executed in the first suite terminates the loop without executing the else clause's suite<sup>[10](https://docs.python.org/3/reference/compound_stmts.html)</sup>.

## How compilers optimize loops

**Invariant hoisting.** Loop-invariant code motion is a form of partial redundancy elimination whose purpose is to find code in a loop body that produces the same value in every iteration of the loop<sup>[13](https://lfcps.org/course/Compilers11/17-loopinv.pdf)</sup>. A computation is loop invariant if its value does not change as long as control stays within the loop, and code motion moves such a statement to the loop preheader<sup>[14](https://www.cs.cmu.edu/afs/cs/academic/class/15745-f03/public/lectures/L7_handouts.pdf)</sup>. An expression d = a ⊕ b qualifies when a and b are numerical constants or are defined outside the loop, meaning all reaching definitions lie outside it<sup>[13](https://lfcps.org/course/Compilers11/17-loopinv.pdf)</sup>. In SSA form, a side-effect-free loop-invariant computation can be moved to a block before the loop when doing so preserves program behavior, making the required dataflow analysis trivial<sup>[13](https://lfcps.org/course/Compilers11/17-loopinv.pdf)</sup>. If the loop may not be entered, compilers guard the hoisted code with an extra basic block so the computation is not needlessly performed<sup>[13](https://lfcps.org/course/Compilers11/17-loopinv.pdf)</sup>.

**Unrolling and vectorization.** LLVM's loop vectorizer increases instruction-level parallelism by partially unrolling loops so multiple execution units can be utilized<sup>[15](https://releases.llvm.org/23.1.0/docs/Vectorizers.html)</sup>. The vectorizer uses a cost model to decide when unrolling is profitable, weighing register pressure and generated code size<sup>[15](https://releases.llvm.org/23.1.0/docs/Vectorizers.html)</sup>. When trip counts do not divide evenly by the vectorization and unroll factors, a scalar remainder (epilogue) loop executes the tail iterations, and epilogue vectorization helps small trip-count loops stay in vector code<sup>[15](https://releases.llvm.org/23.1.0/docs/Vectorizers.html)</sup>.

**Canonicalization.** In one measured case, a C++ range-for loop compiled to exactly the same code as a hand-written pointer-based while loop, because the compiler canonicalized the range-for into identical assembly<sup>[6](https://slides.xania.org/202512/08-going-loopy)</sup>.

## By the numbers

Measured results show what coordinated [loop optimization](https://www.edgepedia.org/loop-optimization) is worth. The VALU (vectorization-aware loop unrolling) heuristic, evaluated on a production compiler, reaches up to 2x speedup over already highly optimized -O3 builds on 17 kernels of the TSVC benchmark suite<sup>[7](https://dl.acm.org/doi/10.1145/3377555.3377890)</sup>. Across full benchmarks from FreeBench and MiBench, VALU yields a geometric-mean speedup of 1.06x by coordinating unrolling with SLP vectorization<sup>[7](https://dl.acm.org/doi/10.1145/3377555.3377890)</sup>. The underlying gap it addresses is real: most production compilers apply unrolling and SLP vectorization independently, tuning unrolling to avoid code bloat rather than to maximize vectorization, which leads to missed vectorization opportunities<sup>[7](https://dl.acm.org/doi/10.1145/3377555.3377890)</sup>.

Compiler choice also matters. A machine-learning study that characterizes loop nests by hardware performance counters found headroom for state-of-the-art compilers ranging from 1.10x to 1.42x for serial auto-vectorized code and from 1.30x to 1.71x for auto-parallelized code, meaning no single compiler is uniformly best<sup>[16](https://ar5iv.labs.arxiv.org/html/1902.00603)</sup>. Unroll-factor selection itself can be learned: an ensemble-learning approach trained on almost 2,500 loops from 72 benchmarks estimates unroll factors for new loops<sup>[17](https://www.iieta.org/journals/ijcmem/paper/10.18280/ijcmem.120308)</sup>. Compile-time prediction has limits, since ill-structured loops with conditional control flow inside their bodies and variable-dependent loops are difficult to unroll at compile time and require profiling to establish iteration-count predictability<sup>[18](https://ece.northeastern.edu/groups/nucar/publications/wwc4.pdf)</sup>.

## Loops, recursion, and goto

A loop statement differs from recursion in who manages the repetition. Dijkstra observed in EWD215 that repetition clauses such as while B repeat A and repeat A until B are logically superfluous, because repetition can be expressed with recursive procedures, but he kept them for realism: induction lets programmers retain intellectual grasp on the processes they generate<sup>[19](https://www.cs.utexas.edu/~EWD/transcriptions/EWD02xx/EWD215.html)</sup>.

The historical alternative was goto-based looping. FORTRAN (1957) provided counted DO loops with a labeled continuation line, such as DO 100 I = 1, N, compiled efficiently with the test at the end of the loop and loop unrolling<sup>[4](https://xavierleroy.org/CdF/2023-2024/1.pdf)</sup>. Algol 60 (1960) introduced structured counted loops, for i := e1 step e2 until e3 do s, and general loops, while be do s<sup>[4](https://xavierleroy.org/CdF/2023-2024/1.pdf)</sup>. Dijkstra's 1968 letter to Communications of the ACM, sent without a title (the editors added "Go to statement considered harmful"), argued that unbridled use of the go to statement makes it terribly hard to find a meaningful set of coordinates in which to describe process progress<sup>[12](https://xavierleroy.org/control-structures/book/main005.html)</sup><sup> • </sup><sup>[19](https://www.cs.utexas.edu/~EWD/transcriptions/EWD02xx/EWD215.html)</sup>. Knuth's 1974 survey, *Structured Programming with go to Statements*, found that eliminating goto can reduce code clarity or decrease performance when it forces code duplication or Boolean flags<sup>[12](https://xavierleroy.org/control-structures/book/main005.html)</sup>.

Structured loops won on theoretical and practical grounds. A theorem establishes that every unstructured program—and, put another way, every flowchart—can be matched by a structured program built from a single while loop plus one additional integer variable.<sup>[4](https://xavierleroy.org/CdF/2023-2024/1.pdf)</sup> FORTRAN and BASIC, which featured goto statements and counted loops, later gained control structures in the Algol mold — FORTRAN 77 introduced if...else, and FORTRAN 90 added do while<sup>[4](https://xavierleroy.org/CdF/2023-2024/1.pdf)</sup>; moreover, IBM's 1977 structured-programming manual for FORTRAN put forward the DOWHILE as the construct for iterating over code as long as a condition holds<sup>[20](http://ftpmirror.your.org/pub/misc/bitsavers/pdf/ibm/370/fortran/GC20-1790-0_An_Introduction_to_Structured_Programming_in_FORTRAN_197707.pdf)</sup>. Some later languages omit goto entirely: Modula-2 (1980), Eiffel (1986), Python (1991), and Java (1995)<sup>[4](https://xavierleroy.org/CdF/2023-2024/1.pdf)</sup>.

## Correctness, termination, and verification

A loop invariant is a logical proposition valid at the head of a given loop irrespective of the iteration, used to establish properties that validate the corresponding Hoare triple<sup>[21](https://arxiv.org/pdf/2311.07948)</sup>. In the classic Hoare-style rule, two obligations must be discharged: the initiation property, {P} Init {Inv}, stating that initialization ensures the invariant, and the consecution (or inductiveness) property, {Inv ∧ ¬Exit} Body {Inv}, stating that the body preserves the invariant whenever the exit condition does not hold<sup>[8](https://se.inf.ethz.ch/~meyer/publications/methodology/invariants.pdf)</sup>.

Termination is demonstrated independently by means of a loop variant — an element of some well-founded set, typically the natural numbers, that grows smaller with every pass through the loop<sup>[8](https://se.inf.ethz.ch/~meyer/publications/methodology/invariants.pdf)</sup>. Full automation is impossible in general: it is an undecidable question whether a fragment of a loop body has the same effect with each iteration, so compilers and verifiers use conservative approximations<sup>[13](https://lfcps.org/course/Compilers11/17-loopinv.pdf)</sup>.

Infinite loops have defined semantics only within limits. Under C rules, an infinite loop makes a program's behavior undefined when neither its statement nor its expression performs anything observable—such as input/output, volatile accesses, or atomic and synchronization operations—anywhere within it; loops whose controlling expression is a constant expression are exempt, meaning `while(true)` remains an endless loop in all cases<sup>[3](https://cppreference.com/c/language/while)</sup>. C++ similarly makes behavior undefined if a loop without observable behavior does not terminate, except for a trivial infinite loop, allowing compilers to remove such loops<sup>[1](https://eel.is/c++draft/stmt.iter)</sup>. Rust types the boundary explicitly: a loop expression without an associated break expression is diverging and has type `!`<sup>[11](https://doc.rust-lang.org/stable/reference/expressions/loop-expr.html)</sup>.

## History and recent developments

The loop statement's history runs from FORTRAN's labeled DO through Algol 60's structured for and while<sup>[4](https://xavierleroy.org/CdF/2023-2024/1.pdf)</sup> to the iterator-based foreach forms of modern languages and Python's async for, which allows convenient iteration over asynchronous iterables<sup>[10](https://docs.python.org/3/reference/compound_stmts.html)</sup>.

Several changes since 2023 have reshaped loop syntax and semantics. Rust stabilized let chains in the 2024 edition, allowing &&-chained let statements inside if and while conditions, intermixed with boolean expressions, with both irrefutable and refutable patterns<sup>[22](https://github.com/rust-lang/rust/pull/132833)</sup>; this generalizes the while let loop, which the language defines as equivalent to a loop expression containing a match expression<sup>[11](https://doc.rust-lang.org/stable/reference/expressions/loop-expr.html)</sup>. C++ has relaxed its range-based for loop in steps: starting with C++17, the begin-expr and the end-expr need no longer share a type, and the end-expr in particular need not be an iterator<sup>[23](https://en.cppreference.com/cpp/language/range-for)</sup>; starting with C++23, temporaries created in the range-initializer keep their lifetime only when they would otherwise be destroyed at that initializer's end<sup>[23](https://en.cppreference.com/cpp/language/range-for)</sup>. C++26 defines the trivial infinite loop, a trivially empty iteration statement whose converted controlling expression is a constant expression evaluating to true, and specifies that its statement is replaced with a call to `std::this_thread::yield`, with implementation-defined behavior on freestanding implementations<sup>[1](https://eel.is/c++draft/stmt.iter)</sup>.

## References

1. [[stmt.iter] Iteration statements, C++ working draft](https://eel.is/c++draft/stmt.iter)
2. [Iteration statements, C# language reference, Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/iteration-statements)
3. [while loop, cppreference.com (C)](https://cppreference.com/c/language/while)
4. [Xavier Leroy, The birth of control structures: from 'goto' to structured programming, Collège de France course notes](https://xavierleroy.org/CdF/2023-2024/1.pdf)
5. [Loops and iteration, MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration)
6. [Matt Godbolt, Going loopy](https://slides.xania.org/202512/08-going-loopy)
7. [Vectorization-aware loop unrolling with seed forwarding, CC 2020, ACM](https://dl.acm.org/doi/10.1145/3377555.3377890)
8. [Bertrand Meyer, Loop invariants: analysis, classification, and examples, ETH Zurich](https://se.inf.ethz.ch/~meyer/publications/methodology/invariants.pdf)
9. [for loop, cppreference.com (C)](https://cppreference.com/c/language/for)
10. [Compound statements, Python 3 documentation](https://docs.python.org/3/reference/compound_stmts.html)
11. [Loop expressions, The Rust Reference](https://doc.rust-lang.org/stable/reference/expressions/loop-expr.html)
12. [Xavier Leroy, Structured programming and the goto controversy](https://xavierleroy.org/control-structures/book/main005.html)
13. [Lecture notes on loop-invariant code motion](https://lfcps.org/course/Compilers11/17-loopinv.pdf)
14. [CMU 15-745 lecture: loop-invariant computations and code motion](https://www.cs.cmu.edu/afs/cs/academic/class/15745-f03/public/lectures/L7_handouts.pdf)
15. [Auto-Vectorization in LLVM, LLVM documentation](https://releases.llvm.org/23.1.0/docs/Vectorizers.html)
16. [Towards an Achievable Performance for the Loop Nests, arXiv](https://ar5iv.labs.arxiv.org/html/1902.00603)
17. [Optimizing Program Efficiency by Predicting Loop Unroll Factors Using Ensemble Learning, IJCMEM](https://www.iieta.org/journals/ijcmem/paper/10.18280/ijcmem.120308)
18. [Runtime Predictability of Loops, NUCAR, Northeastern University](https://ece.northeastern.edu/groups/nucar/publications/wwc4.pdf)
19. [E. W. Dijkstra, EWD215: A Case against the GO TO Statement (1968)](https://www.cs.utexas.edu/~EWD/transcriptions/EWD02xx/EWD215.html)
20. [IBM, An Introduction to Structured Programming in FORTRAN (July 1977)](http://ftpmirror.your.org/pub/misc/bitsavers/pdf/ibm/370/fortran/GC20-1790-0_An_Introduction_to_Structured_Programming_in_FORTRAN_197707.pdf)
21. [Loop invariants and Hoare-triple validity, arXiv](https://arxiv.org/pdf/2311.07948)
22. [Stabilize let chains in the 2024 edition, Rust PR 132833](https://github.com/rust-lang/rust/pull/132833)
23. [Range-based for loop (since C++11), cppreference.com](https://en.cppreference.com/cpp/language/range-for)

---
*Topic: Encyclopedia › Technology and the built world › Computing and digital systems › Software and programming › Programming languages › Programming language concepts*

*Initially written Sep 17, 2026 · Reviewed: Oct 15, 2026 · Edited: Oct 15, 2026 · Last review: Oct 15, 2026*

*Copyright 2026 EdgeChat AI, a subsidiary of Biostate AI.*

License: Edgepedia Community License 1.0, https://www.edgepedia.org/edgepedia/license
