3. A Tour of C++

In these notes we will look at a couple of small sample programs that demonstrate some of the basic features of C++.

Throughout this course, we’ll be using std_lib_cmpt125.h to simplify our programs. Put a copy of it in the same directory as your .cpp programs. std_lib_cmpt125.h includes a number libraries and functions that we will be using throughout the course.

3.1. Example: Hello, World

The traditional first C/C++ program is one that prints “Hello, world!” on the screen. It demonstrates some of the basic syntax of C++:

// helloworld.cpp

#include "std_lib_cmpt125.h"

int main()
{
   cout << "Hello, world!\n";
   return 0;
}

Save this code — exactly as it appears — in a file called helloworld.cpp.

When the two files are ready, compile helloworld.cpp like this:

$ make helloworld
g++     helloworld.cpp   -o helloworld

The make command automatically calls the g++ C++ compiler to create an executable file called helloworld. To run it type this:

$ ./helloworld
Hello, world!

3.2. Hello World Line by Line

Lets go through the program line by line:

  • The first line is a source code comment:

    // helloworld.cpp
    

    // marks the beginning of a source code comment that continues until the end of the line.

  • The next line is an include:

    #include "std_lib_cmpt125.h"
    

    #include statements work by inserting the contents of the included file. In this case, the line #include "std_lib_cmpt125.h" gets replaced by the contents of the file std_lib_cmpt125.h.

  • The next line is the header for the main function:

    int main()
    

    First is the return type, in this case int. This means that main will return (i.e. evaluate to) a value of type int (i.e. an integer).

    Second is the name of the function: main. After the name comes the list of input parameters, i.e. the inputs to the function. This particular function has no inputs, so we just write ().

    main is a special function. All C++ programs must have exactly one function called main that returns an int. A C++ program always calls main first.

    Be careful to spell main correctly. Case matters in C++, so Main, MAIN, and mAIn are all wrong.

  • After the function header comes an open brace:

    {
    

    In C++, { marks the beginning of a block of code. Some programmers read it as “begin”.

    The block of code under a function header is called the function’s body.

  • This line prints “Hello, world!” on the console:

    cout << "Hello, world!\n";
    

    cout is a pre-defined object that stands for “character output”. The << operator indicates that the string on the right should be sent to the console as output.

    "Hello, world!\n" is a string literal. In C++, string literals always start and end with a double-quote mark ". The \n is a special escape character called newline. When a newline is sent to cout it causes the cursor to go to the next line.

    Finally the line ends with a semi-colon, ;. In C++, a semi-colon is a statement terminator, i.e. it marks the end of a statement.

  • The final line of the function body is a return statement:

    return 0;
    

    return does two things: it immediately ends the function, and it returns (in this case) the value 0. This is the value that the program returns to the operating system when it ends, and it may be useful if the program is called as part of a script. If the program doesn’t terminate correctly, then it is customary to return a non-zero value to indicate what sort of error has occurred.

    Notice that 0 is an integer, and so is of type int. The return-type declared in the function’s header should be the same as the type of the value returned in the return statement. Otherwise, errors can result. For example, if you tried to return a string, such as "zero", you’d get an error message like this:

    $ make helloworld
    g++     helloworld.cpp   -o helloworld
    helloworld.cpp: In function ‘int main()’:
    helloworld.cpp:8:11: error: invalid conversion from ‘const char*’ to ‘int’ [-fpermissive]
    make: *** [helloworld] Error 1
  • Finally, the program ends with a close brace:

    }
    

    A } marks the end of a block of code, and it matches the open brace { at the top. All open and closed braces must be matched like this.

3.3. Compiling and Linking

When you create an executable file with make helloworld, a few things occur:

  1. The C++ compiler is called on helloworld.cpp. The compiler converts the source code (i.e. the contents of helloworld.cpp into object code. Code that doesn’t follow the syntax rules for C++ is known as a syntax error, and usually causes a compiler error, aka compile-time error.

  2. If the compiler ran without finding any errors, then it produces object code that is sent to a program called the linker. The linker combines object code from multiple files to create a single executable file.

    For example, the code for cout and << is already compiled and stored as object code in a library file. Thus, you don’t need to re-compile it every time you compile your program. Instead, the library object code is linked into your executable file.

    It’s possible that linking could fail because your program calls a function that is not in any of the linked object code files. Such errors are called linker errors, or sometimes link-time errors.

  3. If the linker runs without error, then it produces an executable file that can be run. If there are errors in the program that were not caught by the compiler or linker, they could cause your program to crash or behave badly in some way. Errors like this that occur while a program is running are known as run-time errors.

Usually, we talk imprecisely and say that a program is being compiled when it is really being compiled and linked. Most of the time that presents no problems, but everyone once in a while bugs related to the linker can arise, and so it is important to understand that the linker exists.

3.4. The CMPT 125 Header File

As mentioned above, we’ll be using the header file std_lib_cmpt125.h in most of our programs. It imports a number of standard libraries, and also defines some useful helper functions that we’ll be using throughout the course.

The #include statement is a simple textual include, i.e. it is replaced by the contents of the file it includes. So if you like typing you could delete the include statement and type in the contents of std_lib_cmpt125.h. The results would be the same.

3.5. Example: What’s Your Name?

Let’s write a program that asks the user for their name and age, and then says hello to them. It will work like this:

What's your name? May
How many years old are you? 19

Hi May!
A decade from now you'll be 29 years old!

Here’s a first draft that does what we want:

int main() {
  cout << "What's your name? ";
  string name;
  cin >> name;

  cout << "How old are you? ";
  int age;
  cin >> age;

  cout << "Hi " << name << "!\n"
       << "A decade from now you'll be "
       << (age + 10) << " years old.\n";
} // main

Notice how we use cin to read input. The statement cin >> age knows to read an int because the variable age is of type int.

Nothing stops the user from entering a nonsensical age, such as 0 or -18. So to deal with that we can use an if-statement to catch non-positive ages:

// ...

if (age <= 0) {
  cout << "Your age must be greater than 0!\n";
} else {
  cout << "Hi " << name << "!\n"
       << "A decade from now you'll be "
       << (age + 10) << " years old.\n";
}

// ...

This handles negative ages, but it doesn’t handle the case when the user enters a non-integer for the age, e.g.:

What's your name? May
How old are you? six
Your age must be greater than 0!

This is not a sensible error message, since the problem is that the user a non-integer.

The way to handle this problem is to check to see if reading an int from cin was successful:

// ...

cout << "How old are you? ";
int age;
cin >> age;

if (!cin.good()) error("age must be an int");

// ...

The expression cin.good() is true if the last operation on cin was successful. If the user enters a non-int for the age, then cin >> age will cause cin.good() to be false.

In C++, ! means “not”, and so the expression !cin.good() is true just when cin.good() is false.

The error function is not a standard C++ function, but is instead defined in std_lib_cmpt125.h. It causes the program to immediately crash with an error message.

3.6. Example: Finding the Median

In this example, we want to create a program that reads in a sequence of numbers, and then prints the median value of the numbers. Recall that the median of a set of numbers is the number that is in the middle when the numbers are examined in ascending sorted order. For example, the median of {6, 9, 2, 1, 4} is 4.

Here’s how we want our program to work:

Please enter some numbers: 6 9 2 1 4

median: 4

To signal the end of the input, we use an EOF (end-of-file) character by pressing ctrl-D in Linux, or ctrl-Z in Windows.

Here’s a program that does the calculation:

#include "std_lib_cmpt125.h"

int main() {
  cout << "Please enter some numbers: ";

  vector<double> nums;          // nums initialized to be empty
  double x;                     // unknown initial value
  while (cin >> x) {
    nums.push_back(x);          // append x to the end of nums
  }

  sort(nums.begin(), nums.end());

  cout << "\nMedian: " << nums[nums.size() / 2] << endl;
} // main

A few notes:

  • nums is declared to be of type vector<double>, meaning it is a container that can hold doubles. Initially it is empty, i.e. it has 0 doubles in it.
  • The loop beginning while (cin >> x) repeats the block of code underneath it until a non-double (such as the EOF character) is read in.
  • sort is a function in C++’s standard library that can be used to sort vectors. By default, sort arranges the vector into ascending order.

It turns out to be quite easy to also find the median of a list of words. The median of a list of strings is the word that would occur in the middle if the list were arranged in alphabetical order:

#include "std_lib_cmpt125.h"

int main() {
  cout << "Please enter some words: ";

  vector<string> words;         // words initialized to be empty
  string s;                     // unknown initial value
  while (cin >> s) {
    words.push_back(s);         // append x to the end of words
  }

  sort(words.begin(), words.end());

  cout << "\nMedian: " << words[words.size() / 2] << endl;

} // main

Of course, it’s not immediately obvious why it would ever be useful to calculate the median of a set of words, but the point of this example is to demonstrate how easy it is to do some basic string processing in C++.

3.7. Example: Generating Random Passwords

Suppose you need to generate random passwords. One way to do this is to choose a set of symbols you want to appear in the passwords, and then to choose a sequence of those symbols at random.

So we’ll start by declaring a string that contains all the legal symbols:

const string legal_symbols = "abcABC123!\":";

This statement declares a variable named legal_symbols of type string. It’s also declared const, which means that the compiler won’t let legal_symbols be changed (all we can do is read legal_symbols). In general, it’s a good idea to declare your variables to be const whenever possible, as it can help you find errors, and sometimes may even speed up your program.

We initialize legal_symbols to be this string literal:

"abcABC123!\":"

In C++, string literals begin and end with a " character. If you want a " to appear as character in the string itself then you need to write it as \" as we’ve done.

Next, a password is a sequence of random characters chosen from legal_symbols, and so we’ll make a function that returns one random symbol:

char rand_symbol() {
  int n = legal_symbols.size();
  int r = randint(n);           // 0 <= randint(n) < n
  return legal_symbols[r];
}

This function is named rand_symbol, and it does not take any inputs. It returns a value of type char.

The expression legal_symbols.size() returns the number of characters in legal_symbols, and the randint function is from std_lib_cmpt125.h. The expression legal_symbols[r] returns that character at index location r of legal_symbols.

Now that we can get one random symbol, lets write a function to generate a random password of some given length:

string rand_password(int n) {
  string s;
  for(int i = 0; i < n; ++i) {
    s += rand_symbol();
  }
  return s;
}

The name of this function is rand_password, and it takes one input parameter of type int called n. It returns a string that contains n random characters.

The statement s += rand_symbol() appends the character returned by rand_symbol() to the (right) end of s. It’s inside a for-loop that executes n times, and so we end up with a string of exactly n random characters.

Here’s the complete program:

#include "std_lib_cmpt125.h"

// characters that can appear in a password
const string legal_symbols = "abcABC123!\":";

// Returns one random symbol.
char rand_symbol() {
  int n = legal_symbols.size();
  int r = randint(n);           // 0 <= randint(n) < n
  return legal_symbols[r];
}

// Returns a string of n random symbols.
string rand_password(int n) {
  string s;
  for(int i = 0; i < n; ++i) {
    s += rand_symbol();
  }
  return s;
}

int main() {
  randomize();                  // ensure a different random number
                                // seed each time the program run

  cout << rand_password(5) << endl;
} // main

And here are a couple of sample runs:

$ make rand_passwords

$ ./rand_passwords
1c"2a

$ ./rand_passwords
bB3CA

$ ./rand_passwords
!cCab

3.8. Questions

  1. What does this code print?

    cout << "a\nb\n" << "c"
    
  2. What does // mean in a C++ program?

  3. What is the return-type of main?

  4. What is the input to a compiler? What is the output of a compiler?

  5. What is the input to a linker? What is the output of a linker?

  6. Give an example of a compile-time error.

  7. Give an example of a run-time error.

  8. Show how cin.good() can be used in a fragment of code to test if an int has successfully been read from cin.

3.9. Programming Questions

  1. Modify the median program to also print the minimum value and the maximum value of the data entered.