Chapter 3 Notes

Please read chapter 3 of the textbook.

True Tables for Boolean Expressions

boolean expressions are important in C++ because they appear in if-statements and loops

recall that a boolean expression is an expression that evaluates to either true or false

here are some examples

true     // evaluates to true

false    // evaluates to false

5 == 5   // evaluates to true

4 == 2   // evaluates to false

5 != 5   // evaluates to false

4 != 2   // evaluates to false

2 <= 3   // evaluates to true

5 <= 1   // evaluates to false

true && true   // evaluates to true

(x < 0) || (x > 1)  // value depends upon x

!(x == y)           // value depends upon x and y

(a == 0 && b == 0) || (a != 0 && b != 0) // value depends upon a and b

as with regular arithmetic expressions, parentheses can be used to change the order of evaluation of sub-expressions

Truth Tables

the logical operators && (and), || (or), and ! (not) are often defined using truth tables

see p. 114 of the textbook

Operator Precedence

C++ operators have different levels of precedence

in 1 + 2 * 3, * is evaluated before + because * has higher precedence than +

all C++ operators have a precisely defined precedence level

here are the precedence levels of a few important operators

highest precedence (done first)

            unary operators: + - ++ -- !
binary arithmetic operators: * / %
binary arithmetic operators: + -
          boolean operators: < > <= >=
          boolean operators: == !=
          boolean operators: &&
          boolean operators: ||

lowest precedence (done last)

for example, consider the expression x + 1 > 2

+ has a higher precedence than >, so x + 1 is evaluated first

then the result of x + 1 is compared to 2

you could re-write the expression equivalently as (x + 1) > 2

Short-Circuit Evaluation

C++ uses a common trick to speed up evaluation of some expressions

consider the boolean expression true || E, where E is some other boolean expression (although we don’t know exactly what it is, or what it evaluates to)

no matter the value of E, the expression true || E must be true because it starts with true

so in this expression C++ does not even bother to evaluate E!

similarly, an expression of the form false && E is always false, and so C++ does not evaluate E

Short-Circuit Evaluation

in general, when C++ evaluates the expression E1 || E2 it first evaluates E1

if E1 is true then it stops and returns true for the entire expression

otherwise, if E1 is false, then it evaluates E2

similarly, when C++ evaluates the expression E1 && E2 it first evaluates E1

if E1 is false then it stops and returns false for the entire expression

otherwise, if E1 is true, then it evaluates E2

Short-Circuit Evaluation

short-circuit evaluation can speed up the evaluation of some boolean expressions

it also forces the order in which the sub-expressions of boolean expressions are checked

Integers as Boolean Values

C++ lets you use integers as boolean values

this is how things were traditionally done in C (and C++ inherits almost everything from C)

the integer 0 is considered to be false, and every other non-0 integer is considered to be true

so you can write expressions like this

(0 && -6) || 5   // same as (false && true) || true

!52              // same as !true

however, while this is allowed in C++, it is rarely a good idea to do this

using integers as booleans can be quite confusing

it makes your code harder to understand, which in turn makes your code harder to debug and modify

Enumeration Types

an enumeration type is way to create your own data type

enumeration types are useful when you want a type for a small, finite series of values

// C++11-style enumerations
enum class Day { Sun, Mon, Tue, Wed, Thu, Fri, Sat };

Day today = Day::Mon;  // must put Day:: at start of Day values

C++ also supports older, C-style enumerations, e.g.

enum Direction { NORTH = 0, SOUTH = 1, EAST = 2, WEST = 3 };

C-style enumerations pretty much just give names to integers

in C++11, you should almost always prefer the enum class enumerations

they are usually clearer, and help catch more errors than C-style enumerations

Enumeration Types

the use of Day:: to access the values of Day seems a bit inconvenient

but it allows different enumeration types to use values with the same name, e.g.

// enum.cpp

#include "cmpt_error.h"
#include <iostream>

using namespace std;

// C++11-style enumerations
enum class Day { sun, mon, tue, wed, thu, fri, sat };

enum class Solar_obj {sun, moon, earth};

int main() {
    Day today = Day::sun;  // must put Day:: at start of Day values

    if (today == Day::sun) {
        cout << "Sunday\n";
    } else if (today == Day::mon) {
        cout << "Monday\n";
    } else if (today == Day::tue) {
        cout << "Tuesday\n";
    } else if (today == Day::wed) {
        cout << "Wednesday\n";
    } else if (today == Day::thu) {
        cout << "Thursday\n";
    } else if (today == Day::fri) {
        cout << "Friday\n";
    } else if (today == Day::sat) {
        cout << "Saturday\n";
    }

    // There is no confusion between Day::sun and Solar_obj::sun.
    Solar_obj obj = Solar_obj::sun;  // Must put Solar_obj:: at start
                                     // of Solar_obj values
    if (obj == Solar_obj::sun) {
        cout << "The Sun\n";
    } else if (obj == Solar_obj::moon) {
        cout << "The Moon\n";
    } else if (obj == Solar_obj::earth) {
        cout << "The Earth\n";
    }
} // main

Multi-way if-statements

one common use of if-statements is to check the value of a variable

and then to do different things depending upon that value

// simple_arith.cpp

#include "cmpt_error.h"
#include <iostream>

using namespace std;

int main() {
    while (true) {  // loop forever (infinite loop)
        cout << "eval> ";
        double x, y;
        char op;
        cin >> x >> op >> y;

        if (op == '+') {
            cout << x + y;
        } else if (op == '-') {
            cout << x - y;
        } else if (op == '*') {
            cout << x * y;
        } else if (op == '/') {
            cout << x / y;
        } else {
            cout << "Sorry, I don't know the operator " << op;
        }
        cout << '\n';
    } // while
}

Multi-way if-statements

if there’s only one statement in the body of an if-statement, then the braces are not required

if (op == '+')
    cout << x + y;
else if (op == '-')
    cout << x - y;
else if (op == '*')
    cout << x * y;
else if (op == '/')
    cout << x / y;
else
    cout << "Sorry, I don't know the operator " << op;

this is nicer to read

but don’t forget to add the braces back in when you add new statements!

if (op == '+')
    cout << x + y;
else if (op == '-')
    cout << x - y;
else if (op == '*')
    cout << x * y;
else if (op == '/')
    cout << x / y;
else
    cout << "Sorry, I don't know the operator " << op;
    cout << "\nI only know +, -, *, and /\n";

don’t be fooled by the indentation!

remember that C++ doesn’t care about indentation when it comes to determining blocks of code

the new cout statement is not part of the else body

instead, the new cout statement is the first statement after the entire if-else-if statement, and so it always executed

the correct way to write this is with braces

if (op == '+')
    cout << x + y;
else if (op == '-')
    cout << x - y;
else if (op == '*')
    cout << x * y;
else if (op == '/')
    cout << x / y;
else {
    cout << "Sorry, I don't know the operator " << op;
    cout << "\nI only know +, -, *, and /\n";
}

because of this problem, it’s usually wise to always uses braces even if they are not necessary

then you have one less problem to worry about in your code!

if (op == '+') {
    cout << x + y;
} else if (op == '-') {
    cout << x - y;
} else if (op == '*') {
    cout << x * y;
} else if (op == '/') {
    cout << x / y;
} else {
    cout << "Sorry, I don't know the operator " << op;
    cout << "\nI only know +, -, *, and /\n";
}

The switch Statement

the switch can be useful to make decisions based on integers or characters

int main() {
    cout << "eval> ";
    double x, y;
    char op;
    cin >> x >> op >> y;

    switch (op) {
        case '+':
            cout << x + y;
            break;
        case '-':
            cout << x - y;
            break;
        case '*':
            cout << x * y;
            break;
        case '/':
            cout << x / y;
            break;
        default:
            cout << "Sorry, I don't know the operator " << op;
    }
    cout << '\n';
}

notice the use of break — that’s important!

if you forget break in a switch statement, then the flow of control “falls through” to the next case (!)

switch statements only work with integer types, such as int and char

we will mostly avoid them in this course

they are sometimes a little more readable than if-statements, and possibly a little more efficient, but they are less flexible and tend to be more error- prone

The switch Statement

switch statements can be used with enumerated types

enum class Day { sun, mon, tue, wed, thu, fri, sat };

Day today = Day::sun;
switch (today) {
    case Day::sun:
        cout << "Sunday\n";
        break;
    case Day::mon:
        cout << "Monday\n";
        break;
    case Day::tue:
        cout << "Tuesday\n";
        break;
    case Day::wed:
        cout << "Wednesday\n";
        break;
    case Day::thu:
        cout << "Thursday\n";
        break;
    case Day::fri:
        cout << "Friday\n";
        break;
    case Day::sat:
        cout << "Saturday\n";
        break;
}

Loops

as we’ve seen, loops execute a block of code 0 or more times

we saw some examples of while loops earlier

C++ also has for-loops and do/while loops

for-loops come in two flavours: regular, and ranged

ranged for-loops are new in C++11, while regular for-loops are essentially a tidier version of a while-loop

do/while loops are just like while-loops, except the condition is checked at the bottom instead of the top

we won’t use do/while loops much, if at all, in this course

While Loops

this program prints the square of the numbers from 1 to 10

int main() {
    int i = 1;  // i is the loop index variable
    while (i < 11) {
        cout << i << " * " << i << " = " << i * i << "\n";
        ++i;
    }
    cout << "done\n";
}

notice that i is initialized to 1

that’s because we decided this program should start at 1

notice that the condition for the loop is i < 11

that’s because we want 10 to be the last value of i that is squared

in other words, as long as i is less than 11, the loop body should be repeated

we could have written the condition like this: i <= 10

the statement ++i adds 1 to i

it’s called the loop’s increment statement

without it you’d have an infinite loop that runs forever

While Loops

while loops typically take this general form

initialization_statement;

while (condition) {
    statement_1;
    statement_2;
    // ...
    statement_n;

    increment;
}

the exact details will vary depending upon the purpose of the loop

and somtimes a while-loop will not take this form

but in general, this is a good pattern to follow when writing loops

Increment Operators

the expression ++i adds 1 to the variable i

++ is called the increment operator

-- is called the decrement operator

they are short and convenient and occur all the time in C++ code

you can use them inside expressions, e.g.

int i = 1;
int a = 5;

cout << "i = " << i << "\n"
     << "a = " << a << "\n";
// i = 1
// a = 5

a = a + ++i + 2;

cout << "i = " << i << "\n"
     << "a = " << a << "\n";
// i = 2
// a = 9

Increment Operators

the expression ++i is called pre-increment

that’s because ++i first increments i, and then returns the value of i

the expression i++ is called post-increment

that’s because i++ first increments i, and then returns the value of i - 1 (i.e. the value before the increment)

when used as statements, there’s no difference between post-increment and pre- increment

int i = 0;
++i;  // i is now 1
i++;  // i is now 2

Increment Operators

but used in expressions, there are serious problems

often, the results are undefined, e.g.

int i = 0;
cout << ++i
     << "\n"
     << i
     << "\n";

this does not compile using our (strict) course makefile

the problem is that C++ cannot guarantee what, exactly, the value of i will be

many C/C++ programmers ignore this problem

but it can be a serious source of hard-to-find bugs

rule of thumb: never use increment (or decrement) operators in an expression

in this course ambiguous use of increment/decrement operators will usually be caught by our strict compiler options

this is a good thing that will stop you from writing buggy programs!

For Loops

recall this general while-loop template

initialization_statement;

while (condition) {
    statement_1;
    statement_2;
    // ...
    statement_n;

    increment;
}

this happens frequently enough that C++ provides a special loop for this case

this is called a for-loop

here’s the basic template

for(initialization_statement; condition; increment) {
    statement_1;
    statement_2;
    // ...
    statement_n;
}

this behaves the same way as the above while-loop

but the advantage is that all the loop-control information — the initialization condition, and increment — are in one place at the top of the loop

For Loops

int main() {
    cout << "How many iterations do you want? ";
    int n = 0;
    cin >> n;

    for(int i = 0; i < n; ++i) {
        cout << " i = " << i << "\n";
    }
    // cout << "i = " << i << "\n";
    cout << "Done!\n";
}

compare this to an equivalent version using a for-loop

int main() {
    cout << "How many iterations do you want? ";
    int n = 0;
    cin >> n;

    int i = 0;
    while (i < n) {
        cout << " i = " << i << "\n";
        ++i;
    }
    // cout << "i = " << i << "\n";
    cout << "Done!\n";
}

there is subtle difference here

in the for-loop, the scope of the i variable is within the loop only

but in the while-loop version, you can access i after the loop

For Loops

the parts of the for-loop header are optional

cout << "How many iterations do you want? ";
int n = 0;
cin >> n;

int i = 0;
for(; i < n; ) {
    cout << " i = " << i << "\n";
    ++i;
}
cout << "Done!\n";

an idiomatic way to write an infinite loop (a loop that never stops) is this

for(;;) { // infinite loop
    cout << "chugga chugga toot toot!\n";
}

type ctrl-C to end this!

For Loops

there’s often multiple ways to write the same loop

for example, suppose you want to print a count-down from 10 to 1 for a rocket

for(int i = 10; i > 0; --i) {
    cout << "... " << i << "\n";
}
cout << "Blast off!!\n";

here’s another solution

for(int i = 10; i >= 1; --i) {
    cout << "... " << i << "\n";
}
cout << "Blast off!!\n";

or even this

for(int i = 0; i < 10; ++i) {
    cout << "... " << (10 - i) << "\n";
}
cout << "Blast off!!\n";

try to use the method that you find clearest and simplest

clear, simple code is almost always better than complicated and tricky code!

For Loops

the condition for a for-loop can be more complex that checking the value of the loop-control variable

as an example, lets write a program that takes a positive int as input and prints all the factors of n like this

Please enter a positive integer: 12
12 = 1 * 12
12 = 2 * 6
12 = 3 * 4

an important requirement here is that the left factor must be less than, or equal to, the right factor

so a line like “12 = 4 * 3” should not be printed

// factors.cpp

#include "cmpt_error.h"
#include <iostream>

using namespace std;

int main() {
    cout << "Please enter a positive integer: ";
    int n;
    cin >> n;
    if (n <= 0) {
        cout << n << "isn't big enough!\n";
    } else {
        for(int factor = 1; factor * factor <= n; ++factor) {
            if (n % factor == 0) { // test if n is a multiple of factor
                int a = factor;
                int b = n / factor;
                cout << n << " = " << a << " * " << b << "\n";
            } // if
        } // for
    } // if
} // main

notice how we use comments to label all the }s at the end of the program

this helps ensure that the close braces are paired correctly with their open braces

we also can’t easily predict how many lines this program will print!

the for-loop condition handles it correctly, though

Example: 2-digit Numbers Divisible by their Digit Sum

loops can appear with in loops

this can be quite confusing, so use nested loops with care

for example, suppose you want to solve this mathematical problem

what are all the 2-digit integers, i.e. 11 to 99, that are divisible by the sum of their digits?

one solution to this problem is this

int main() {
    int count = 0;
    for(int a = 1; a < 10; ++a) {
        for(int b = 1; b < 10; ++b) {
            int n = 10 * a + b;
            if (n % (a + b) == 0) {
                cout << n << " is divisible by the sum of its digits\n";
                ++count;
            }
        }
    }
    cout << "count = " << count << "\n";
} // main

this code has a for-loop within a for-loop

the variables a and b each range over all possible digits

which we can use to create all numbers 11 to 99

nested loops are tricky in general!

Example: Sum of the Digits of a Positive int

Write a program that prints all the digits of a positive int, along with their sum. Use only basic C++ operators in your answer. Don’t use strings or any functions from other files (like cmath).

For example, given the int 38722, your programs should print:

2
2
7
8
3
sum = 22

Sample solution:

#include <iostream>

using namespace std;

int main() {
    cout << "What is n? ";
    int n = 0;
    cin >> n;
    cout << "n=" << n << "\n\n";

    if (n == 0) {
        cout << 0 << "\n";
        cout << "sum = 0\n";
    } else {
        int sum = 0;
        // n = 1234
        while (n > 0) {
            // get the ones digit
            int ones = n % 10;

            // print it
            cout << ones << "\n";

            sum += ones;

            // remove ones digit from n
            n = (n - ones) / 10;
        } // while
        cout << "sum = " << sum << "\n";
    } // if
}

Example: Printing a Triangle of Stars

Write a program that reads a positive int, n, from the user, and prints a triangle of stars, with 1 star in the first two, 2 in the second, and so on. For example, if n=4, then your program should print this:

*
**
***
****

Sample solution:

// stars.cpp

#include "cmpt_error.h"
#include <iostream>

using namespace std;

int main() {
    cout << "How many rows do you want? ";
    int num_rows = 0;
    cin >> num_rows;
    for(int i = 0; i < num_rows; ++i) {
        int num_stars = i + 1;
        for(int j = 0; j < num_stars; ++j) {
            cout << '*';
        }
        cout << "\n";
    }
} // main

Example: Sum of Numbers Entered by the User

Write a program that prints the sum of double_s entered by the user. Use the number -1 to indicate the end of the numbers. For example:

Please enter some numbers:
1.2
-1.2
3
0
4.5
-1
sum = 7.5

The -1 is not included in the sum.

Sample solution (while loop):

#include <iostream>

using namespace std;

int main() {
    cout << "Enter some numbers (-1 to end): ";
    double x = 0.0;
    double sum = 0.0;
    while (x != -1) {
        sum += x;
        cin >> x;
    }
    cout << "sum = " << sum << "\n";
}

Sample solution (for loop):

#include <iostream>

using namespace std;

int main() {
    cout << "Enter some numbers (-1 to end): ";
    double sum = 0.0;
    for(double x = 0.0; x != -1; cin >> x) {
        sum += x;
    }
    cout << "sum = " << sum << "\n";
}

Example: All 3-letter Strings

Write a program that prints all 17,576 3-letter strings consisting of lower- case letters a to z. The first few strings are:

aaa
aab
aac
aad
...

Sample solution:

// three_letters.cpp

#include "cmpt_error.h"
#include <iostream>

using namespace std;

int main() {
    int count = 0;
    for(char a = 'a'; a <= 'z'; ++a) {
        for(char b = 'a'; b <= 'z'; ++b) {
            for(char c = 'a'; c <= 'z'; ++c) {
                cout << a << b << c << "\n";
                count++;
            }
        }
    }
    cout << "count = " << count << "\n";
} // main

Ranged for-loops

when we get to vectors and arrays, we’ll see an alternate form of for-loop called a ranged for-loop

ranged for-loops are a more convenient way to loop over the data in an array or vector

What Kind of Loop to Use?

we’ve seen while loops, do/while loops, and for-loops

do/while loops occasionally have their uses, but you probably just avoid them

the problem with do/while loops is that they test their condition at the bottom

and that’s rarely what you want

and even when you do think you need a do/while loop, you often end up adding if-statements near the top to make it simulate a regular while-loop!

so a good rule of thumb is: never use do/while loops

What Kind of Loop to Use?

so should you use for-loops or while-loops?

if you can only pick one, use a for-loop

a for-loop does everything a while-loop does, but it is more readable because all the loop-control information is one place

sometimes, though, a while-loop will result in easier-to-read code

one kind of case is when the increment, or initialization, of the loop is complex and needs more than one statement

The break Statement

we’ve already seen the break statement inside of a switch

a break statement can also be used to make the flow of control immediately jump out of a loop

int sum = 0;
int count = 0;
for (;;) {
    cout << "Please enter a positive integer (0 to quit): ";
    int n = 0;
    cin >> n;
    if (n == 0) {
        break;  // jump out of the loop
    } else if (n < 0) {
        cout << n << " is not a positive integer!\n";
        break;  // jump out of the loop
    } else {
        sum += n;
        ++count;
    }
}
cout << "Done!\n";

double avg = double(sum) / count;
cout << "You entered " << count << " positive integers.\n";
cout << "Their sum is " << sum << ".\n";
cout << "Their average is " << avg << ".\n";

generally, using break like this is a bad idea

it usually makes loops harder to understand, especially if you have more than one break

it’s better if the only way to get out of a loop is because its condition is false

that will help you understand what the loop is doing, which aids in debugging

The break Statement

here is one alternative that avoids break

int sum = 0;
int count = 0;
bool done = false;
while (!done) {  // while "done is not false" do the following ....
    cout << "Please enter a positive integer (0 to quit): ";
    int n = 0;
    cin >> n;
    if (n == 0) {
        done = true;  // jump out of the loop
    } else if (n < 0) {
        cout << n << " is not a positive integer!\n";
        done = true;  // jump out of the loop
    } else {
        sum += n;
        ++count;
    }
}
cout << "Done!\n";

double avg = double(sum) / count;
cout << "You entered " << count << " positive integers.\n";
cout << "Their sum is " << sum << ".\n";
cout << "Their average is " << avg << ".\n";

this uses a flag variable called done

the idea is to set done to true when the loop is finished

it’s initially false to ensure the loop iterates at least once

flag variables like done are a usually technique to remember when writing loops

they can be useful when a loop has a complex stopping condition that doesn’t easily fit into the loop-condition

Designing Loops

mastering loops is hard because there are so many ways to write them

but you always need to think about, at least, these three things with any loop

  • what is the “setup” for the loop, i.e. what variables does it need and what are their initial values
  • what work should be done on each iteration of the loop
  • when, exactly, does the loop stop

in general, writing loops is one of the more challenging parts of programming

they are hard to visualize, and thus hard to debug

as we continue in this course, we will learn standard patterns for writing loops