Programmed before? Try these 6 questions and skip what you already know. Nothing is scored, and every step stays open.

On this page

Decisions

Every program so far ran the same statements each time, from the top to the bottom. This lesson shows how a program asks a question about its values and then runs some statements and skips others, depending on the answer.

You need what modules 1 to 3 gave you: variables, std::cout, std::cin, and the remainder %.

Questions with a yes or no answer

A comparison puts two values side by side and asks one question about them. The answer is one of two values, true or false.

#include <iostream>

int main() {
    int price = 12;
    int budget = 20;
    std::cout << (price < budget) << "\n";
    std::cout << (price == budget) << "\n";
    bool can_buy = price <= budget;
    std::cout << can_buy << "\n";
}
1
0
1

price < budget asks "is 12 less than 20?". The answer is true. std::cout prints true as 1 and false as 0. It does not print the words.

There are six comparisons.

You write It asks
a < b is a less than b?
a > b is a greater than b?
a <= b is a less than b, or equal to it?
a >= b is a greater than b, or equal to it?
a == b are a and b equal?
a != b are a and b different?

Three things to notice.

  • "Equal" is written with two equals signs, ==. One equals sign is assignment, which you know from module 2: it stores a value. One = too few shows what happens when the two are confused.
  • <= and >= include the case where the two values are equal. < and > do not. When both values are 20, price <= budget is true and price < budget is false.
  • A comparison printed in a << chain needs parentheses around it. << is worked out before <, so without them the compiler would try to print price first and compare afterwards, and it rejects that.

true and false are values of a type called bool. A bool variable holds one of those two values and nothing else. The line bool can_buy = price <= budget; works out the comparison and stores its answer, in the same way that int total = 3 + 4; works out the sum and stores 7. No parentheses are needed there, because = is worked out last.

if

An if statement runs some statements only when the answer to a question is true.

#include <iostream>

int main() {
    int stock = 3;
    if (stock < 5) {
        std::cout << "Order more.\n";
    }
    std::cout << "Stock: " << stock << "\n";
}
Order more.
Stock: 3

Its parts, in order:

  • the word if;
  • a condition in parentheses: something that works out to true or false, such as a comparison;
  • braces { and } around the statements that the condition controls. The statements between a pair of braces are called a block.

The computer works out the condition. If it is true, the block runs. If it is false, the whole block is skipped.

The last statement is after the closing brace, so the if does not control it. It runs either way. Change stock to 8 and the condition is false: the first message is skipped, and the Stock: line is still printed.

There is no semicolon after the parentheses or after the closing brace. The statements inside the block are indented, so that a reader can see at a glance what the if controls. The compiler does not need the indentation. The reader does.

else

Often there are two things to do: one when the condition is true, another when it is false. An else adds the second block.

#include <iostream>

int main() {
    int balance = 40;
    int price = 0;
    std::cin >> price;
    if (price <= balance) {
        std::cout << "Paid. Left: " << balance - price << "\n";
    } else {
        std::cout << "Not enough money.\n";
    }
    std::cout << "Goodbye.\n";
}
55
Not enough money.
Goodbye.

The condition 55 <= 40 is false, so the first block is skipped and the block after else runs.

Exactly one of the two blocks runs, every time. Never both, never neither. Then the program continues after the last closing brace, with Goodbye.

An else has no condition of its own. It means "in every other case". It belongs to the if whose block it follows.

A chain of choices

For more than two cases, put another if after the else. This is a chain.

#include <iostream>

int main() {
    int age = 0;
    std::cin >> age;
    if (age < 5) {
        std::cout << "Free\n";
    } else if (age < 18) {
        std::cout << "Child ticket\n";
    } else if (age < 65) {
        std::cout << "Adult ticket\n";
    } else {
        std::cout << "Senior ticket\n";
    }
}
18
Adult ticket

The conditions are tested from the top, one at a time. The block of the first condition that is true runs. Everything below it in the chain is skipped, without being tested. If no condition is true, the final else block runs.

Follow it for 18. 18 < 5 is false. 18 < 18 is false too, because 18 is not less than itself. 18 < 65 is true, so Adult ticket is printed and the chain is finished.

Three things to notice.

  • Each test can rely on the ones above it having failed. When the computer reaches age < 18, it already knows that age < 5 was false. So the second block is for ages 5 to 17, although the condition does not mention 5.
  • The order matters. If age < 65 were the first test, a child of 3 would get an adult ticket, because 3 is less than 65 and the first true condition wins. Put the narrowest test first.
  • The numbers in the conditions are boundaries. Most mistakes in a chain are at a boundary: < written where <= was meant, or the other way round. Try the value on each side of every boundary. Here that means 4 and 5, 17 and 18, 64 and 65.

A chain is not the same as several separate if statements. Without the word else, every if is a question of its own, and each is asked whatever the answers to the others were. Two of them can be true for the same value, and then two blocks run. In a chain, at most one block runs.

One = too few

== compares. = assigns. Writing one = inside a condition is a mistake that the compiler accepts.

#include <iostream>

int main() {
    int seats = 3;
    if (seats = 10) {
        std::cout << "The bus is full.\n";
    }
    std::cout << "Seats taken: " << seats << "\n";
}
The bus is full.
Seats taken: 10

Three seats were taken, and the program says the bus is full. Read the condition as the computer does.

  1. seats = 10 is an assignment. It stores 10 in seats. The 3 is gone.
  2. The condition needs a true or a false, and it has been given the number 10. C++ turns a number into a bool by one rule: 0 counts as false, and every other number counts as true.
  3. 10 is not 0, so the block runs.

So this if does two things that were not intended. It changes the variable, and it does not ask a question at all. With seats = 0 in the condition the block would never run, whatever seats held before.

Most compilers, including the one in the lab, print a warning for a line like this. A warning is not an error: the program still runs. It is the compiler saying that the line is legal and probably not what you meant. Read warnings.

Both, either, not

Three more symbols join answers together.

You write Say It is true when
a && b a and b both are true
a \|\| b a or b at least one is true
!a not a a is false

Here is every case.

a b a && b a \|\| b !a
false false false false true
false true false true true
true false false true false
true true true true false

Notice that || is true when both sides are true. In everyday English "tea or coffee" often means one and not both. In C++ "or" includes both.

#include <iostream>

int main() {
    int hour = 14;
    bool is_holiday = false;
    bool is_open = hour >= 9 && hour < 17 && !is_holiday;
    std::cout << is_open << "\n";
    std::cout << (hour < 9 || hour >= 17) << "\n";
}
1
0

The shop is open from 9 up to, and not including, 17, except on holidays. At 14 on an ordinary day all three parts of is_open are true, so the whole condition is true. The second line asks whether the hour is outside opening time. Neither side is true, so the answer is false.

A range needs two comparisons joined by &&. In mathematics you would write 9 ≤ hour < 17. In C++ that form does not mean what it says.

std::cout << (9 <= hour < 17) << "\n";

By the rules of C++, the computer would work out 9 <= hour first and get true or false. Then it would compare that answer with 17. Used as a number, true counts as 1 and false as 0, and both are less than 17. So this condition would be true for every hour, including 23.

The compiler in this lab knows the mistake and refuses the program, with a message that says a chained comparison does not behave like a mathematical expression. Some other compilers accept the line and let the program give wrong answers. Either way, write the variable twice.

#include <iostream>

int main() {
    int hour = 23;
    std::cout << (9 <= hour && hour < 17) << "\n";
}
0

The order in which these are worked out: comparisons first, then !, then &&, then ||. That is why hour >= 9 && hour < 17 needs no parentheses. Two cases do need them.

  • To apply ! to a comparison, put the comparison in parentheses: !(hour < 9).
  • When one condition mixes && and ||, put parentheses around the part you mean to be worked out first. a || b && c means a || (b && c), which is rarely what a reader guesses.

Where a variable ends

A statement such as int discount = 10; creates a variable: it gives a type, a name and a first value. To create a variable in this way is to declare it, and the statement is its declaration.

A variable does not last for the whole program. A variable declared inside a block ends at that block's closing brace. After the brace, the name means nothing.

#include <iostream>

int main() {
    int total = 120;
    if (total > 100) {
        int discount = 10;
    }
    std::cout << "Discount: " << discount << "\n";
}

The compiler rejects the last statement with a message like this one.

your_program.cpp:8:34: error: use of undeclared identifier 'discount'

It is the message you get for a misspelt name, and for the same reason: at line 8 there is nothing called discount. The variable began on line 6 and ended at the brace on line 7.

The part of a program in which a name can be used is called its scope. The scope of a variable starts at its declaration and stops at the closing brace of the block it was declared in.

When a value is needed after an if, declare the variable before the if, and give it a new value inside.

#include <iostream>

int main() {
    int total = 120;
    int discount = 0;
    if (total > 100) {
        discount = 10;
    }
    std::cout << "Discount: " << discount << "\n";
}
Discount: 10

Now discount is declared in the block of main, so it lasts until the final brace. A block can use the variables of the block around it, which is how the statement inside the if reaches it.

Inside the braces the line is discount = 10;, an assignment, with no type in front. Writing int discount = 10; there would declare a second variable with the same name. That second one would get the 10 and end at the brace, and the first would still hold 0.

switch

A chain that compares one whole number with several fixed values can be written as a switch.

#include <iostream>

int main() {
    int place = 2;
    switch (place) {
        case 1:
            std::cout << "Gold\n";
            break;
        case 2:
            std::cout << "Silver\n";
            break;
        case 3:
            std::cout << "Bronze\n";
            break;
        default:
            std::cout << "No medal\n";
    }
    std::cout << "Thank you for taking part.\n";
}
Silver
Thank you for taking part.

Read it in this order.

  • switch (place) works out the value in the parentheses once. It must be a whole number.
  • Each case names one fixed value and ends with a colon. The computer jumps to the case whose value is equal to the one it worked out, and runs the statements from there.
  • break; leaves the switch. The program continues after its closing brace.
  • default: is where the computer jumps when no case matches. It plays the part of the final else.

A case is a place to jump to. It is not a block, and nothing stops the computer at the next case line. Only break does. This is the same program with one break removed.

#include <iostream>

int main() {
    int place = 2;
    switch (place) {
        case 1:
            std::cout << "Gold\n";
            break;
        case 2:
            std::cout << "Silver\n";
        case 3:
            std::cout << "Bronze\n";
            break;
        default:
            std::cout << "No medal\n";
    }
}
Silver
Bronze

The computer jumped to case 2: and printed Silver. No break followed, so it ran on into the statements written under case 3: and printed Bronze as well. The break after that stopped it. The compiler accepts this. End every case with break;.

A switch tests only "is this value equal to that fixed value?". For a range, such as age < 18, or for a condition with &&, use a chain of else if.

Did the read work?

Module 3 said that std::cin >> age; fails when the input is not a number, and left it there. The read can be checked, because std::cin >> age can itself be used as a condition. It is true when a value was read and stored, and false when the read failed.

#include <iostream>

int main() {
    int age = 0;
    if (std::cin >> age) {
        std::cout << "Next year you will be " << age + 1 << ".\n";
    } else {
        std::cout << "I need a whole number.\n";
    }
}
41
Next year you will be 42.

The condition does two jobs. It tries to read, and it answers whether that worked. Here the input was 41, the read worked, and the first block ran.

Put soon in the Input box and run it again. The text soon cannot be stored in an int, so the read fails, the condition is false, and the else block runs.

Use age only in the block where the read worked. After a failed read, do not use the variable: what it holds is not what the user typed.

From now on, a program that reads a number and then depends on it should check the read in this way.

Your turn

Now try it yourself

Predict what a program will do, run it, and step through it line by line. The checks tell you when your own code works.

Loading the lab…

In this lab

  1. True or false?

    Both variables hold 7. The program prints the answers to six comparisons, one after another on one line, with nothing between them.

    Each comparison is in parentheses because it is inside a << chain.

  2. An answer in a variable

    The answer to a comparison is stored in a bool variable and then printed.

  3. Does the line run?

    One output statement is inside the braces of the if. The other comes after the closing brace.

  4. Day or night

    This program draws a sky and a sun. The variable hour holds the time of day, from 0 to 23, but nothing uses it: the sky is always blue.

    Change the program so that the sky is "midnightblue" when hour is 20 or later, and "skyblue" at every other hour. Use an if with an else.

    Draw the sky once, as one rectangle, and keep the circle as the last shape so that it stays in front.

    Run it with hour at 19, at 20 and at 21, and look at the picture each time. The checks change hour for you.

  5. Only the first true one

    This chain should turn a score into a grade: A from 90, C from 70, D from 60, and F below that. The score is 95.

  6. One = too few

    The game should be over when there are no lives left. Look closely at the condition: it has one =, not two.

    This program compiles. The compiler prints a warning about the if line.

  7. Both, either, not

    Four conditions are printed on one line, with nothing between them. age is 15 and has_ticket is true.

  8. Inside the box

    The program draws a grey box and one point at (x, y). The point should be "green" when it is inside the box and "red" when it is outside.

    At the moment the condition tests only one thing: that the point is not left of the box's left edge. So a point far below the box is green too.

    Finish the condition. The box covers x from 200 to 400 and y from 100 to 250. A point exactly on an edge counts as inside.

    Change only the condition. Try several values of x and y: one in the middle, one on each edge, one a single step outside each edge. The checks do the same.

  9. Where does the name end?

    This program should print 1 when n is odd. The variable is_odd is declared inside the braces of the if and printed after them.

  10. Running on

    Look at where the break statements are in this switch, and where one is missing.

  11. Was that a number?

    The read is the condition of the if. Decide which block runs for each input.

  12. Signal lamp

    A lamp beside the road shows drivers how fast they are going.

    Read one whole number, the speed. Then draw one circle with its centre at (300, 200) and a radius of 60. Its colour depends on the speed:

    • under 50: "green"
    • from 50 to 79: "yellow"
    • 80 or more: "red"

    The speed comes from the Input box. Before you press Check, try the speeds on both sides of each boundary: 49 and 50, 79 and 80.

  13. Leap year

    Read a year and print whether it is a leap year.

    The rule has three parts:

    • a year that can be divided by 4 with no remainder is a leap year,
    • except a year that can be divided by 100, which is not,
    • except a year that can be divided by 400, which is a leap year after all.

    So 2024 is a leap year, 1900 is not, and 2000 is.

    Print exactly one line, in one of these two forms:

    2024 is a leap year
    1900 is not a leap year
    
  14. A menu that answers

    Print this menu, and then the word Choice: followed by one space, with no \n after it:

    1 Tea
    2 Coffee
    3 Water
    Choice:
    

    Then read a whole number and answer it on the same line as Choice: .

    • for 1, print Tea is 2.50
    • for 2, print Coffee is 3.00
    • for 3, print Water is free
    • for any other number, such as 9, print There is no item 9, with the number that was read
    • for something that is not a number, print Please type a number

    The answer is exactly one line and ends with \n. With the input 2, the whole output is:

    1 Tea
    2 Coffee
    3 Water
    Choice: Coffee is 3.00
    

    Use a switch for the numbers, and check that the read worked.

  15. Largest of three

    Read three whole numbers and print the largest of them, in this form:

    Largest: 9
    

    The numbers may come in any order, some of them may be equal, and they may be negative. For 5 5 2 the answer is 5. For 3 3 3 it is 3.

  16. Kind of triangle

    Read three whole numbers, the lengths of three sides, and print exactly one of these four lines:

    • not a triangle when the three lengths cannot form a triangle
    • equilateral for a triangle with all three sides equal
    • isosceles for a triangle with exactly two sides equal
    • scalene for a triangle with no two sides equal

    Three lengths form a triangle only when every side is shorter than the other two added together. So 1 2 3 is not a triangle, because 3 is not shorter than 1 + 2. Neither is 1 1 5, although two of its sides are equal.

    The equal sides, and the longest side, can be in any position.

  17. Quadrant

    Two grey lines divide the canvas into four quarters. The vertical line is at x = 300 and the horizontal line is at y = 200.

    Read two whole numbers, x and y. Draw the point as circle(x, y, 8, "red"), after the two lines. Then print one line that says where the point is:

    • top left when x is less than 300 and y is less than 200
    • top right when x is greater than 300 and y is less than 200
    • bottom left when x is less than 300 and y is greater than 200
    • bottom right when x is greater than 300 and y is greater than 200
    • on a dividing line when x is exactly 300, or y is exactly 200, or both

    Remember that y is measured from the top, so a smaller y is higher up.

  18. One round

    Two players play one round of rock, paper, scissors. Each move is a number:

    • 1 is rock. It beats scissors (3).
    • 2 is paper. It beats rock (1).
    • 3 is scissors. It beats paper (2).

    Read two whole numbers: the first player's move, then the second player's. Print exactly one of these lines:

    Player 1 wins
    Player 2 wins
    Draw
    

    Equal moves are a draw. You may rely on both numbers being 1, 2 or 3.

    A switch on the first player's move is one way to organise the cases.

  19. Find the scope

    This program should read a whole number and print whether it is even, as 1 or 0:

    4 is even: 1
    7 is even: 0
    

    It does not compile. Press Run and read the message: it names the last statement of the program, but the cause is further up. The answer is declared in the wrong place.

    Repair it. Keep a bool variable called is_even that holds the answer, and keep the last statement as it is.

  20. Truth table

    The condition a && !b || c has three parts, each true or false. That makes eight rows.

    1. On paper, draw a table with the columns a, b, c and result, and eight rows, one for each combination. Fill in the result column by hand. Remember the order: ! first, then &&, then ||.
    2. The program below prints the first row, with false written in place of all three names. Add the other seven rows in the same way. There is no shorter way yet: that comes in the next module.
    3. Run it and compare the printed column with your own.
    4. In your notes, add one line that says what = inside the condition of an if did in the step "One = too few".

    Nothing checks this one. Go through the list below when you are done.