C++ uses if and else to make choices. For example:
cout << "How old are you?";
int age;
cin >> age;
if (age <= 0) {
cout << "That's impossible!\n";
} else if (age < 1) {
cout << "You're very young for a user.\n";
} else if (age > 10 && age < 20) {
cout << "You're a teenager.\n";
} else {
cout << "That's a very interesting age.\n";
}
cout << "done\n";
When C++ executes this if-statement, it first checks to see if the expression age <= 0 is true or false. If it’s true, it prints the message underneath it, and then immediately jumps out of the if-statement and prints “done”. But if it’s false, the expression (age < 1) is checked next. If that’s true, the corresponding message is printed, and then C++ immediately jumps out and prints “done”. But if it’s false, it continues to the next expression. If (age > 10 && age < 20) is true, then the “teenager” message is printed, and then “done” is printed. Otherwise, if it’s false, then the message in the else block is printed.
In this particular if-statement, there are three conditions that are checked. They are checked in the order they occur, and the first one that is true will cause the block of code underneath it to be executed. If none of the expressions are true, then the code block under else is executed.
If-statements can have many different forms. For instance:
if (age < 0) {
error("age can't be negative");
}
If the code in the body of the if-statement is only a single statement, then the curly braces are not necessary:
if (age < 0)
error("age can't be negative");
If it is short enough, the statement can even be written all on one line:
if (age < 0) error("age can't be negative");
Generally, it is wise to always include the curly braces to avoid confusion. For instance, don’t be mislead by the indentation in this code:
if (age < 0)
cout << "age can't be negative";
cout << "it must be greater than 0"; // misleading indentation!
The second cout statement is always executed, no matter the value of age, because it is not inside the if-statement. The correct indentation makes this clear:
if (age < 0)
cout << "age can't be negative";
cout << "it must be greater than 0"; // better indentation
If you always include the curly braces, then this will never be a problem:
if (age < 0) {
cout << "age can't be negative";
cout << "it must be greater than 0";
}
If the if-statement and its body can fit on a single line, then it’s probably okay to leave off the braces.
Note
C/C++ also provide an if-like structure called a switch statement. switch is a popular way to efficiently implement if-statements that use only simple conditions on integers. You can read more about them in our textbook. But be warned: switch statements do not work like any other kind of statement in C/C++, and have a number of annoying limitations and rules that you must keep in mind when using them.
You can always easily rewrite a switch statement as an if-statement, and so we will not be using switch statements in this course.
While-loops are one of C++’s basic structures for repeating — or iterating — a block of code. For example, this program prints the squares of the numbers from 1 to 99:
int main() {
int i = 0;
while (i < 100) {
cout << i << " " << i * i << "\n";
++i;
}
}
Intuitively, you can think of a while-loop as saying “repeat the following block of code until it’s not the case that i < 100”.
Some notation:
int i = 0 is an initializer for the loop, i.e. it initializes the loop variable i.
The line while (i < 100) is called the loop’s header, and i < 100 is the loop’s condition.
The code block after the header is called the loop’s body:
{
cout << i << " " << i * i << "\n";
++i;
}
The loop body is only executed when the condition is true, when i < 100 is true. After the body executes the flow of control jumps back up to the top of the loop to check the condition again.
The final statement of the loop is i++, and this is informally known as the loop’s increment statement. This is important because without it we’d have an infinite loop, e.g.:
int i = 0;
while (i < 100) {
cout << i << " " << i * i << "\n";
}
This loop never stops! It’s surprisingly easy to forget increment statements.
Lets see a few more examples of loops.
This loops prints the numbers from 1 up to 10:
int i = 1;
while (i <= 10) {
cout << i << "\n";
++i;
}
This loop prints the numbers from 10 down to 1:
int i = 10;
while (i >= 1) {
cout << i << "\n";
--i;
}
This loop reads in strings from the keyboard, stopping when an end-of-file (EOF) character is encountered (ctrl-d in Linux):
int main()
{
string word; // initialized to ""
while (cin >> word) {
cout << word << " " << word.size() << "\n";
}
}
The expression cin >> word is both the loop condition and the loop increment. When cin >> word reads a word, it returns true if it actually did read in a word, and false if it instead read in the EOF character. It also puts the just-read word into the variable word (the increment).
Combining a condition and increment like this is typical in C++, although it can get confusing especially when you first see it. Generally, we will try to avoid this and keep the increment and condition separate.
Note
C/C++ also has a loop structure called a do/while loop. The essential difference between a do/while loop and a regular while loop is that the condition of a do/while loop is checked at the bottom of the loop after the code in the body has been executed. Thus, a do/while loop’s body is always executed at least once, where the code for a while-loop might never be executed (i.e. if the condition is false the first time it is checked).
However, do/while loops are rarely useful in practice, and so we will skip them in this course.
For-loops are very similar to while-loops, except they bundle all the loop-control information into the top of the loop. For example, this loop prints the squares of the numbers from 1 to 99:
int main() {
for(int i = 0; i < 100; ++i) {
cout << i << " " << i * i << "\n";
}
}
For-loops can look intimidating at first because all the loop-control information is in one place at the top of the loop. In contrast, the while-loop version of this program scatters this information around in three different places. In general, for-loops are preferable to while-loops because they are more organized and easier to understand.
In general, for-loops have this structure:
for(initialize; condition; increment) {
// ... code body ...
}
If you were to convert this to a while-loop, it would be this:
{
initialize;
while (condition) {
// ... code body ...
increment;
}
}
Notice that the initialization statement is outside the loop, and the increment is inside the loop at the bottom of the code body.
A for-loop can easily be converted to a while-loop, and so some programmers think of a for-loop as simply syntactic sugar for a while-loop: there’s nothing that you can do with a for-loop that you can’t do with a while-loop.
Lets write for-loop versions of the while-loops from above.
This program prints numbers, and their squares, from 0 up to 99:
for(int i = 0; i < 100; ++i) {
cout << i << " " << i * i << "\n";
}
This loops prints the numbers from 1 up to 10:
for(int i = 1; i <= 10; ++i) {
cout << i << "\n";
}
This loop prints the numbers from 10 down to 1:
for(int i = 10; i >= 1; --i) {
cout << i << "\n";
}
And finally, this loop reads in strings from the keyboard, stopping when an end-of-file (EOF) character is encountered (ctrl-d in Linux):
for(string word; cin >> word; ) {
cout << word << " " << word.size() << "\n";
}
Look carefully at the for-loop header: there is nothing after the second ;. That’s because cin >> word is both the condition and the increment.
Note that the variable word is not usable after the for-loop:
for(string word; cin >> word; ) {
cout << word << " " << word.size() << "\n";
}
cout << word; // compiler error: word is out of scope
When you define a variable inside a for-loop header, it is only usable in the header an in the body of the loop. While this can be annoying in some cases, it is actually a wise pre-caution that can prevent subtle errors. If you really do want to use word outside of the loop, move the initialization code outside of the for-loop like this:
string word;
for(; cin >> word; ) {
cout << word << " " << word.size() << "\n";
}
cout << word; // okay: word is in scope
Finally, occasionally it is useful to create an infinite loop, and C/C++ programmers often uses the following style of for-loop for that:
for(;;) { // infinite loop
cout << "happy happy joy joy\n";
}
This program runs forever, or until you do something drastic like kill it in the operating system, or turn off the computer.
In practice, you can use either while-loops or for-loops. However, we will generally be using for-loops since they are almost always more readable than while-loops.
A function is a named block of statements that help us re-use code. For example:
double square(double x) // header
{
return x * x; // body
}
This function is named square and it takes a single double as input and returns a value of type double. The return statement tells us what value the function evaluates to when it is called. For example:
double m = square(5); // m is 25
double n = 10 * square(2) + square(3); // n is 49
An expression like square(5) is a function call. When square(5) is evaluated, the following happens:
The return statement is important, and every C++ function that evaluates to something should end with one.
Also, when a return statement is encountered, the function immediately ends. So something like this would not work:
double square(double x)
{
return x * x;
cout << "square called\n"; // never executed!
}
Note
C/C++ does not have a special operator for squaring values. However, some languages do. For instance, in Python x ** 2 is the square of x.
It’s worth taking a moment to compare C++ functions to regular mathematical functions. In mathematics, you define the square function like this:
Evaluation of a mathematical function and a C++ function works
essentially the same, at least on a conceptual level. From math class
you know that, say, equals
. Similarly, the C++ expression 2 + 3 * square(4) is also 50.
If we were to use C++ terminology, then is the return
value of
. Mathematicians don’t bother to write “return”
since it is understood to be a return value. Mathematical functions
are often short expressions long, and don’t include things like
variable assignments or print statements. We need return in C++ to
distinguish regular non-returning statements from return
statements.
This definition also doesn’t say what kind of value
is. Instead, we simply know that squaring works with real numbers and
so assume
is real. A more precise definition of
might say that it maps from reals to reals; this is closer to what C++
requires.
We also try to give self-descriptive names to C++ functions so that
C++ source code is easy to read. In mathematics we use the name
because it is short and so easy to write by hand. But in
C++, it’s more important that a function name give a hint about what
it does, and so f would be a very bad name for a function that
squares its input.
Finally, we note that C++ functions are far more general than mathematical functions. For instance, there is no mathematical equivalent for a function like this:
void print_hello() {
cout << "Hello!\n";
}
Mathematics doesn’t deal with notions like “printing”, or “reading files”.
Lets see a few more example of C++ functions.
Example. This function inverts a double:
double invert(double x) {
return 1 / x;
}
A problem with this function is that 1 / x is undefined when x is 0:
cout << invert(0) << endl; // can't invert 0: 1 / 0 undefined
When I run this on my computer it prints inf. But really, we should treat it as an error, and so it is wiser to re-write invert like this:
double invert(double x) {
if (x == 0) error("can't invert 0");
return 1 / x;
}
Now if you try to call invert(0), the program is immediately halted with an error message.
Example. Not all functions return values. For instance:
void print_instructions() {
cout << "To play game\n"
<< "insert quarter\n";
}
The return-type of this function is void, meaning it does not evaluate to anything when you call it. Instead it just prints some text to the screen, which is not the same as returning the text. You can only call a void function as a statement:
print_instructions(); // okay
string s = print_instructions(); // compiler error!
Example. Here’s a function that can be useful for reading numbers from the user:
double get_double() {
cout << "Please enter a double: ";
double x;
cin >> x;
return x;
}
This function reads a double from the console and then returns it. You can use it like this:
double a = get_double();
double b = get_double();
cout << "a + b = " << (a + b) << "\n";
This is quite convenient, and you may want to use this function in your own programs.
Example. A small improvement we can make to get_double is to allow the programmer using it to supply their own prompt:
double get_double(string prompt) {
cout << prompt;
double x;
cin >> x;
return x;
}
Now you can write this code:
double a = get_double("Please enter a: ");
double b = get_double("Please enter b: ");
cout << "a + b = " << (a + b) << "\n";
The only problem with this new function is that sometimes we might be happy with the default prompt and not want to bother to supply one. So C++ lets us supply a default value for the prompt:
double get_double(string prompt = "Please enter a double: ") {
cout << prompt;
double x;
cin >> x;
return x;
}
Now supplying a prompt is optional:
double a = get_double("Please enter a: ");
double b = get_double();
cout << "a + b = " << (a + b) << "\n";
Warning
The string that is being passed to get_double is being passed by value. That means a copy of the string is made, and, if the string is big, that will take extra time and memory. We’ll see later how to avoid this problem (without using pointers) by instead passing prompt as a constant reference:
double get_double(const string& prompt = "Please enter a double: ") {
cout << prompt;
double x;
cin >> x;
return x;
}
This style of parameter-passing does not make a copy of the passed-in string. Instead, get_double refers to the actual original copy of the string a read-only way (so get_double can’t change prompt in any way). We’ll learn more about this as the course continues.
Example. This function restricts a number to a given range:
double constrain(double x, double lo, double hi) {
if (x < lo) return lo;
if (x > hi) return hi;
return x;
}
For example:
double x = get_double();
x = constrain(x, 1, 10);
cout << "x = " << x << "\n";
Or:
double x = constrain(get_double(), 1, 10);
cout << "x = " << x << "\n";
Or even:
cout << "x = " << constrain(get_double(), 1, 10) << "\n";
Note that there are a number of other ways to write the constrain function. For instance, this also works:
double constrain(double x, double lo, double hi) {
if (x < lo) {
return lo;
} else if (x > hi) {
return hi;
} else {
return x;
}
}
Different programmers prefer different versions. The first one is short and relatively easy to read, while the second version seems cluttered with all the braces. However, the second version is more consistent and explicit, and so may be easier to modify in practice.
You are allowed to define variables inside a function:
double get_double(string prompt = "Please enter a double: ") {
cout << prompt;
double x;
cin >> x;
return x;
}
Here, both prompt and x are defined inside get_double. This means that these variables are only usable inside get_double: when get_double ends both prompt and x are automatically deleted.
Variables defined inside a function are called local variables. Variables declared outside a function are global variables. Local variables only exist in the function in which they are defined, which means you cannot reference them outside of they’re function.
More specifically, all definitions in C++ have a scope, i.e. a span of text within the program where it is usable. The scope of local variables is the function they are defined in, while the scope of global variables is the entire program.
Since a variable need not be defined at the start of a function, the scope of a local variable might not be the entire function. For example:
double get_double(string prompt = "Please enter a double: ") {
cout << prompt;
double x;
cin >> x;
return x;
}
The scope of x starts at the second line of this function. If you were to try to access it before the statement double x, you’d get an error, e.g.:
double get_double(string prompt = "Please enter a double: ") {
cout << x; // compiler error: x not defined!
cout << prompt;
double x;
cin >> x;
return x;
}
However, it is possible that if x were also defined as a global variable, then there is no error, e.g.:
double x = 55; // global x
double get_double(string prompt = "Please enter a double: ") {
cout << x; // okay: prints global x
cout << prompt;
double x; // local x
cin >> x; // local x
return x; // local x
}
Local and global variable names can be quite confusing, and so it is considered good practice to simply not use global variables.
Note
Some languages, such as Java, do not have global variables at all and so never have this sort of problem.
As of summer 2012, you could buy Finch robots in bulk according to these prices:
For example, 25 robots would cost $2225, because .
Write a program that asks the user how many robots they want to buy, and then tells them the total cost. If the user enters 0 or fewer robots, then print a friendly message say that they need to buy at least 1 robot.