SI204 Class 14: Odds & Ends

Homework
Work on the project and study for the exam! For instance, look at last year's 6Wk exam!

Lecture
This lecture is dedicated to odds and ends that we didn't cover because they weren't critical, and yet that are often nice to know.

switch statements

The switch statement is a bit of a holdover from the C language on which C++ is based, but it is something you'll probably see if you look at a lot of programs. You use a switch statement when you want to test a single variable or expression for several different cases. The hitch is that the variable pretty much needs to be an int or a char, which limits when switch can be used. Switch breaks things up into cases. You write
switch(expr)
{
    
... where expr is an expression of type int or char, and then list cases consisiting of possible values of expr. These cases must be constants, and each case is followed by a :, then a sequence of statements to be executed, and finally a break;. In other words, each case looks like this:
case constk:
         stmt1
         stmt2
         ...
         stmtr
break;
You can list as many of these cases as you want. You can also put
default:
         stmt1
         stmt2
         ...
         stmtr
break;
in as one "case". This is a catch-all that catches every situation in which expr didn't match one of the other cases. Here's an example program using switch. It reads a date in "mm/dd/yyyy" format and returns the date in "dd monthname, yyyy" format.

cin.get() and eof

When we read in a value to variable x with the statement
cin >> x;
cin skips over leading whitespace in the input stream, meaning it skips over newline characters ('\n'), tabs ('\t'), and spaces (' '). This is a bit of a problem if you want to do something like count the number of lines in a file. You can read input a char at a time without skipping over whitespace using cin.get(), and you can read from ifstream object fin a char at a time without skipping over whitespace using fin.get().

So, suppose you wanted to read in a line of text from the user and print out how many characters were in the line, you'd write this:

#include <iostream>
using namespace std;

int main()
{
  int count = 0;
  for(char c = cin.get(); c != '\n'; c = cin.get())
    count++;
  cout << "There were " << count << " characters!" << endl;
  return 0;
}

Suppose fin is the name of an ifstream object that you're reading from (remember to include fstream if you want to read and write with files). You can test whether or not you've come to the end of the file with the expression fin.eof() which evaluates to true if you have tried to read beyond the end of the file, and false otherwise. So if you wanted to write a program to count the number of characters in the file "Input.txt, you'd do something like this:

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
  // Open file stream
  ifstream FIS("Input");

  // Read char c until the end of file
  int count = 0;
  for(char c = FIS.get(); !FIS.eof(); c = FIS.get())
    count++;
  cout << "There were " << count << " characters!" << endl;

  return 0;
}

Formatting numbers with cout (or any ofstream object)

Oftentimes the way cout decides to format things might not agree with the way we'd like to see them formatted. For example, if x is a double with value 4.0, cout will write "4". Or, for example, the number of decimal places cout shows might not be suffient. Chapter 20 of your book contains lots of information about the iostream library, and Section 20.9 in particular tells you how to change the way output is formatted - it even includes lots of examples.

Formatting can be affected by "i/o manipulators" (you have to include the library iomanip for some of these). If you want to force cout to always show a decimal point, you "send" it the manipulator showpoint (which does not come from iomanip, rather is included in the iostream library).

double x = 3.0;
cout << x << endl;
cout << showpoint;
cout << x << endl;
YIELDS
3
3.000000

If you want to set the precision to be displayed in printint a number of type double use the setprecision(k) manipulator, where k is the number of digits you want shown (which does require the iomanip library). For example:

double x = 3.1234567890123456789;
cout << x << endl;
cout << setprecision(15);
cout << x << endl;
YIELDS
3.123456
3.12345678901235

Remember! Include the library iomanip when using setprecision!

Exiting main() early

The "return 0;" at the end of your program exits your program. In fact, inside main you can stick a "return 0;" wherever you want and as often as you want, and it'll exit the program. For example, maybe you want to write a program that reads an integer k from the user and writes out 1/k. If the user enters zero, of course, there's a problem. Now we'll just castigate the user and exit the program if he does that!

#include <iostream>
#include <iomanip>
using namespace std;

int main()
{
  // Get number from user
  cout << "Enter a non-zero integer: ";
  int k;
  cin >> k;
  
  // Deal with bad input
  if (k == 0)
  {
    cout << "You follow directions like a Firstie!"
	 << endl;
    return 1;
  }

  // Write out decimal approximation of 1/k
  cout << setprecision(20);
  cout << "1/" << k << " is "
       << 1/double(k) << endl;

  return 0;
}

do-while Loops

We've spent a lot of time with while-loops and for-loops. Both of these loops test whether to continue, and then go through the loop body, i.e. the test is done at the beginning of the loop. A "do-while" loop allows you to put the test at the end of the loop. This is convenient for certain tasks. For example, we looked at a program that reads expressions like
1 + 5 + 3 + 48 + 32 =
and prints out the resulting sum. This can be done nicely with a do-while

#include <iostream>
using namespace std;

int main()
{
  int next, sum = 0;
  char op;

  do {
    cin >> next;
    sum = sum + next;
    cin >> op;
  }while(op != '=');

  return 0;
}
#include <iostream>
using namespace std;

int main()
{
  int next, sum = 0;
  char op;

  // Must initialize with something other
  // than '=' just to make sure we enter
  // the loop the first time.
  op = 'X';

  while(op != '=') {
    cin >> next;
    sum = sum + next;
    cin >> op;
  }

  return 0;
}

In case you don't find that compelling, here's another example. Suppose we want to keep reading in ints from a list until we read a negative number.

#include <iostream>
using namespace std;

int main()
{
  int n;

  do {
    cin >> n;
  }while(n >= 0);

  return 0;
}
#include <iostream>
using namespace std;

int main()
{
  int n;

  cin >> n;  // read n the first time
  while(n >= 0) 
  {
    cin >> n;
  }

  return 0;
}

Short-circuit evaluation of boolean expressions

This is a fairly subtle point, and one whose use may not be apparent quite yet. When the boolean expression
E1 && E2
    
gets evaluated, E1 is evaluated first. If it's false, the value of the expression is false, without ever evaluating E2. Similarly, when
E1 || E2
    
gets evaluated, E1 is evaluated first. If it's true, the value of the expression is true, without ever evaluating E2. This is called short-circuit evaluation. This can have interesting consequences, and can be quite usefull. For example: suppose I want to read in integers from the user until I read one that evenly divides 472, which I will then print out. My first attempt might be

#include <iostream>
using namespace std;

int main()
{
  int n;

  do {
    cin >> n;
  }while(472 % n != 0);

  cout << n << endl;

  return 0;
}

There's a problem with this program, though. If the user enters 0, the program will crash, because 472 % 0 asks the computer to divide by zero. Clearly if the user enters zero we need to keep looping to look for a number that divides 472. So we'd like to modify our program so that it keeps looping if the n entered by the user is zero ... as opposed to crashing, which is what it does now. The following modification works:

#include <iostream>
using namespace std;

int main()
{
  int n;

  do {
    cin >> n;
  }while(n == 0 || 472 % n != 0);

  cout << n << endl;

  return 0;
}

Now, the only reason this works is the short-circuiting evaluation, which makes sure that when n == 0 is true, we never even try to evaluate 472 % n != 0. This also means that ++ and -- in expressions involving booleans can be extremely tricky, especially when a few implicit conversions come into play as well. For example, here's a game of C++ Jeapardy: This program outputs the answer, what's the question?

#include <iostream>
using namespace std;

int main()
{
  int k, n = 0;
  cout << "Enter integer k: ";
  cin >> k;

  for(int i = 1; i <= k && (k % i || ++n); i++); 
  cout << "The answer is " << n << endl;
  
  return 0;
}

P.S. Don't ever write programs like this!

P.S. This version might even be slightly worse!

Asst Prof Christopher Brown
Last modified: Fri Sep 20 08:47:21 EDT 2002