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.

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 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.

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.

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.

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.