FirstHack Learn
Log in Sign up free

Conditionals and Loops

9 min read · 14 views

Conditionals and Loops

Every program you will write is built from three shapes: doing things in order, choosing between alternatives, and repeating something. You already have the first. This tutorial covers the other two.

Both are inherited almost unchanged from C, so if you know C loops you are mostly revising. The one genuinely new tool is the range-based for, which is worth the tutorial on its own.

Making a decision with if

An if statement runs a block only when a condition is true.

C++
#include <iostream>

int main() {
    int marks = 63;

    if (marks >= 90) {
        std::cout << "Grade: A\n";
    } else if (marks >= 75) {
        std::cout << "Grade: B\n";
    } else if (marks >= 60) {
        std::cout << "Grade: C\n";
    } else if (marks >= 40) {
        std::cout << "Grade: D\n";
    } else {
        std::cout << "Grade: F\n";
    }

    bool hasAttendance = true;
    if (marks >= 40 && hasAttendance) {
        std::cout << "Result: Pass\n";
    } else {
        std::cout << "Result: Detained or failed\n";
    }

    return 0;
}

The chain is read top to bottom and stops at the first match. That ordering matters: if you had tested marks >= 40 first, a student with 95 would be graded D, because that test would succeed before any of the higher ones were reached.

The comparison operators are ==, !=, <, >, <=, >=. The logical operators are && for and, || for or, ! for not. Both && and || are short-circuiting: in a && b, if a is false, b is never evaluated at all. This is a feature, not an optimisation detail. It lets you write a safety check on the left and a use on the right.

⚠️Always use braces

C++ lets you omit the braces when a body is a single statement. Do not. The classic bug is adding a second line later and forgetting that only the first belongs to the if. The indentation looks correct, the behaviour is not, and this reads as fine right up until it fails. Braces cost two characters.

Choosing with switch

When you are comparing one integer or character against a list of exact values, switch is clearer than a long if chain.

C++
#include <iostream>

int main() {
    char choice = 'C';

    switch (choice) {
        case 'A':
            std::cout << "Option A: view attendance\n";
            break;
        case 'B':
            std::cout << "Option B: view marks\n";
            break;
        case 'C':
        case 'c':
            std::cout << "Option C: view timetable\n";
            break;
        default:
            std::cout << "Unknown option\n";
            break;
    }

    int month = 2;
    switch (month) {
        case 1: case 3: case 5: case 7: case 8: case 10: case 12:
            std::cout << "31 days\n"; break;
        case 4: case 6: case 9: case 11:
            std::cout << "30 days\n"; break;
        case 2:
            std::cout << "28 or 29 days\n"; break;
        default:
            std::cout << "Not a month\n"; break;
    }

    return 0;
}

The break is not decoration. Without it, execution falls through into the next case and keeps going until it hits a break or the closing brace. Deliberate fall-through is occasionally useful, as with 'C' and 'c' above where two labels share one body. Accidental fall-through is one of the most common bugs in student code.

switch only works on integral types: int, char, bool, enum. You cannot switch on a std::string or a double.

Counting with for

A for loop packs three parts into one line: initialisation, a condition tested before each pass, and an update run after each pass.

C++
#include <iostream>

int main() {
    for (int i = 1; i <= 5; i++) {
        std::cout << "Pass " << i << "\n";
    }

    int sum = 0;
    for (int i = 1; i <= 10; i++) {
        sum += i;
    }
    std::cout << "Sum 1 to 10 = " << sum << "\n";

    for (int i = 10; i >= 1; i -= 2) {
        std::cout << i << " ";
    }
    std::cout << "\n";

    int n = 5, factorial = 1;
    for (int i = 2; i <= n; i++) {
        factorial *= i;
    }
    std::cout << n << "! = " << factorial << "\n";

    return 0;
}

Declaring int i inside the for header limits it to the loop. That is good practice: the counter has no meaning outside, and keeping it local means you can reuse the name in the next loop without confusion.

while and do-while

Use while when you do not know in advance how many passes are needed.

C++
#include <iostream>

int main() {
    int n = 9875;
    int digits = 0;
    int copy = n;

    while (copy > 0) {
        copy /= 10;
        digits++;
    }
    std::cout << n << " has " << digits << " digits\n";

    int reversed = 0;
    copy = n;
    while (copy > 0) {
        reversed = reversed * 10 + (copy % 10);
        copy /= 10;
    }
    std::cout << "Reversed: " << reversed << "\n";

    int countdown = 3;
    do {
        std::cout << "Tick " << countdown << "\n";
        countdown--;
    } while (countdown > 0);

    return 0;
}

do-while tests the condition after the body, so the body always runs at least once. That is the right shape for menus and for retrying invalid input, where you must ask once before you can check the answer.

The range-based for loop

This is the loop C does not have, added in C++11. When you want to visit every element of a container and do not care about index arithmetic, it says exactly that and nothing more.

C++
#include <iostream>
#include <vector>
#include <string>

int main() {
    std::vector<int> marks = {78, 65, 92, 41, 88};

    int total = 0;
    for (int m : marks) {
        total += m;
    }
    std::cout << "Total: " << total << "\n";
    std::cout << "Average: " << (total / (double)marks.size()) << "\n";

    for (int m : marks) {
        if (m < 50) {
            std::cout << m << " is below the pass mark\n";
        }
    }

    std::string word = "compiler";
    int vowels = 0;
    for (char c : word) {
        if (c=='a' || c=='e' || c=='i' || c=='o' || c=='u') {
            vowels++;
        }
    }
    std::cout << word << " has " << vowels << " vowels\n";

    return 0;
}

Read for (int m : marks) as "for each int m in marks". There is no index, so there is no chance of an off-by-one error or of running past the end. When you genuinely need the position, use an ordinary indexed for.

To modify elements rather than read copies of them, write for (int& m : marks) with an ampersand. References are covered in the next tutorial.

break and continue

break leaves the loop immediately. continue skips the rest of the current pass and jumps to the next one. Both apply to the innermost loop only. Used sparingly they make code shorter; scattered through a long loop they make it very hard to follow.

Common mistakes

  • Using = instead of == in a condition. if (x = 0) assigns and is always false. Compile with -Wall and the warning appears.
  • A semicolon after the loop header. for (int i = 0; i < 5; i++); runs an empty body five times, then runs your block once. The compiler will not complain.
  • Forgetting break in a switch. Execution falls through into the following cases.
  • A loop counter that never reaches the stopping condition. while (n != 0) n -= 2; never ends if n starts odd. Prefer n > 0.
  • Modifying the counter inside the body as well as the header. The loop then skips values in a way that is very easy to misread.
  • Comparing floating-point values with ==. 0.1 + 0.2 == 0.3 is false, because these values are stored as approximations. Compare the absolute difference against a small tolerance instead.

Conditionals and loops are where programs stop being lists of instructions and start solving problems. Write a multiplication table, a prime check, and a digit-sum program before moving on. All three appear on the Practice page.

Create a free account to track what you have finished.