skip to content

Structured Programming

Control flow assembled from sequence, selection and iteration inside blocks and subroutines, argued correct with invariants. Interviewers probe it to see whether you can argue control flow correct.

on this pageshow

questions

30

In a loop that processes a list of pending items, what does break do that continue does not?

level: juniorimportance: must knowfreq 70%

answer

  1. one leaves the pass, one the loop
  2. where control lands afterwards
  3. the next condition test is the target
  4. remaining items: skipped or still processed
  5. an advance inside the body gets skipped

basics

~20 s

break ends the loop itself: control resumes at the first statement after it. continue ends only the current pass, skipping the rest of the body and jumping to the loop's next condition test, so later items are still processed.

solid answer

~40 s

Both are forward jumps out of the middle of the body, but they aim at different targets. `break` abandons the loop entirely: the condition is never tested again, control resumes after the loop, and every item still pending is left unprocessed. `continue` abandons only the current pass: the rest of the body is skipped, the condition is evaluated again, and the next item is taken. Draining a list of pending dependencies, `break` is how you stop on a fatal conflict, while `continue` is how you skip a dependency that needs no work. The trap is a `continue` in a loop whose body itself performs the step to the next item — the jump skips that step, the condition sees the same state, and the loop never makes progress.

code

pseudocode · 7 lines
pseudocode
i = 0
while i < count(pending):
    item = pending[i]
    if already_installed(item):
        continue          // back to the test with i unchanged
    install(item)
    i = i + 1             // the rest of the body - skipped by continue

go deeper

for a junior

Know the one-line difference cold and say where control lands: after the loop for one, at the next condition test for the other. This is a first-screen question and a vague answer reads as never having debugged a loop.

for a middle

Explain the skipped-advance hang from the mechanism: the jump skips the rest of the body, and the step that makes progress was in the rest of the body. Then say where you would move that step.

for a senior

Show that you audit early exits in review: every value the code after the loop reads must be set on every path out, including the one the early exit takes. That is where these bugs actually ship.

for a principal

Frame it as a readability budget the team spends: each extra exit is another condition a reader must hold in mind, so a standard worth setting is that an exit must correspond to a condition someone can name in one phrase.

## Two jumps out of the middle of a body A loop body is a block that runs top to bottom and then hands control back to the loop's condition. `break` and `continue` are both **forward jumps** that leave that block early, and the whole difference between them is where control lands. - `break` leaves the **loop**. The condition is never evaluated again; execution resumes at the first statement after the loop. Whatever was still pending is left unprocessed by that loop. - `continue` leaves the **pass**. The remainder of the body is skipped and control goes straight to the loop's next condition evaluation. The loop is still alive and the next item is taken normally. Both jump forward and outward — never backward, never into the middle of some other block. That is why disciplined control flow tolerates them at all: the reader can see where each one lands without hunting for a label somewhere else in the routine, which is precisely what an unrestricted jump denies. ## Where each one lands | | break | continue | |---|---|---| | Control resumes | at the statement after the loop | at the loop's next condition test | | Condition re-evaluated | no | yes | | Items still pending | left unprocessed | processed as usual | | Natural use | a fatal conflict ends the drain | an item that needs no work this pass | | Classic failure | code after the loop reads a value this exit never set | the jump skips the step that makes progress | ## The bug continue is famous for Picture an installer walking a list of pending dependencies by index. The body reads the item at the current index, does the work, and increments the index as its last statement. Now someone adds a fast path: if the dependency is already installed, `continue`. That jump skips **the rest of the body**, and the increment is part of the rest of the body. The condition is re-tested with the index unchanged, the same already-installed item is read, the same fast path fires, and the loop spins forever. The repair is a matter of where progress lives, not of avoiding `continue`: 1. Move the step that makes progress into the loop's own header, where no `continue` can skip it. 2. Or make the body's first act consume the item — popping from a work list rather than indexing into it — so that taking the item *is* the progress. 3. Or place the skip after the progress step, so the fast path skips only the work and not the advance. The same reasoning explains why the same shape is harmless elsewhere: in a loop that pops an item off a work list at the top of the body, a `continue` further down cannot hang the loop, because the pop already happened. ## Neither jump suspends anything A common misreading is that jumping out of the body somehow puts the loop's reasoning on hold. It does not. Whatever the loop keeps true on every pass is still true at the instant either jump fires — `continue` reaches the condition with it true, exactly as a pass that ran to the end would, and `break` carries it out of the loop along with the condition that triggered it. The code after a `break` gets a different exit condition than the code after a normal exit, and it has to be written for both. That is also why the value a loop is computing must be established on **every** path out. A search loop that assigns its result only on the path that never breaks leaves the tail reading something the break path never set. ## Why the loop uses break in the first place Some loops genuinely want to test in the middle: read an item, and only after reading it can you tell whether there is anything to process. Writing that with the test at the top forces you to duplicate the read before the loop and again at the bottom of the body — the loop-and-a-half problem. A `break` in the middle removes the duplication, at the price of an exit the reader has to account for. - Reach for `break` when a condition means the loop's whole job is over or impossible. - Reach for `continue` when this one item needs nothing, and the loop's job is unchanged. - Reach for neither when the condition is really the loop's exit condition: put it in the condition, where it is visible at the top. - Never reach for either to fake a jump backwards; that is not what they do, and a loop is already the backward jump. ## What an interviewer is listening for A candidate who answers "break exits, continue skips" has the definition. A candidate who adds *where control lands* and *what the loop still owes afterwards* has the mechanism, and is the one who will spot the skipped-advance hang in review rather than in production.

  • A loop pops an item off a work list at the top of the body and uses continue further down. Why does that one not hang?
    Because the progress step already happened before the jump. The pop removed the item from the work list, so the condition is re-tested against a smaller list and the next pass takes a different item. The hang needs a skipped advance, not a continue.
  • Why does a loop whose real test sits in the middle of the body often use break?
    Because the value the test needs is only available after part of the body has run — you must read before you can tell whether there is anything to process. Writing it with a top test duplicates that read before the loop and again at the bottom; a mid-body break removes the duplication. That is the loop-and-a-half problem.

saying these in an interview costs you the question

  • Says continue exits the loop like break, just later
  • Thinks continue skips the loop's condition test as well
  • Assumes an advance written inside the body still runs on continue
  • Believes a jump out of the body suspends what the loop keeps true
  • Treats break as returning from the whole subroutine
open as a page

When a nested block declares a name that an enclosing block already declares, what happens to references to that name inside and after that block?

level: juniorimportance: must knowfreq 62%

basics

~10 s

The inner declaration shadows the outer one: inside that block the name resolves to the new declaration, while the outer binding keeps its own value, untouched, and becomes visible again once the block ends.

open as a page

A compound condition's second operand also writes to a log — when does short-circuit evaluation skip it?

level: juniorimportance: must knowfreq 74%

basics

~20 s

Short-circuit evaluation stops as soon as the result is decided: a short-circuiting AND skips its second operand when the first is false, an OR when the first is true. The skipped operand never runs, so its log write never happens.

open as a page

Under call by value versus call by reference, what exactly does a call copy when a routine is handed a sensor record to fill?

level: juniorimportance: must knowfreq 78%

basics

~20 s

Call by value copies the argument's value into a fresh parameter slot, so assigning to the parameter cannot reach the caller. Call by reference copies the location of the caller's variable, so assigning to the parameter replaces the caller's variable.

open as a page

Go To Statement Considered Harmful is remembered as goto is evil, but what defect in jump-ridden control flow did it actually name?

level: middleimportance: must knowfreq 55%

basics

~20 s

The objection was about reasoning, not taste. With unrestricted jumps you can no longer describe where a running program has got to using a few coordinates, so a position in the text stops telling you what already happened.

open as a page

Replacing a jump out of a step sequence with a boolean flag tested by every later step — why can that be worse than the jump?

level: middleimportance: must knowfreq 46%

basics

~20 s

The jump said once where control went; the flag says it everywhere. It adds a mutable value that every later step reads, so the reader must now track extra state through the whole routine to know which statements actually run.

open as a page

An installer loop pops a dependency and may push new ones onto its work list — what argues it terminates?

level: middleimportance: must knowfreq 58%

basics

~20 s

A variant: a measure that strictly decreases on every pass and cannot fall forever. The work list's length is not one here, since a pass may push. Pair the count of never-yet-visited dependencies with the list's length and compare the pairs in order.

open as a page

A helper reads a page-width setting it never declares: under lexical scope versus dynamic scope, which declaration wins?

level: middleimportance: must knowfreq 55%

basics

~20 s

Under lexical scope the read binds to the declaration in the surrounding program text, decided before the program runs. Under dynamic scope it binds to the most recent still-active declaration on the chain of calls, so the caller decides.

open as a page

A permission check branches on allow, deny and escalate with a catch-all default — what does that default hide later?

level: middleimportance: must knowfreq 64%

basics

~20 s

A catch-all default makes any outcome added later fall silently into an existing arm, so nothing reports the missing case. Listing every outcome explicitly, with no default, turns that later addition into a loud failure at the point of change instead.

open as a page

Splitting a nightly payroll routine into basic blocks — which statements begin a new block, and why?

level: middleimportance: must knowfreq 46%

basics

~20 s

A block starts at a leader: the routine's first statement, any statement a branch can jump to, and any statement immediately after a branch. A block then runs from its leader to just before the next one, entered only at its top.

open as a page

The structured program theorem claims three control forms suffice — what does its construction add to the code?

level: middleimportance: must knowfreq 55%

basics

~20 s

It adds bookkeeping: a control variable holding which chunk of the original flowchart runs next, wrapped in one loop over a selection that dispatches on it. The rewrite preserves the computed result, not the structure a reader sees.

open as a page

What does the activation record pushed for one call to a telemetry ingest routine actually hold?

level: middleimportance: must knowfreq 60%

basics

~20 s

An activation record is the block of storage created for one call. It holds the return address, saved caller state such as the previous frame's base, the incoming parameters, the routine's locals, temporaries for part-finished expressions, and room for the result.

open as a page

A buffer declared inside a section block is stored in a document that outlives it: what ends when the block exits?

level: juniorimportance: should knowfreq 52%

basics

~20 s

Only the name's scope ends. Scope is the region of program text where a name can be used; lifetime is the stretch of run time during which the value exists, and the document's reference keeps that value alive.

open as a page

Of backward jumps, forward jumps to a shared exit, and jumps into the middle of a block, which resists structured rewrite?

level: middleimportance: should knowfreq 40%

basics

~20 s

The jump into the middle of a block resists rewrite, because it gives that block a second entry, and every structured construct has exactly one. Backward jumps become iteration and forward jumps become a guarded region; the inward jump forces the code itself to change.

open as a page

A work-list loop breaks as soon as it finds a conflict — what must hold at that exit point?

level: middleimportance: should knowfreq 46%

basics

~20 s

At that exit, the property the loop preserves on every pass together with the condition that fired the break must imply whatever the code after the loop assumes. The normal exit condition — the work list emptied — is false there, so the tail cannot rely on it.

open as a page

A running total prints zero after an edit added a declaration inside the loop block: how do you diagnose which binding is written?

level: middleimportance: should knowfreq 40%

basics

~20 s

Resolve each mention outward to its nearest declaration and compare them. The write inside the loop reached a new per-iteration binding the edit introduced, while the read after the loop still reaches the outer binding, which nothing ever wrote.

open as a page

In an ordered chain of conditional arms guarding a permission decision, what makes a later arm unreachable?

level: middleimportance: should knowfreq 44%

basics

~20 s

An arm is unreachable when every input satisfying its condition already satisfied an earlier condition in the chain, or when its own condition is self-contradictory and no input satisfies it at all. First-match selection then guarantees it never runs.

open as a page

In a straight-line stretch of a payroll routine, when may two adjacent statements be swapped safely?

level: middleimportance: should knowfreq 42%

basics

~20 s

Only when neither depends on the other's effect: neither reads what the other writes, neither writes what the other reads, and they do not both write the same place. Observable effects count as a shared place too.

open as a page

A telemetry routine returns a reference to a record held in its own frame; why is that reference invalid after the return?

level: middleimportance: should knowfreq 50%

basics

~20 s

The frame is released at the return, so the storage the reference names is no longer reserved for that record and the next call may claim it. The reference is invalid from the return onwards, even though reading through it often still succeeds.

open as a page

A firmware update routine acquires four resources in order and the fourth fails — why is a forward jump to a cleanup ladder defensible for releasing the three already held?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Because the jump is forward only, targets one label, and never enters a block partway, so it keeps the single-entry discipline while the structured alternatives either duplicate the release sequence or deepen the routine one level per resource.

open as a page

Rewriting a recursive dependency walk as a loop, what must the explicit stack hold that recursion kept implicitly?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Whatever was still to be done after each pending call came back: which item is being visited, how far through its dependencies you had got, and any partial result waiting to be combined. A call in tail position leaves nothing pending, so that rewrite needs no stack at all.

open as a page

A retry loop re-fetches an index until the fetch succeeds, with no attempt cap — can you argue it terminates?

level: seniorimportance: should knowfreq 44%

basics

~20 s

No. Nothing in the loop's own state decreases, so there is no measure to exhibit; whether it stops depends on the world outside the code. You can still show partial correctness — if it exits, the fetch succeeded — and you get termination only by introducing a bound.

open as a page

A section block opens a scratch file and then exits by raising an error: what does block-bound release guarantee about that file?

level: seniorimportance: should knowfreq 44%

basics

~20 s

Nothing, unless the release was bound to the block's exit rather than written as a trailing statement. A release bound to block exit runs on every path out, the raising one included; a trailing call is simply never reached.

open as a page

After two operands of a short-circuiting condition were swapped for speed, an audit counter stopped incrementing — why?

level: seniorimportance: should knowfreq 46%

basics

~20 s

The counter lived inside the operand that was moved to the right, where a short-circuiting operator evaluates it only when the left operand has not already decided the result. Swapping operands preserves the boolean value but not which effects happen.

open as a page

A routine that recurses once per queued sensor reading crashes on large batches; what makes deep recursion fail?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Every live call holds a whole frame, and frames are pushed into one bounded region reserved for the running thread. Depth multiplied by frame size outgrows that region, and the overrun is detected as a fault rather than handled gracefully.

open as a page

A tangled nightly payroll routine can be flattened mechanically into one loop over a state variable — when is that transformation the right call for the team?

level: principalimportance: should knowfreq 33%

basics

~20 s

Rarely as a destination, often as a scaffold. The flattening is behaviour-preserving and mechanical, which is its whole value on code nobody dares touch, but it moves the structure into a variable, so it must be followed by extracting and naming real regions.

open as a page

In a grammar where a conditional arm may be a single statement, which conditional does a trailing else attach to?

level: middleimportance: nice to knowfreq 28%

basics

~10 s

By the conventional resolution of the dangling-else ambiguity, a trailing else binds to the nearest preceding conditional that does not already have one — the inner one. The grammar decides this, not the indentation.

open as a page

Under copy-restore parameter passing, when does the result differ from call by reference for the same call?

level: middleimportance: nice to knowfreq 24%

basics

~20 s

They diverge whenever a second route reaches the same storage during the call. Copy-restore works on a private copy and writes back once at the return, so aliased writes are lost or overwritten and timing differs; call by reference makes every write land immediately.

open as a page

In a control-flow graph, what makes a region single-entry/single-exit, and what does that buy you?

level: seniorimportance: nice to knowfreq 28%

basics

~20 s

A region is single-entry/single-exit when every edge into it from outside lands on one node and every edge out of it leaves from one node. It can then be collapsed to a single node and reasoned about as one statement.

open as a page

You own a codebase of tangled legacy routines — what standard would you set for when an unstructured jump may remain, and how would you enforce it?

level: principalimportance: nice to knowfreq 28%

basics

~20 s

Write the rule about the defect, not the keyword: permit forward-only jumps to a single top-level label inside one routine, refuse backward and inward ones, and rewrite on touch behind characterisation tests rather than in one sweep.

open as a page