Chapter 6 – Control Structures (C++)

Control Structures – General Overview

  • A C++ program is rarely a single linear sequence; instead it needs to
    • Repeat certain instructions (iteration)
    • Choose between alternative paths (selection / decision-making)
  • Constructs that provide these capabilities are collectively called control structures.
  • Every control structure ultimately manipulates the program counter (the instruction pointer) so that execution jumps forward, backward, or conditionally.
  • Introduction of control structures brings a new syntactic unit: the compound statement / block.

Compound Statements (Blocks)

  • Definition: A block is a group of C++ statements treated as one.
    • Written inside braces {}.
    • Each inner statement ends with a semicolon ; as usual.
    • Example syntax:
      cpp { statement1; statement2; statement3; }
  • Usage rules:
    • Wherever a single statement is allowed syntactically, a block can be substituted.
    • If only one instruction should run, braces are optional; if many, braces are mandatory.
  • Practical implication: Always consider braces for clarity, even for single-line bodies, to avoid logical errors when adding lines later (industry best practice).

Conditional Structure: if

  • Purpose: Execute a statement / block only if a boolean condition evaluates to true.
  • Syntax:
  if (condition)
      statement;      // may be a single instruction or a block
  • Flow (high-level algorithm):
    1. Evaluate condition.
    2. If true → run statement.
    3. Else → skip statement and continue after the if.
  • Simple example:
  if (x == 100)
      cout << "x is 100";
  • Multi-statement body example (using a block):
  if (x == 100) {
      cout << "x is ";
      cout << x;
  }
  • Complete applied program (division guard):
  #include <iostream>
  using namespace std;

  int main() {
      int dividend, divisor;
      cout << "Please enter two integers to divide:";
      cin >> dividend >> divisor;

      if (divisor != 0)
          cout << dividend << "/" << divisor << " = "
               << dividend / divisor << '\n';
      return 0;
  }
  • Demonstrates guarding against divide-by-zero error; if divisor \neq 0 the division is executed.
    • Nested if: An if inside another if to create hierarchical checks.
  if (value >= 0)
      if (value <= 10)
          cout << "In range";
  cout << "Done\n";
  • Logic: Only prints In range when 0 \leq value \leq 10.

Conditional Structure: if … else

  • Allows one action on true branch and an alternative on false branch.
  • Syntax:
  if (condition)
      statement1;   // executes when condition true
  else
      statement2;   // executes when condition false
  • Example:
  if (x == 100)
      cout << "x is 100";
  else
      cout << "x is not 100";
  • Multi-statement bodies require blocks.
  • Extended program (revisited division):
  if (divisor != 0) {
      cout << dividend << "/" << divisor << " = "
           << dividend / divisor << '\n';
  } else {
      cout << "Division by zero is not allowed\n";
  }

Conditional Structure: Nested / Cascaded if … else if … else

  • Purpose: Check multiple mutually exclusive conditions in sequence.
  • Generic syntax:
  if (cond1)
      stmt1;
  else if (cond2)
      stmt2;
  ...
  else
      stmtN;
  • Multi-category sign example:
  if (x > 0)
      cout << "x is positive";
  else if (x < 0)
      cout << "x is negative";
  else
      cout << "x is 0";
  • Full program translating integers 0 to 5 into words:
  if (value < 0)          cout << "Too small";
  else if (value == 0)    cout << "zero";
  else if (value == 1)    cout << "one";
  else if (value == 2)    cout << "two";
  else if (value == 3)    cout << "three";
  else if (value == 4)    cout << "four";
  else if (value == 5)    cout << "five";
  else                    cout << "Too large";

Iteration Structures (Loops) – Overview

  • A loop repeats a statement / block while or until a condition holds.
  • Motivation: Avoid code duplication; central for algorithms such as searching, sorting, numerical methods.
  • C++ provides three native loop constructs, each best suited for particular scenarios:
    1. for loop – counter-controlled, known iteration count.
    2. while loop – condition-controlled, unknown/variable count.
    3. do … while loop – post-test version, guarantees at least one execution.

for Loop

  • Syntax: \texttt{for (initialization; condition; update) statement;}
  • Sequence:
    1. Execute initialization once.
    2. Check condition. If false → exit loop.
    3. Execute statement (body).
    4. Execute update.
    5. Repeat from step 2.
  • Countdown example:
  for (int n = 10; n > 0; n--) {
      cout << n << ", ";
  }
  cout << "END!";
  • Output: 10,\ 9,\ 8,\ 7,\ 6,\ 5,\ 4,\ 3,\ 2,\ 1,\ END!
    • Omitted fields: Any of the three fields may be empty but semicolons remain.
  • Example: for (; n < 10; ) (no init, no update).

while Loop

  • Syntax: ```cpp
    while (expression)
    statement;
- Semantics: Entry-controlled; body executes **only if** expression is true.
- Custom countdown program:

cpp
int n;
cin >> n;
while (n > 0) {
cout << n << ", ";
--n; // crucial to avoid infinite loop
}
cout << "DONE!";

- Vital design note: Ensure something inside the loop eventually makes the condition false.

## `do … while` Loop
- Syntax:

cpp
do
statement;
while (condition);

- Semantics: Exit-controlled; body runs **at least once**.
- Echo program:

cpp
unsigned long n;
do {
cout << "Enter number (0 to end): "; cin >> n;
cout << "You entered: " << n << "\n";
} while (n != 0);

- Use-case guideline: Best when the termination condition depends on body interaction (e.g., reading user input inside the loop).

# Jump Statements
- Purpose: Alter the normal sequential flow instantly.
- Four in C++: `break`, `continue`, `return`, `goto`.

## `break`
- Immediately exits **innermost** loop or `switch`.
- Early-abort countdown:

cpp
for (int n = 10; n > 0; n--) {
cout << n << ", ";
if (n == 3) {
cout << "countdown aborted!";
break;
}
}

- Practical application: Emergency termination, searching until found, menu exit.

## `continue`
- Skips remaining body statements **only for the current iteration**, proceeds with next.
- Skipping 5 in countdown:

cpp
for (int n = 10; n > 0; n--) {
if (n == 5) continue; // jump to next iteration
cout << n << ", ";
}
cout << "DONE!";

## `goto`
- Unconditional jump to a **label** within same function.
- Example:

cpp
int n = 10;
loop:
cout << n << ", "; if (--n > 0) goto loop;
cout << "DONE!";

- Warnings / best practice:
  - Breaks structured programming;
  - Can create *spaghetti code*; avoid unless interfacing with low-level constructs or generated code.

## `return`
- Immediately exits the **current function**, optionally providing a return value.
  - In `main`, `return 0;` conventionally signals normal termination.

## `exit()` Function
- Prototype: `void exit(int exitcode);` from `<cstdlib>`.
- Terminates **entire** program, bypassing stack unwinding.
- Use exit\,(0) for normal termination; non-zero for error signalling.
- Distinction: `exit` executes static object destructors and `atexit` handlers, whereas some low-level aborts do not.

# Selective Structure: `switch`
- Designed for multi-way branching based on **integral** constant values (chars, ints, enums).
- Syntax skeleton:

cpp
switch (expression) {
case constant1:
statements1;
break;
case constant2:
statements2;
break;

default:
default_statements;
}

- Operational steps:
  1. Evaluate expression once.
  2. Compare with each `case` label in order.
  3. Jump into first matching label; execute downward until `break` or end of `switch`.
- Equivalence example (shows relation to `if-else if` chain):

cpp
switch (x) {
case 1: cout << "x is 1"; break;
case 2: cout << "x is 2"; break;
default: cout << "value of x unknown";
}
`` is semantically identical to nestedif/else if` shown earlier.

  • Fall-through behavior:
    • Omitting break lets execution continue into subsequent cases.
    • Allows grouping of labels:
      cpp switch (x) { case 1: case 2: case 3: cout << "x is 1, 2 or 3"; break; default: cout << "x is not 1, 2 nor 3"; }
  • Limitations:
    • case labels must be compile-time constants – no variables, no ranges.
    • For ranges or complex predicates, revert to if / else if.

Practical, Philosophical & Ethical Notes

  • Safety & correctness: Guarding against divide-by-zero, infinite loops, and other runtime errors is both a practical and ethical imperative (prevents crashes, data loss).
  • Maintainability: Using structured constructs (for, while, switch) rather than goto promotes readable, verifiable code – aligning with professional standards and collaborative development norms.
  • Resource considerations: Infinite loops or forgotten break statements can hog CPU cycles; responsible coding conserves shared computational resources.
  • User experience: Clear prompts and graceful exits (exit(0), informative error messages) respect users’ time and trust.
  • Historical context: Early languages relied heavily on goto; modern best practice ("structured programming" movement by Dijkstra) discourages it – foundational principle for subsequent object-oriented paradigms.

Quick Reference (Cheat Sheet)

  • \texttt{if}\,(cond) → single-branch decision
  • \texttt{if}\,(cond) … \texttt{else} → dual-branch decision
  • \texttt{if}\,…\texttt{else if}\,…\texttt{else} → multi-branch decision
  • \texttt{switch (expr)} → constant multi-branch selection, uses \texttt{case} labels & \texttt{break}
  • \texttt{for (init; cond; upd)} → counter loop
  • \texttt{while (cond)} → pre-test loop
  • \texttt{do}\,…\texttt{while (cond);} → post-test loop (executes at least once)
  • \texttt{break} → terminate loop / switch
  • \texttt{continue} → skip to next iteration
  • \texttt{return [value];} → exit function
  • \texttt{goto label;} → unconditional jump (avoid)
  • \texttt{exit(code);} → terminate program immediately (from )

Connections to Earlier / Foundational Material

  • Relies on Boolean logic (true / false) previously introduced in expressions chapter.
  • Uses operators such as ==, !=, >, < covered in relational-operator lecture.
  • Loop counters and conditions typically utilize variables, assignment, and arithmetic operations from basic C++ syntax lessons.

Real-World Relevance & Examples

  • Input validation (e.g., checking divisor \neq 0) mirrors production systems preventing crashes.
  • Menus in console apps often implemented with switch.
  • Sensor polling loops in embedded systems often require while(true) with internal break upon condition.
  • Game loops commonly exploit while for main update cycle, combined with continue for frame-skipping logic.

Common Pitfalls & Best Practices

  • Forgetting braces in nested if structures → dangling else problem.
  • Off-by-one errors in loop bounds (e.g., last iteration executed 10 times instead of 9).
  • Infinite loops due to non-mutated condition variable.
  • Neglecting break in switch causing unintended fall-through.
  • Prefer pre-increment/ decrement (++i) where semantics allow to potentially optimize.
  • Consider using range-based for in modern C++ for container iteration.

Formulas & Symbolic Reminders

  • Loop counter update: n \leftarrow n - 1 in countdown examples.
  • Guard condition pattern: \text{while}\,(condition) where condition → false eventually.
  • Division guard: divisor \neq 0 before evaluating \dfrac{dividend}{divisor}.

Summary

  • Control structures empower programs to make decisions and perform repetition, forming the backbone of algorithmic logic in C++.
  • Mastery involves understanding syntax, flow of control, and the nuanced differences between constructs so the most expressive, safe, and efficient tool is chosen for each task.