Programming in C++/Java/Python

Chapter 4
Selection, Iteration, and Program Flow

Diploma in Software Engineering
Programming in C++/Java/Python   •   Diploma

Selection, Iteration, and Program Flow

Introduction

A program is not merely a list of instructions executed from top to bottom. Useful software must make decisions, repeat operations, respond to changing data, and terminate safely. These capabilities are provided by program-flow statements. In C++, Java, and Python, the principal flow-control mechanisms are Boolean expressions, conditional selection, pattern or switch selection, iteration, nesting, scope control, and transfer statements.

This chapter develops these mechanisms from first principles and applies them to structured programming problems. Although the three languages use different syntax, their underlying logic is similar. A condition evaluates to either true or false; a selection statement chooses among alternatives; and a loop repeats a block while a controlling condition permits it.

Learning Objectives

By the end of this chapter, you should be able to:

  • construct and evaluate Boolean expressions;
  • use if, else if, and else correctly;
  • apply switch in C++ and Java and match in Python where appropriate;
  • design while, do-while, and for loops;
  • use nested selection and iteration safely;
  • explain block scope and variable lifetime;
  • apply break, continue, and controlled early return;
  • identify infinite loops, off-by-one errors, and unreachable code;
  • select an appropriate flow-control structure for a practical problem;
  • test programs containing alternative paths and repeated execution.

1. Boolean Logic: The Foundation of Decisions

A Boolean expression is an expression whose result is true or false. In C++, Java, and Python, relational operators compare values:

OperatorMeaning
==equal to
!=not equal to
<less than
>greater than
<=less than or equal to
>=greater than or equal to

Logical operators combine Boolean expressions. C++ and Java use && for AND, || for OR, and ! for NOT. Python uses and, or, and not.

For two propositions P and Q, conjunction is true only when both are true, while disjunction is true when at least one is true. Negation reverses a proposition's truth value.

PQP AND QP OR Q
falsefalsefalsefalse
falsetruefalsetrue
truefalsefalsetrue
truetruetruetrue

Short-circuit evaluation

In an AND expression, evaluation can stop as soon as a false operand is found. In an OR expression, evaluation can stop as soon as a true operand is found. This is called short-circuit evaluation. It can improve efficiency and prevent errors:

if denominator != 0 and numerator / denominator > 2:
    print("Condition satisfied")

The division is attempted only if denominator != 0 is true. Without short-circuit protection, the program could attempt division by zero.

Precedence and clarity

Operators are evaluated according to precedence rules. Parentheses should be used when they improve clarity or prevent misunderstanding:

if ((age >= 18 && hasId) || isStaff) {
    allowEntry();
}

A common error is confusing assignment with comparison. In C++, = assigns a value, whereas == compares values. Java also distinguishes these operators. Python uses = for assignment and == for comparison.

2. Conditional Selection

The if statement

An if statement executes a block only when its condition is true.

if (temperature > 30) {
    cout << "Hot day";
}

Equivalent Java code is:

if (temperature > 30) {
    System.out.println("Hot day");
}

Python uses indentation rather than braces:

if temperature > 30:
    print("Hot day")

The body of a conditional should be clearly delimited. In C++ and Java, braces are recommended even for a one-statement body because they reduce errors when statements are later added.

Two-way selection

An if-else statement selects exactly one of two alternatives.

if mark >= 50:
    result = "Pass"
else:
    result = "Fail"

The condition is evaluated once. If it is true, the first block executes; otherwise, the second block executes.

Multi-way selection

An if-else if-else chain is appropriate when conditions represent ranges or complex tests.

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

The order matters. Once a true condition is found, later conditions are skipped. Conditions should normally be arranged from the most restrictive or highest range to the lowest. If mark >= 50 appeared first, every passing mark would incorrectly receive the same result.

Guard clauses

A guard clause handles an invalid or exceptional condition immediately and returns from a function. This reduces nesting:

def calculate_average(total, count):
    if count <= 0:
        return None
    return total / count

Guard clauses are useful for input validation, but they should not conceal important cleanup or bypass required security checks.

3. Pattern and Switch Selection

When a program selects an action based on one discrete value, a switch-style structure can be clearer than a long conditional chain.

C++ and Java switch

switch (choice) {
    case 1:
        cout << "Create";
        break;
    case 2:
        cout << "Search";
        break;
    case 3:
        cout << "Exit";
        break;
    default:
        cout << "Invalid choice";
}

The break statement prevents execution from continuing into the next case. Omitting it may cause fall-through, which is sometimes deliberate but is often a defect. default handles values that match no listed case.

Modern Java also provides switch expressions, and modern C++ provides enhanced forms of selection, but compatibility depends on the compiler and language version. Code intended for teaching or deployment should state its required version.

Python match

Python 3.10 introduced structural pattern matching:

match command:
    case "start":
        begin_service()
    case "stop":
        stop_service()
    case _:
        print("Unknown command")

The underscore is a wildcard pattern. Python's match can match structures, not merely constants, and therefore differs conceptually from a simple C-style switch.

Choosing between conditional and switch-style selection

Use if when conditions involve ranges, unrelated Boolean expressions, or complex relationships. Use switch or simple match when one expression is compared with several discrete alternatives. The objective is readability and correct coverage, not merely shorter code.

4. Iteration and Loop Design

A loop repeats a block of statements. Every reliable loop has three conceptual components:

  1. Initialization establishes the starting state.
  2. Continuation condition determines whether another iteration is allowed.
  3. Progress or update moves the program toward termination.

If progress does not eventually make the condition false, the loop may be infinite.

while loop

A while loop is a pre-test loop: its condition is checked before each iteration. It may execute zero times.

int count = 1;
while (count <= 5) {
    cout << count << " ";
    count++;
}

The output is 1 2 3 4 5. The variable count is initialized before the loop and updated inside it.

Python syntax is similar:

count = 1
while count <= 5:
    print(count)
    count += 1

do-while loop

C++ and Java provide do-while, a post-test loop. Its body executes at least once.

do {
    choice = readChoice();
} while (choice < 1 || choice > 3);

This is appropriate for menu input when the user must be prompted at least once. Python has no built-in do-while statement; the same behavior can be created using while True and a controlled break.

while True:
    choice = int(input("Choice: "))
    if 1 <= choice <= 3:
        break

for loop

A for loop is useful when iteration is controlled by a counter or a collection.

for (int i = 0; i < 5; i++) {
    System.out.println(i);
}

This prints values from 0 through 4. The upper bound is exclusive because the continuation condition is i < 5.

Python's for loop iterates over items:

for i in range(5):
    print(i)

range(5) produces 0, 1, 2, 3, and 4. With range(start, stop, step), the stop value remains exclusive.

Sentinel-controlled iteration

A sentinel is a special value that signals completion and is not treated as ordinary data.

total = 0
while True:
    value = int(input("Enter value, -1 to finish: "))
    if value == -1:
        break
    total += value

The sentinel must be chosen so that it cannot be confused with valid data, or the input design must explicitly document that limitation.

Counting and accumulation

A counter records how many times an event occurs; an accumulator combines values.

passed = 0
sum_marks = 0
for mark in marks:
    sum_marks += mark
    if mark >= 50:
        passed += 1

If marks contains n values, the average is calculated as:

average = summarks ÷ n

Here, sum_marks is the total of all marks and n is the number of marks. The formula is applicable only when n > 0; otherwise division by zero must be prevented.

5. Nested Control Structures

A control structure placed inside another is nested. For example, a nested loop can process rows and columns of a table:

for row in range(3):
    for column in range(4):
        print(row, column)

The inner loop completes all four iterations for each outer-loop iteration, producing 3 × 4 = 12 pairs. Nested structures are useful for matrices, grids, timetables, and search problems, but deep nesting can reduce readability and increase the possibility of errors.

A nested conditional might validate a user before checking a requested operation:

if (authenticated) {
    if (hasPermission) {
        performOperation();
    } else {
        deny("Insufficient permission");
    }
} else {
    deny("Authentication required");
}

Security-sensitive conditions should be explicit. A program must not treat authentication and authorization as interchangeable concepts.

6. Scope and Lifetime

Scope is the region of source code in which a name can be accessed. A variable declared inside a block normally has block scope.

int value = 10;
if (value > 0) {
    int messageCode = 1;
    cout << messageCode;
}
// messageCode is unavailable here

Java follows similar block-scope rules. Python has function and module scope, but an if block does not create a separate local scope in the same way. Nevertheless, excessive reuse of names can make Python programs difficult to understand.

A variable should be declared in the narrowest scope that meets its purpose. This reduces accidental modification and makes dependencies easier to identify. Shadowing—declaring an inner variable with the same name as an outer variable—can be legal but confusing and should generally be avoided.

Lifetime describes how long a variable exists. A local variable commonly exists during execution of its function or block, while an object managed by a language runtime may remain alive while references to it exist. Scope and lifetime are related but not identical.

7. Flow-Control Transfer

break

break exits the nearest loop or switch. It is useful when the answer has been found or a sentinel is encountered.

for item in items:
    if item == target:
        found = True
        break

continue

continue skips the remainder of the current iteration and begins the next iteration.

for number in range(1, 11):
    if number % 2 == 0:
        continue
    print(number)

This prints odd numbers. Excessive use of continue can make the control path harder to follow, so a positively structured condition may sometimes be clearer.

return

return ends a function and optionally supplies a value. It is often the cleanest way to report a result or reject invalid input. Functions should have predictable return behavior; mixing unrelated return types without documentation makes calling code fragile.

Exceptions

Exceptions are appropriate for abnormal events that cannot be handled conveniently through ordinary conditions, such as a failed file operation or invalid conversion. They should not replace ordinary validation in every case. A program should catch only exceptions it can meaningfully handle and should not silently discard error information.

8. Common Errors and Testing Strategies

Off-by-one errors

An off-by-one error occurs when a loop executes one time too many or too few. Carefully distinguish < from <=, and document whether a bound is inclusive or exclusive.

Accidental infinite loops

Typical causes include a missing update, an update in the wrong direction, or a condition that can never become false. A loop invariant—the property expected to remain true during iteration—helps reason about correctness.

Dangling else

In nested C++ or Java conditionals without braces, an else attaches to the nearest unmatched if. Braces eliminate this ambiguity.

Unreachable or incomplete branches

Every relevant input category should be covered. A default branch, validation condition, or explicit error response can prevent silent failure.

Testing flow control

Tests should include:

  • the smallest valid input;
  • the largest valid input;
  • values at each boundary;
  • invalid input;
  • empty collections;
  • one-item collections;
  • repeated values;
  • conditions that cause zero loop iterations;
  • conditions that cause exactly one iteration;
  • conditions that exercise every branch.

Code coverage can show which branches were executed, but coverage alone does not prove correctness. Assertions and expected-output tests are also required.

9. Worked Example: A Menu-Based Grade Processor

Suppose a training centre needs a program that repeatedly accepts marks, counts passes and failures, and displays a report when the user chooses to finish. A mark is valid only when it lies from 0 through 100 inclusive.

passed = 0
failed = 0
total = 0
count = 0

while True:
    print("1. Enter mark")
    print("2. Display report")
    print("3. Exit")
    choice = input("Choice: ")

    if choice == "1":
        mark = int(input("Mark: "))
        if mark < 0 or mark > 100:
            print("Invalid mark")
            continue

        total += mark
        count += 1
        if mark >= 50:
            passed += 1
        else:
            failed += 1

    elif choice == "2":
        if count == 0:
            print("No marks entered")
        else:
            average = total / count
            print("Average:", average)
            print("Passed:", passed)
            print("Failed:", failed)

    elif choice == "3":
        break

    else:
        print("Unknown choice")

Analysis

The outer while True creates a menu that continues until break. The first selection validates the mark. The continue statement prevents invalid data from reaching the accumulation logic. The second selection uses a guard condition to prevent division by zero. The third selection terminates the loop. The final branch handles unexpected menu input.

The average formula is valid only when count > 0. The program maintains the invariant that count equals the number of valid marks represented by total, passed, and failed. This invariant helps detect programming errors during testing.

10. Applied Case: Library Self-Service Kiosk

Background

A library wants a kiosk through which a member can search for a book, borrow it, or return it. The system must reject unauthenticated users and prevent borrowing when the member has reached the loan limit.

Problem

The kiosk must repeatedly display operations, validate input, and handle different book and member states. A poorly designed program might allow an unauthorized user to reach borrowing code or might continue processing after an invalid identifier.

Analysis and approach

A suitable design uses:

  1. a guard clause for authentication;
  2. a loop for repeated menu interaction;
  3. switch-style selection for fixed commands;
  4. nested conditions for availability and loan limits;
  5. explicit termination through an exit command.

Illustrative pseudocode is:

if authentication fails:
    display error
    stop

repeat:
    display menu
    read command

    if command is SEARCH:
        read title
        display matching books
    else if command is BORROW:
        read book identifier
        if book does not exist:
            display error
        else if book is unavailable:
            display error
        else if member has reached loan limit:
            display error
        else:
            create loan record
    else if command is RETURN:
        process return
    else if command is EXIT:
        stop
    else:
        display invalid command

Outcome and learning points

The design separates authentication, command selection, validation, and business action. This makes the code easier to test and reduces the chance that a failed validation will fall through to an operation. In a real system, authorization decisions must be enforced on the server or protected application layer, not only in a kiosk interface. Audit logs should record borrowing and return actions, while personal data should be protected according to applicable privacy requirements.

11. Professional Practice and Contemporary Considerations

Readable control flow is a professional quality attribute. Use meaningful names, consistent indentation, small functions, and comments that explain intent rather than restating syntax. Prefer structured alternatives to deeply nested or unrestricted jumps. Static analysis tools can identify unreachable branches, suspicious conditions, missing break statements, and some possible infinite loops.

AI-assisted programming tools can suggest loops and conditions, but generated code must be reviewed. Common AI-generated defects include incorrect boundary conditions, assumptions about input formats, and missing authorization checks. Developers remain responsible for testing, security, licensing compliance, and correctness.

Flow-control choices also have ethical consequences. A validation branch that rejects users unfairly, mishandles accessibility requirements, or silently loses data is not merely a technical defect. Programs should provide clear feedback, preserve user control, and fail safely. In safety-critical or financially significant systems, requirements should specify exceptional behavior and independent testing should verify it.

Chapter Summary

Program flow determines how software responds to data and events. Boolean expressions provide the true-or-false decisions required by selection and iteration. if structures are suitable for ranges and complex conditions, while switch or match is effective for discrete alternatives. while, do-while, and for loops support different repetition patterns, and every loop requires initialization, a continuation condition, and progress toward termination.

Nested structures solve multidimensional and staged problems but should be controlled for readability. Scope limits where names can be used, while lifetime describes how long values exist. break, continue, return, and exceptions provide controlled transfer, but they should be applied deliberately. Professional programs validate boundaries, prevent infinite loops, test every branch, protect authorization decisions, and use clear, maintainable structure.

Key Terms

Boolean expression: An expression whose result is true or false.

Condition: A Boolean test that controls program execution.

Selection: Choosing one execution path from alternatives.

Iteration: Repeated execution of a block of statements.

Short-circuit evaluation: Stopping Boolean evaluation when the result is already known.

Guard clause: An early condition that handles invalid or exceptional input.

Switch: A multi-way selection structure based on discrete values.

Pattern matching: Selection based on the form or value of data.

Loop: A control structure that repeats statements.

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

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

Sentinel: A special value that signals the end of input.

Counter: A variable used to record occurrences or iterations.

Accumulator: A variable that progressively combines values.

Nesting: Placing one control structure inside another.

Scope: The region in which a name can be accessed.

Lifetime: The period during which a variable or object exists.

Fall-through: Continuing from one switch case into another without a terminating statement.

Loop invariant: A property that remains true during loop execution.

Off-by-one error: A boundary error causing one extra or one missing iteration.

Review Questions

Short Answer Questions

  1. What is a Boolean expression, and why is it important in program flow?
  2. Distinguish between a pre-test loop and a post-test loop.
  3. What is the purpose of break in a switch statement?
  4. Explain the difference between scope and lifetime.
  5. Why must a program guard against division by zero when calculating an average?

Long Answer Questions

  1. Compare if-else, switch, and Python match selection. State when each is most appropriate.
  2. Explain the three components of a reliable loop and illustrate each with code.
  3. Discuss common causes of infinite loops and off-by-one errors, including testing methods.
  4. Explain nesting, guard clauses, and scope in the design of a menu-driven program.
  5. Describe how authorization, validation, and error handling should be implemented in a self-service application.

Analytical / Application Questions

  1. Design a program that reads positive integers until a sentinel is entered and reports the count, total, and average. Identify the condition needed to avoid division by zero.
  2. A loop is intended to print integers from 1 through 10 but prints only 1 through 9. Diagnose the likely condition error and propose a correction.
  3. Design the control flow for an examination system that accepts marks, rejects invalid values, assigns grades, and reports the average. Identify at least five boundary tests.

Multiple Choice Questions with Answers

Question 1

Which operator represents logical AND in Java?

A. ||

B. &&

C. !

D. ==

Correct Answer: B. &&

Question 2

Which loop is guaranteed to execute its body at least once in C++?

A. while

B. for

C. do-while

D. range

Correct Answer: C. do-while

Question 3

What is the purpose of a sentinel value?

A. To increase loop speed

B. To signal the end of input

C. To declare a variable

D. To select a class

Correct Answer: B. To signal the end of input

Question 4

What prevents division by zero when calculating an average?

A. Checking that the count is greater than zero

B. Using a switch statement

C. Using continue after division

D. Declaring the total as a string

Correct Answer: A. Checking that the count is greater than zero

Question 5

What commonly happens in a C++ switch when break is omitted?

A. The program always terminates

B. The next case may execute

C. The condition becomes false

D. The switch becomes a loop

Correct Answer: B. The next case may execute

Question 6

What does continue do inside a loop?

A. Exits every enclosing function

B. Repeats the current statement indefinitely

C. Skips to the next iteration

D. Terminates the program

Correct Answer: C. Skips to the next iteration

Question 7

How many pairs are produced by a loop with 3 outer iterations and 4 complete inner iterations?

A. 7

B. 12

C. 16

D. 24

Correct Answer: B. 12

Question 8

Which structure is generally most suitable for checking whether a mark lies within several ranges?

A. if-else-if chain

B. switch with no cases

C. break statement

D. try statement only

Correct Answer: A. if-else-if chain

Question 9

What is an off-by-one error?

A. A syntax error in a variable name

B. A loop boundary error causing one extra or missing iteration

C. A failure to import a library

D. A memory allocation method

Correct Answer: B. A loop boundary error causing one extra or missing iteration

Question 10

Why should variables usually be declared in the narrowest practical scope?

A. To increase accidental modification

B. To make dependencies and access easier to control

C. To guarantee infinite loops

D. To eliminate all Boolean expressions

Correct Answer: B. To make dependencies and access easier to control

Programming in C++/Java/Python
Chapter 4 of 15