A "Feild" Guide to Programming with C++
Introductory programming notes by Henry FeildChapter 4: Boolean expressions and Branching
Important takeaways
- branching is a way to make a program behave differently based on Boolean expressions (e.g., involving user input)
- Two major ways of branching in C++ are: if/else-if/else statements and switch statements
- Boolean expressions evaluate to true or false and can include test operators (like ==, !=, <, >, etc.) between C++ expressions (like variables and constants)
- Multiple Boolean expressions can be combined using logical OR (||) and AND (&&) operators, or negated using the NOT (!) operator
Contents
Overview
It is rare that we write a program that does exactly one thing every time it runs, never deviating from a strict set of steps. More commonly, we want to write programs that react to inputs from the user or another source and execute one piece of code or another given some condition about the current state of variables.
In \ref, we see a flowchart for an ATM system, where we get data from the user (their bank account number and the amount to withdraw from the account). Rather than just dispensing the money, we first check to ensure the user has sufficient funds in their account to make the withdraw. Based on the outcome of that check, we go down one of two paths: we either update the user's account balance and make the withdraw, or we void the transaction and notify the user about the insufficient funds. We refer to each path as a branch of a program. Each branch contains a set of instructions that should only be executed under certain conditions, and when presented with several parallel branches, the computer can only take one.
In this chapter, we will learn about how to implement branching in C++ and how to construct the conditions (called Boolean expressions) to instruct the computer when to take a branch.
(Back to top)if
statements
The simplest branch structure in C++ is the if
statement. It works
by running a specified block of code at a point in a program only if a given
condition holds. If the condition does not hold, the block of code is skipped
entirely.
Let's see how to use if
statements; say we want to read a number in
from the user and, if the number is less than 10, we'll print out a message. The
code might look like this:
The general structure is:
We call this thing in the parentheses an Boolean expression. A Boolean expression always evaluates to a Boolean value, so either true or false. A Boolean expression can be:
- a Boolean:
true
,false
- a comparison of two expressions, using these test operators:
==
,>=
,>
,<=
,<
, and!=
; works between numeric, Boolean, character, and string types (and any type that has those operators defined) - two or more expressions joined with logic operators:
&&
and||
- a Boolean expression preceded by the negation operator
!
Here are a few examples of Boolean expressions:
schoolName == "Endicott"
: evaluates to true if the value stored in the variableschoolName
is the string "Endicott".name < "Bob"
: evaluates to true if the value of the string variable on the left comes alphabetically before the string literal "Bob" on the right.(name[0] >= 'A' && name[0] <= 'M') || (name[0] >= 'a' && name[0] <= 'm')
: evaluates to true if the first character of the string variablename
is in the first half of the alphabet (either capital or lowercase). We'll see more of these complex expressions below.!isFinished
: evaluates to true if the value of the Boolean variableisFinished
is false (read this like: "not finished").
Back to the if
statement above. Everything within the curly braces
immediately following an if
statement is the body of the branch. If the expression evaluates to true,
then the block of code inside of the curly braces will be executed; if the
expression evaluates to false, then the code inside the curly braces is skipped
and will not be executed. The program will pick up just after the ending curly
brace.
If the body consists of a single statement, the compiler does not require us to include the curly braces. For example, we can rewrite \ref as follows:
Be careful with this—sometimes programmers start off with no braces because there's only one statement in the body, but then add another statement later and forget to add in the curly braces. This can cause hard-to-spot errors! It is strongly recommended that you always use curly braces to avoid such errors.
(Back to top)else
statements
if
statements are good, but it would be nice to say something like:
"if x is true, do this, otherwise, do that". Luckily, C++ has
a built in statement called else
, which goes along with
if
. Here's the structure:
The else must directly follow an if
statements (or an else
if
statement, which we will learn about next). There is no extra
Boolean expression necessary—the body of the else
will
execute if the body of the if is not executed. Exactly one of them will execute:
either the if
body or the else
body. Similar
to if
, we do not need the curly braces if there is only a single
statement. Here is an updated version of \ref that
uses an else
to print out an alternative message when the number
provided by the user is 10 or greater:
else
else if
statements
C++ has another structure that can be used an if
statement, called
else if
. Use this when you want to test another expression when the
previous expression evaluates to false. Here's the general structure:
The final else
is optional. In addition, you may have as many
else if
statements after the initial if
. An
else if
's Boolean expression will only be tested if none of the
previous Boolean expressions in the current set of branches evaluated to true.
If its Boolean expression evalautes to true, then its body is executed and
subsequent else if
's and the final else
, if present,
will be skipped. It's important to know that when an else
statement is present, exactly one branch will be executed—the others will
be ignored. If no else
statement is present, then it is possible
that none of the branches will be executed.
Here is an updated version of \ref that
uses an else if
to print out an alternative message when the number
provided by the user is exactly 10:
else if
switch
statements
For case-based branching where you want to do something when an (not necessarily Boolean) expression
evaluates to a particular value, you can also use a switch
statement.
These can be used with whole number types, like int
and
long
, as well as char
types. Here's the
structure:
Here, the expression can be any code that evaluates to a whole number value,
e.g., an int
or char
. Each case within the curly
braces covers one of the possibilities, where val1
,
val2
, etc. are the individual values you want to test against
(these must be constant expressions—expressions that involve only literals
or constants; nothing that can vary during runtime). You need a
break
statement at the end of each case, or else the code within
the next case will be run (sometimes you want that). Finally, the
default
at the end is somewhat analogous to an else
statement; it's is triggered when no other case matches (or when the previous
case doesn't end with a break
statement). Here's an example where
we use a switch statement to determine what choice the user just entered:
I mentioned above that sometimes we want to leave break statements out so we can run the code in the following case statement. Below is an adaptation of \ref that is agnostic of the capitalization of the input character (so 'k' and 'K' are both valid to keep going, and 'q' or 'Q' are valid for quitting).
Complex Boolean expressions
We talked a little bit about complex Boolean expressions in Section
\ref; in this section, we will elaborate on these expressions further and
provide additional examples. First, let's start with an example. Suppose that we want a
program that asks the user how happy they are on a scale of 1–10. If we
want only numbers in that range, then we had better check to make sure the user
did that correctly. To check it, we need to make sure the number they provide is
greater than or equal to 1 and also less then or equal to 10. That's really two
subexpressions: the first compares the input to 1, the second compares the input
to 10. If the number is between 1 and 10 (inclusive), then both expressions will evaluate to true. So, we can use the logical
"and" operator, &&
, between the two sub expressions. Here's
what the program looks like:
Looking at the expression rating >= 1 && rating <= 10
, you
may be wondering why this is evaluated as (rating >= 1) && (rating <=
10)
and not (rating >= 1 && rating) <= 10
(strangely, this
will actually evaluate to true as long as rating is greater than or equal to 1).
The reason is operator precedence.
Operator Precedence
Just like in algebra, certain operators are evaluated before others, regardless
of their place in the expression being evaluated. For example, 10 + 3 *
5
is evaluated as 10 + (3*5)
, not (10 + 3) * 5
.
This is because multiplication has a higher precedence than addition. In C++, we
have the usual operators, plus many additional ones. Their order of precedence
is shown in the table below. Operators in the same tier are evaluated in order
from left to right.
Relative precedence | Operators |
---|---|
Highest | x++ , x-- |
! (not) | |
* , / , % (modulo) | |
+ , - | |
> , >= , < , <= | |
== , != | |
&& | |
|| | |
Lowest | = , += , -= , *= , /= |
Just as in algebra, you may override any order of precedent by using parentheses to group subexpressions. For more information about operator precedence (including operators will have not yet encountered), see this page.
De Morgan's Laws
Turning back to our example, our if
statement checks if the user's
input is in the valid
range. What we really want is to check if it's not in the valid range,
and if so, exit the program. If the rating is valid, then the code that exits
the program will be skipped and we can carry on doing whatever we want with the
valid range. To do this, we have a couple of options...
Option 1. The obvious thing we can do is to wrap our original
expression in parentheses and negate it using the logical "not" operator:
!
. This is a unary operator and is placed just before the
expression we want to negate.
Option 2. We can change the if
statement so that
we check if the rating is less than 1 or is greater than 10. This requires the
logical "or" operator: ||
. Here's what Option 2 looks like:
De Morgan's Laws. We can actually go from Option 1 to Option 2 algebraically by distributing the logical not into the expression. To do this, we use De Morgan's Laws. The two laws say the following:
- "NOT (x AND y)" is the same as "(NOT x) OR (NOT y)":
!(x && y)
is the same as!x || !y
- "NOT (x OR y)" is the same as "(NOT x) AND (NOT y)":
!(x || y)
is the same as!x && !y
Note that x
and y
are placeholders for arbitrary
expressions. We distribute the not to all subexpressions inside and flip the
operator. In the case of test operators, we don't distribute the not to
the operands, but we do flip the operator as follows:
!(x < y)
is equivalent tox >= y
!(x > y)
is equivalent tox <= y
!(x <= y)
is equivalent tox > y
!(x <= y)
is equivalent tox > y
!(x == y)
is equivalent tox != y
!(x != y)
is equivalent tox == y
So, if we have the Boolean expression from method 1, we can distribute the not as follows:
!(rating >= 1 && rating <= 10)
!(rating >= 1) || !(rating <= 10)
rating < 1 || rating > 10