Fundamentals of Programming (C/C++)

Chapter 4
Decision and Iteration Structures

Bachelors Program in Computer Applications
Fundamentals of Programming (C/C++)   •   Bachelor

Decision and Iteration Structures

Introduction

Programs rarely execute every instruction in a fixed linear order. A useful program must choose among alternatives, repeat actions, validate input, and stop when a goal has been achieved. In C and C++, these behaviours are expressed through control-flow structures. This chapter develops the principles and practices of selection and repetition, with emphasis on correctness, readability, testing, and structured programming.

A control-flow statement changes the order in which statements execute. The principal structures are:

  • Sequence: statements execute from top to bottom.
  • Selection: one of several alternatives is chosen.
  • Iteration: a block executes repeatedly while a condition permits.

These structures can be combined and nested, but every program should have a clear control-flow design.

Learning Objectives

After studying this chapter, you should be able to:

  1. Explain Boolean expressions and relational and logical operators.
  2. Implement decisions using if, if-else, nested conditions, and switch.
  3. Use the conditional operator for simple value selection.
  4. Design for, while, and do-while loops.
  5. Apply counters, sentinels, validation, nesting, and loop invariants.
  6. Identify and prevent infinite loops, off-by-one errors, and unintended fall-through.
  7. Select control structures appropriate to a problem and justify the choice.

Boolean Expressions and Conditions

A condition is an expression whose value determines control flow. In C and C++, zero represents false and any nonzero value represents true. Nevertheless, modern C++ programs should generally use the bool type and write conditions that are directly understandable.

Relational operators compare values: <, <=, >, >=, ==, and !=. Logical operators combine conditions: && means logical AND, || means logical OR, and ! means logical NOT.

For A && B, the result is true only when both operands are true. For A || B, it is true when at least one operand is true. C and C++ use short-circuit evaluation: in A && B, B is not evaluated if A is false; in A || B, B is not evaluated if A is true. This can prevent invalid operations, as in:

if (denominator != 0 && numerator / denominator > 2) {
    // Safe because division is attempted only when denominator is nonzero.
}

Operator precedence affects interpretation. Relational operators have higher precedence than &&, and && has higher precedence than ||. Parentheses are recommended whenever they improve clarity.

A common error is confusing assignment with equality:

if (x = 5)      // assignment; usually an error
if (x == 5)     // comparison

Floating-point values should not normally be compared for exact equality because representation and rounding may differ. A tolerance-based test is more reliable:

#include <cmath>

if (std::abs(a - b) < 0.000001) {
    // Treat a and b as approximately equal.
}

The tolerance must be selected according to the scale and precision requirements of the application.

Selection Structures

The if Statement

The simplest selection structure executes a statement or block only when a condition is true.

if (temperature > 35) {
    std::cout << "Heat warning\n";
}

Braces should be used even for a single statement. They reduce maintenance errors when statements are later added.

The if-else Statement

An if-else selects exactly one of two alternatives.

if (marks >= 40) {
    std::cout << "Pass\n";
} else {
    std::cout << "Fail\n";
}

For several mutually exclusive ranges, an else-if chain is appropriate:

if (marks >= 80) {
    grade = 'A';
} else if (marks >= 70) {
    grade = 'B';
} else if (marks >= 60) {
    grade = 'C';
} else if (marks >= 40) {
    grade = 'D';
} else {
    grade = 'F';
}

The order matters. Since a mark of 85 also satisfies marks >= 40, the most restrictive test must appear first. Conditions should ideally be exhaustive and mutually exclusive in meaning.

Nested Decisions

A decision may contain another decision. Nesting is useful when a second test is meaningful only after the first succeeds.

if (age >= 18) {
    if (hasIdentityCard) {
        std::cout << "Entry permitted\n";
    } else {
        std::cout << "Identity document required\n";
    }
} else {
    std::cout << "Entry restricted by age\n";
}

Deep nesting can make logic difficult to understand. Conditions can often be combined, or early returns can be used in functions, to reduce indentation. The objective is not the fewest lines but the clearest logic.

The switch Statement

switch is suited to selecting among discrete constant values of an integral, character, or enumeration expression.

switch (choice) {
    case 1:
        std::cout << "Add\n";
        break;
    case 2:
        std::cout << "Remove\n";
        break;
    case 3:
        std::cout << "Display\n";
        break;
    default:
        std::cout << "Invalid choice\n";
}

The break statement exits the switch. Omitting it causes fall-through, meaning execution continues into the next case. Fall-through can be intentional when several cases share an action, but it should be documented. The default branch handles values not explicitly listed.

Unlike an if chain, switch does not directly express ranges such as “between 60 and 70.” It is most readable for menu commands, state codes, days, or enumerated categories.

Conditional Operator

The conditional operator ?: is a compact expression for choosing one of two values:

char result = (marks >= 40) ? 'P' : 'F';

Its general form is condition ? value_if_true : value_if_false. It should be used for simple value selection, not for long or deeply nested actions. Excessive use reduces readability.

Iteration Structures

Iteration repeats a block while a controlling condition remains satisfied. Every sound loop design identifies:

  1. Initialization: the starting state.
  2. Continuation condition: when another iteration is allowed.
  3. Progress: how the state changes.
  4. Termination: why the condition eventually becomes false.

The while Loop

A while loop is a pre-test loop. Its condition is tested before each iteration, so the body may execute zero times.

int count = 1;
while (count <= 5) {
    std::cout << count << ' ';
    ++count;
}

The initialization occurs before the loop and progress occurs inside it. If count were never incremented, the loop could be infinite.

A while loop is suitable when the number of iterations is not known in advance, such as reading input until a sentinel or processing records until end-of-file.

The do-while Loop

A do-while loop is a post-test loop. Its body executes at least once.

int choice;
do {
    std::cout << "Enter 1 to continue, 0 to stop: ";
    std::cin >> choice;
} while (choice != 0);

The semicolon after the closing parenthesis is required. This structure is useful for menus and input prompts that must appear before validation.

The for Loop

A for loop places initialization, condition, and update in one header:

for (int i = 0; i < 10; ++i) {
    std::cout << i << '\n';
}

Its conceptual order is:

  1. Execute initialization once.
  2. Test the condition.
  3. Execute the body if true.
  4. Execute the update.
  5. Return to step 2.

The variable i declared in the header normally has loop scope. A for loop is generally preferable when the iteration pattern is count-controlled or range-like. C++ also supports range-based for loops:

for (int value : values) {
    std::cout << value << '\n';
}

Use a reference, such as const int& value, when avoiding copies is important and modification is not required.

Loop Patterns and Invariants

Counter-Controlled Loops

A counter loop performs a known number of repetitions. To print integers from 1 through n, the condition should be i <= n when i begins at 1. Replacing it with i < n creates an off-by-one error.

Sentinel-Controlled Loops

A sentinel is a special value signalling termination. The sentinel must not be processed as ordinary data.

int value;
int total = 0;
std::cin >> value;
while (value != -1) {
    total += value;
    std::cin >> value;
}

The sentinel must be outside the valid data domain. If negative values are valid data, -1 would be a poor sentinel.

Accumulation and Counting

An accumulator stores a running result; a counter records how many times an event occurs.

int sum = 0;
int positives = 0;
for (int i = 0; i < 5; ++i) {
    int x;
    std::cin >> x;
    sum += x;
    if (x > 0) {
        ++positives;
    }
}

Initialization is essential. An uninitialized accumulator has an indeterminate value and can produce undefined or unpredictable results.

Loop Invariants

A loop invariant is a property that remains true at a defined point during every iteration. In the following summation loop, before each iteration with counter i, sum equals the sum of all values already processed:

int sum = 0;
for (int i = 0; i < n; ++i) {
    sum += values[i];
}

The invariant is established because no values have been processed initially. It is preserved because one new value is added each iteration. When the loop terminates, i == n, so all n values have been included. This reasoning supports both correctness and debugging.

A complete termination argument also requires progress. If i increases toward n, and i is bounded above by n, termination follows for finite n.

Nested Loops and Control Statements

A loop may contain another loop. Nested loops are useful for tables, matrices, and repeated combinations.

for (int row = 1; row <= 3; ++row) {
    for (int column = 1; column <= 4; ++column) {
        std::cout << row * column << '\t';
    }
    std::cout << '\n';
}

The inner loop completes all its iterations for each outer-loop iteration. If the outer loop executes r times and the inner loop executes c times per outer iteration, the body executes r × c times, assuming fixed bounds.

break exits the nearest enclosing loop or switch. continue skips the remainder of the current loop iteration and proceeds to the next condition test or update. These statements are useful but should not obscure the loop’s normal termination logic.

Input Validation and Defensive Control Flow

Validation ensures that input satisfies required conditions before it is used. A robust validation loop reports the problem and gives the user another opportunity:

int age;
std::cin >> age;
while (age < 0 || age > 120) {
    std::cout << "Enter an age from 0 to 120: ";
    std::cin >> age;
}

Real programs must also handle failed extraction, not merely out-of-range values:

int age;
while (!(std::cin >> age) || age < 0 || age > 120) {
    std::cout << "Invalid input. Try again: ";
    std::cin.clear();
    std::cin.ignore(1000, '\n');
}

The exact recovery limit should reflect application requirements. In production systems, unbounded or carefully bounded input handling is preferable to assuming that a fixed number of characters is sufficient.

Validation should occur at trust boundaries, including user interfaces, files, networks, and inter-process communication. It improves reliability and may reduce security risks, although validation alone is not a complete security strategy.

Worked Application: Electricity Billing

Consider an illustrative billing program. The first 100 units cost 5 currency units each, the next 100 cost 7 each, and units beyond 200 cost 10 each. A fixed service charge of 50 is added. The input must be nonnegative.

#include <iostream>

int main() {
    int units;
    std::cin >> units;

    while (units < 0) {
        std::cout << "Units cannot be negative. Enter again: ";
        std::cin >> units;
    }

    double energyCharge;
    if (units <= 100) {
        energyCharge = units * 5.0;
    } else if (units <= 200) {
        energyCharge = 100 * 5.0 + (units - 100) * 7.0;
    } else {
        energyCharge = 100 * 5.0 + 100 * 7.0
                     + (units - 200) * 10.0;
    }

    double total = energyCharge + 50.0;
    std::cout << "Total: " << total << '\n';
}

For 250 units, the energy charge is 100 × 5 + 100 × 7 + 50 × 10 = 1900, and the total is 1950 currency units. The branches are mutually exclusive and collectively cover every nonnegative integer input. The validation loop establishes the precondition required by the billing logic.

Choosing and Evaluating Control Structures

RequirementAppropriate structure
One optional actionif
Two alternativesif-else
Ordered ranges or complex conditionselse-if chain
Discrete constant choicesswitch
Simple value selectionConditional operator
Known counter patternfor
Unknown repetitions, pre-testwhile
Body must execute oncedo-while

Good control flow follows several principles:

  • Make conditions explicit and meaningful.
  • Use braces consistently.
  • Keep loop initialization, condition, and progress easy to locate.
  • Avoid deeply nested logic where guard clauses or functions improve clarity.
  • Avoid changing a loop counter unpredictably inside its body.
  • Use switch defaults and validate external input.
  • Test boundary values: zero, minimum, maximum, and just-inside or just-outside each range.
  • Prefer simple control flow over clever compression.

Challenges and Limitations

Control structures do not automatically guarantee correctness. A condition can be logically wrong even when the syntax is valid. Integer overflow may invalidate calculations used in conditions. Floating-point comparisons can be unstable. A loop may terminate mathematically but still be impractical when its running time is large. Nested loops can produce quadratic or higher growth in work.

A program that accepts input must also distinguish syntactic validity, such as successfully reading an integer, from semantic validity, such as the integer representing an allowed age. In safety-critical or financial software, requirements should define all boundary cases, error responses, audit needs, and acceptable failure modes.

Contemporary C++ Practice

Modern C++ encourages range-based loops, strongly typed enumerations, standard algorithms, and clear Boolean expressions. For example, enum class avoids accidental conversion between unrelated menu codes. Library algorithms such as std::find, std::count, and std::all_of can express common iteration tasks and reduce manual loop errors. Nevertheless, explicit loops remain essential for learning, custom processing, and situations where the algorithm’s control must be visible.

Automated tools, including static analyzers and AI coding assistants, can suggest conditions or identify suspicious loops, but they cannot replace requirements analysis, testing, or human review. Developers must verify generated code for boundary errors, input assumptions, portability, and security.

Chapter Summary

Decision structures select actions based on Boolean conditions. if and else-if support arbitrary conditions and ranges; switch is effective for discrete constant alternatives; and the conditional operator provides compact two-value selection. Iteration structures repeat work: for is suited to count-controlled repetition, while to pre-test repetition with an uncertain count, and do-while when at least one execution is required. Reliable loops require initialization, a correct continuation condition, progress, and a termination argument. Counters, accumulators, sentinels, nested loops, invariants, and validation are fundamental programming patterns. Structured control flow, boundary testing, readable conditions, and defensive input handling improve reliability and maintainability.

Key Terms

Boolean expression: An expression whose value represents true or false.

Selection: Choosing one control-flow path from alternatives.

Iteration: Repeated execution of a statement or block.

Short-circuit evaluation: Stopping logical evaluation once the result is known.

Nested statement: A statement placed inside another control structure.

Fall-through: Continuing from one switch case into the next case.

Conditional operator: The ?: operator for selecting one of two expressions.

Counter: A variable used to track repetitions or occurrences.

Accumulator: A variable that stores a running total or result.

Sentinel: A special value that signals termination.

Pre-test loop: A loop whose condition is checked before its body.

Post-test loop: A loop whose condition is checked after its body.

Loop invariant: A property preserved at a specified point in every iteration.

Off-by-one error: An error caused by an incorrect boundary or iteration count.

Validation: Checking that input satisfies syntactic and semantic requirements.

Infinite loop: A loop whose termination condition never becomes false.

Guard clause: An early condition used to reject or handle an exceptional case.

Structured programming: Designing control flow with clear sequence, selection, and iteration.

Review Questions

Short Answer Questions

  1. What is the difference between while and do-while?
  2. Why is break commonly used in a switch statement?
  3. Define a loop invariant.
  4. What is a sentinel value?
  5. Why should braces generally be used with if statements?

Long Answer Questions

  1. Explain the syntax, execution order, and appropriate applications of for, while, and do-while loops.
  2. Compare an if-else-if chain with a switch statement, including their limitations.
  3. Explain short-circuit evaluation and show how it can prevent an invalid operation.
  4. Describe how initialization, progress, and a termination argument establish loop correctness.
  5. Discuss input validation, including the difference between failed input conversion and out-of-range input.

Analytical / Application Questions

  1. Design a program that reads examination marks until a sentinel is entered, validates the range, and reports the highest mark and average. State the invariant for the accumulation loop.
  2. A loop is intended to print numbers from 1 through 100 but prints only 1 through 99. Identify the likely boundary error and propose two correct loop headers.
  3. Design a menu-driven program using do-while and switch. Explain how it handles invalid choices, terminates, and avoids unintended fall-through.

Multiple Choice Questions with Answers

Question 1

Which structure is most appropriate when a loop must execute at least once?

A. for

B. while

C. do-while

D. switch

Correct Answer: C. do-while

Question 2

What does short-circuit evaluation guarantee for A && B when A is false?

A. B is always evaluated

B. B is not evaluated

C. A is assigned false

D. The expression becomes an integer

Correct Answer: B. B is not evaluated

Question 3

What is the main purpose of break in a switch case?

A. Repeat the case

B. Skip the condition

C. Exit the switch

D. Restart the program

Correct Answer: C. Exit the switch

Question 4

Which loop component ensures that a counter-controlled loop moves toward termination?

A. Initialization

B. Progress or update

C. Output formatting

D. Variable declaration only

Correct Answer: B. Progress or update

Question 5

What is the output category assigned when marks are 75 in the shown grading chain?

A. A

B. B

C. C

D. D

Correct Answer: B. B

Question 6

For the electricity example, what is the energy charge for 250 units?

A. 1250

B. 1700

C. 1900

D. 1950

Correct Answer: C. 1900

Question 7

Which statement best describes a sentinel?

A. A variable that stores a running sum

B. A special value that signals termination

C. A condition that is always true

D. A case label in every loop

Correct Answer: B. A special value that signals termination

Question 8

What is an off-by-one error?

A. A syntax error in a header file

B. An incorrect boundary causing one iteration too many or too few

C. A failed input conversion

D. A missing switch default

Correct Answer: B. An incorrect boundary causing one iteration too many or too few

Question 9

Which structure is most suitable for selecting among fixed menu codes such as 1, 2, and 3?

A. switch

B. do-while only

C. Nested accumulator

D. Range-based loop

Correct Answer: A. switch

Question 10

If a loop processes each of 10 elements exactly once, which invariant is most relevant?

A. The counter eventually becomes negative

B. The processed portion is correctly represented before each iteration

C. The input is always a sentinel

D. The loop body never changes state

Correct Answer: B. The processed portion is correctly represented before each iteration

Fundamentals of Programming (C/C++)
Chapter 4 of 15