Control flow is the order in which statements execute. A branch chooses a path; a loop repeats work; a function gives a named operation its own parameters and result. Together they let you turn a problem statement into small, testable decisions.
Suppose a learner enters several scores. We need to reject scores outside zero through one hundred, count passing scores and calculate an average. Reading input, checking its meaning and calculating the report are separate responsibilities. Keeping those boundaries clear makes the same calculation reusable in a terminal tool, a test or a larger application.
Branches and repetition
if executes its body when a condition converts to true. An else if adds another test, and else handles the remaining case. Use braces even for one statement so a later edit cannot accidentally place work outside the branch. == compares values; = assigns them. Confusing these operators can produce a condition that compiles but changes your data.
&& and || short-circuit: their right operand is evaluated only when necessary. That makes a condition such as count != 0 && total / count > 50 safe from division by zero, assuming the division has no other range problem.
Use a for loop for a known counting pattern, a range-based for to visit a collection, and while when progress depends on a condition. break exits the nearest loop; continue starts its next iteration. Every loop needs an explanation of what makes it finish. A switch handles discrete alternatives; cases fall through unless control leaves the case, usually with break or return.
A complete score report
Save as scores.cpp. std::array is a fixed-size standard-library collection; the next lessons introduce growable collections.
#include <array>
#include <iostream>
bool valid_score(int score) {
return score >= 0 && score <= 100;
}
bool passed(int score, int threshold) {
return score >= threshold;
}
double average(int total, int count) {
return static_cast<double>(total) / count;
}
int main() {
constexpr int count{4};
constexpr int pass_mark{60};
std::array<int, count> scores{};
for (int& score : scores) {
if (!(std::cin >> score)) {
std::cerr << "Expected four integer scores\n";
return 1;
}
if (!valid_score(score)) {
std::cerr << "Score must be between 0 and 100\n";
return 1;
}
}
int total{};
int passing{};
for (int score : scores) {
total += score;
if (passed(score, pass_mark)) {
++passing;
}
}
std::cout << "Average: " << average(total, count) << '\n';
std::cout << "Passed: " << passing << '\n';
}
clang++ -std=c++20 -Wall -Wextra -Wpedantic -g scores.cpp -o scores
printf '50 60 70 80\n' | ./scores
The output is Average: 65 and Passed: 3 on separate lines. Input 50 60 101 80 prints the range error and exits unsuccessfully. Input containing hello where a score is expected prints the integer-input error. Formatted extraction reads integers, not an entire validated document; this program consumes four scores and does not reject additional tokens afterward.
Make function contracts visible
bool passed(int score, int threshold) names the result and each input type. These parameters are passed by value: the function receives its own copies. A declaration must be visible before a call; placing definitions above main satisfies that requirement here. A void function performs work without returning a value.
A precondition is something the caller must establish. average requires a positive count; this program passes the constant four. Its sum cannot exceed four hundred, so int is sufficient on every conforming implementation. A general-purpose version would need an explicit empty-input policy and a range analysis. A postcondition describes the promised result when the preconditions hold.
Prefer returning a calculated value to printing inside the calculation. A caller can then display it, compare it in a test or use it in another operation. Non-void functions must return a value on every path that reaches their end, apart from special rules such as those for main.
Practice and solution guidance
- Add a
gradefunction returning a letter for score bands. Guidance: test boundaries in descending order, including exactly sixty and one hundred. State that the function expects a validated score. - Reject trailing input after the fourth score. Guidance: attempt to read another token after consuming whitespace, and distinguish ordinary end-of-input from a real stream failure.
- Change the report to count failures too. Guidance: either derive failures from total minus passing or check that independently maintained counts always add up to four.

