© Will Briggs 2021
W. BriggsC++20 for Lazy Programmershttps://doi.org/10.1007/978-1-4842-6306-8_5

5. Loops, Input, and char

Will Briggs1  
(1)
Lynchburg, VA, USA
 

In this chapter, we’ll look at repeated actions, input, and things to do with the character type.

Keyboard input

Consider this code:
int ageInYears;
sout << "How old are you? "; ssin >> ageInYears;

This prints the query about age, then waits for keyboard input. If the user enters a number, that number is stored in ageInYears. (Anything else is likely to give ageInYears a 0 value.) ssin 1 waits for you to press Enter before it processes input, so backspacing is allowed.

ssin uses the same font and cursor as sout; they are both part of SSDL.

You may note how the << arrows go: with sout, they go from the value to the output; with ssin, they go from the input to the variable.

This is as good a time as any to introduce a new basic type: char, or character. Examples of chars include 'A' and 'a' (which are distinct), '?', '1', ' ' (the space character), and '\n'. Here is some code that uses a char variable:
char answer;
sout << "Are you sure (Y/N)? "; ssin >> answer;
if (answer == 'y')
    sout << "Are you *really* sure?\n";
You can also chain things you’re reading in with >>:
ssin >> firstThingReadIn >> secondThingReadIn;
../images/477913_2_En_5_Chapter/477913_2_En_5_Fig1_HTML.jpg
Figure 5-1

Insulting the world, one person at a time

Whether reading chars or numbers or whatever, ssin skips whitespace (spaces, tabs, and returns); so you can type what you want with spaces between, and it can handle it.

Example 5-1 is a sample program that finds ways to insult you no matter what your response. Figure 5-1 shows a sample session.
// Program to insult the user based on input
//       -- from _C++20 for Lazy Programmers_
#include "SSDL.h"
int main (int argc, char** argv)
{
    int ageInYears = 0;
    sout << "Let's see if you can handle the truth.\n";
    sout << "How old are you? "; ssin >> ageInYears;
    bool isOlder = (ageInYears >= 20);
    // Seriously? Well, 20 *is* old if you're a computer program
    if (isOlder) sout << "The truth is you are OLD.\n";
    else         sout << "You're not old enough. Sorry, kid.\n";
    sout << "Hit any key to end.\n";
    SSDL_WaitKey ();
    return 0;
}
Example 5-1

A program using ssin

Antibugging

  • You get a string of error messages like this:2
    main.cpp: In function 'int main(int, char**)':
    main.cpp:11:39: error: no match for 'operator<<' (operand types are 'std::istream' {aka 'std::basic_istream<char>'} and 'int')
         sout << "How old are you? "; ssin << ageInYears;
                                      ~~~~~^~~~~~~~~~~~~
    main.cpp:11:39: note: candidate: 'operator<<(int, int)' <built-in>
    main.cpp:11:39: note:   no known conversion for argument 1 from 'std::istream' {aka 'std::basic_istream<char>'} to 'int'
    In file included from /usr/include/c++/8/string:52,
                     from /usr/include/c++/8/bits/locale_classes.h:40,
                     from /usr/include/c++/8/bits/ios_base.h:41,
                     from /usr/include/c++/8/ios:42,
                     from /usr/include/c++/8/istream:38,
                     from /usr/include/c++/8/sstream:38,
                     from ../../external/SSDL/include/SSDL_display.h:26,
                     from ../../external/SSDL/include/SSDL.h:27,
                     from main.cpp:4:
    /usr/include/c++/8/bits/basic_string.h:6323:5: note: candidate: 'template<class _CharT, class _Traits, class _Alloc> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&)'
         operator<<(basic_ostream<_CharT, _Traits>& __os,
         ^~~~~~~~

and literally pages more. Good luck decoding that.

It all came from one error: the >>’s went the wrong way on an ssin statement. It should have been ssin << ageInYears. Compilers sometimes get confused.

You may get another flood of errors if you try to ssin >> "\n" or something else that isn’t a variable.

Exercises
  1. 1.

    Write a program for converting inches to centimeters, using the formula centimeters = 2.54 * inches. Make it interactive, that is, ask the user for the value to convert.

     
  2. 2.

    Write a program that identifies what generation you’re in (Gen Z, millennial, etc.), based on the age or year of birth the user inputs. You get to pick the ranges.

     
  3. 3.

    The Body Mass Index (BMI) tells you if you’re heavy, thin, or in the middle. (It’s imprecise, but if nothing else, maybe I could convince my grandmother I won’t starve if I don’t take seconds.)

    Per Wikipedia, these are the ranges:

     

Underweight

Less than 18.5

Normal weight

18.5–25

Overweight

25–30

Obese

30+

So, write a program to calculate the user’s BMI. The formula isBMI = Weight in kg/(Height in meters)2

If you’re in a country that uses English units, you’ll also need this information: 1 kg = 2.2 pounds and 1 meter = 39.37 inches.
  1. 4.

    Write a program that asks the user for two times (such as 1:06 or 12:19) and prints the difference neatly (e.g., 11:03 or 0:40, but not 13:0 or -12:70). You provide the times with keyboard input – we’re not asking the computer what time it is.

     
  2. 5.

    …but now we are. Instead of asking the user for the times, measure the two times that the user presses the return key, getting the current system time like so:

    int myTime = time (nullptr);

    This gives you time in seconds since midnight, January 1, 1970 (on systems I know). You’ll need to #include <ctime>.

     

while and do-while

The program can do something if a condition is true…or it can do something while a condition is true.

Here’s a way to determine how many times you can divide a number by 10 before you get 1. (This will be the same as the number of digits in the number if you print it.)
int digits = 0;
while (number > 1)         // while we haven't reached 1
{
    number /= 10;          // divide it by 10
    digits += 1;           // that's one more digit!
}
In Backus-Naur form (BNF), the while statement is
while (<condition>) <action>

As long as the condition is true, the while loop will execute the action. When it stops being true, the loop ends, and the program moves on to whatever comes after.

There’s a variation on while, exactly the same except that it checks the condition after doing the action: do-while . Its BNF form is
do <action> while (<condition>)
and an example is
do
{
    sout << "Ready to rumble (Y/N)? "; ssin >> answer;
}
while (answer != 'n' && answer != 'y');
    //  while answer isn't yes or no, ask again and again
if (answer == 'y')
    ... // rumble!

That wouldn’t work as a while statement, while (answer != 'n' && answer != 'y') ..., because you don’t know what answer is until you’ve asked at least once.

The bottom line is that do-while does the action at least once (before testing the condition), whereas while might quit before taking any action at all. We usually use while, but sometimes do-while is the very thing we need. Thus, we have the Golden Rule of Loops.

Golden Rule of Loops (Version 1)

If you want the loop to execute at least once, use do-while.

If it makes any sense for it to execute zero time, use while.

Loops with SSDL

There’s something I didn’t tell you about SSDL. It doesn’t update the screen every time you draw or print. To save update time, it puts this off till it has a reason to wait on the user: an ssin statement or an SSDL_WaitKey or SSDL_WaitMouse. The following loop, which is intended to keep showing you "Move mouse to right half of screen to end." until you move the mouse to the right, will never display anything:
while (SSDL_GetMouseX() < WHERE_IT_IS)
{
    SSDL_RenderClear ();
    SSDL_SetCursor (0, 0);
    sout << "Move mouse to right half of screen to continue.";
}

SSDL also doesn’t check for things that make it quit the program – pressing Escape or clicking the X to kill the window – until it’s waiting on you. So the preceding code won’t let you quit, either.

The fix is the same for both problems: the function SSDL_IsQuitMessage . It updates the screen, checks for input messages (mouse clicks, keystrokes), and returns whether there’s been a command to quit:
while (! SSDL_IsQuitMessage () && SSDL_GetMouseX() < WHERE_IT_IS)
{
    SSDL_RenderClear ();
    SSDL_SetCursor (0, 0);
    sout << "Move mouse to right half of screen to continue.";
}
Here’s the ready-to-rumble do-while loop from earlier, adapted to allow the user to quit easily. Both it and the preceding while loop are in a source code example ch3/loops-with-SSDL.
do
{
    sout << "Ready to rumble (Y/N)? "; ssin >> answer;
}
while (!SSDL_IsQuitMessage () && (answer != 'n' && answer != 'y'));
Extra

In that last do-while loop, we could ask the user to type 1 for yes and 2 for no, if we wanted to expose ourselves as user-hostile throwbacks to the 1970s and never get hired again. (What does 2 have to do with “no”?) It’s much easier on the user to remember that 'n' means no.

If there are more options to choose than yes and no – say, your program manipulates files, opening, saving, and renaming – it’s still user-friendly to give options with letters (O, S, and R) rather than numbers.

How to make your programs easy to interact with is the subject of one subfield of computer science: human-computer interaction.

break and continue

break means leave the loop immediately. Here’s a version of the preceding while loop, now using break. You decide which way’s clearer:
while (SSDL_GetMouseX() < WHERE_IT_IS)
{
    if (! SSDL_IsQuitMessage ()) break;
    SSDL_RenderClear ();
    SSDL_SetCursor (0, 0);
    sout << "Move mouse to right half of screen to end.";
}

continue means skip the rest of this iteration of the loop and go back to the top. I rarely use it.

Some programming-style mavens are horrified by break and continue. They think you should be able to look at the loop’s continuation condition and see immediately under what circumstance the loop can end – essentially, that these keywords reduce clarity. I agree clarity is crucial, but I’m not sure break is the problem. Certainly if a loop is 50 lines long, it’ll be tedious to examine it for breaks. But I think the solution is not to have loops 50 lines long. Simple is good.

Antibugging

  • The program won’t end, and you can’t even kill the program. You’re probably stuck in a loop, but how do you stop it? First, try Ctrl-C (hold Ctrl down and press C). If that doesn’t work, try these actions:
    • Visual Studio: Debug ➤ Stop Debugging or click the reddish square for stop near the top of the window.

    • MinGW: Kill it with Task Manager.

    • Unix: If you don’t even have a command prompt, press Ctrl-Z in the command window to get it.

There are two commands that can help us. ps lists active processes:
PID TTY          TIME CMD
14972 pts/0    00:00:00 bash
15046 pts/0    00:00:00 bash
15047 pts/0    00:00:01 a.out
15054 pts/0    00:00:00 ps

kill -9 <process-id> means something like “I tried, but I can’t find a nice way to end this process, so just kill it.”

a.out is what we’re trying to kill, but if we ran it with a script like runx, we’d want that gone too. It’s probably the most recent shell command, some command with “sh” in its name. (Get the wrong one and you may kill your terminal. Oops.) This command will kill it and its dependent process a.out:

kill -9 15046
  • The loop repeats forever, and you can’t quit. Maybe it isn’t checking for quit messages. Make your loop condition look like this
    while (! SSDL_IsQuitMessage () &&
           ...whatever else you want to check... )
        ...;
    or, if it’s a do-while,
    do
    {
        ...
    }
    while (! SSDL_IsQuitMessage () && ...);
  • The loop repeats forever until you click quit or does something forever that you wanted done a few times.

    Consider under what condition you break the loop. It must be that it’s never met:
    int rectanglesDrawn = 0;
    while (!SSDL_IsQuitMessage () &&
           rectanglesDrawn < MAX_RECTANGLES)
    {
        SSDL_RenderDrawRect (...);
    }
    The loop never incremented rectanglesDrawn…so no matter how many you draw, the loop doesn’t end. This line should do it:
        ...
        rectanglesDrawn += 1;
    }
  • The loop repeats forever, or won’t repeat when it should. It’s easy to get confused when the loop has a combination of conditions:
    do
    {
        sout << "Answer Y or N: "; ssin >> answer;
    }
    while (! SSDL_IsQuitMessage () && (answer != 'n' || answer != 'y'));

    This may look right, but it actually says keep looping while nobody said quit, and the answer is not yes or not no. Well, it’s always either not yes or not no! Suppose it’s yes. Then “not no” is true, so it keeps going. Suppose it’s no. Then “not yes” is true, so it keeps going.

    The solution is to keep going while it’s not yes and it’s also not no – while it’s a nonsensical answer like '7' or 'X':
    do
    {
        sout << "Answer Y or N: "; ssin >> answer;
    }
    while (! SSDL_IsQuitMessage () && (answer != 'n' && answer != 'y'));
Exercises
  1. 1.

    Have the user keep entering a number until he/she guesses some number you pick.

     
  2. 2.

    …and have the program print how many guesses it took.

     
  3. 3.

    Write a program that asks for a (capital) letter and counts from 'A' till it finds it. Its output will be something like 'E' is the 5th letter of the alphabet!

     
  4. 4.

    …now adapt the program so it keeps repeating till you give it a '.' to end.

     
  5. 5.

    Write a program that draws a bubble wherever you click. The bubble’s size should depend on how long since the last mouse click. Use the Internet to read up on the function SDL_GetTicks() .

     
  6. 6.

    Update Exercise 2 from the end of the previous chapter – the hidden object game – so the user can click the hidden objects in any order.

     
  7. 7.

    Make your own music player: put boxes marked “Music on” and “Music off” near the bottom of the screen, and turn sound on or off appropriately when the user clicks one of the boxes.

     
  8. 8.

    (Harder; requires geometry) Consider the perimeter of a triangle which is centered on a point and whose endpoints are “R” away from that center.

    Now consider if it were a square, or a pentagon, or something with N sides (Figure 5-2). What will the perimeter look like when N is large?
    ../images/477913_2_En_5_Chapter/477913_2_En_5_Fig2_HTML.png
    Figure 5-2

    Polygons for Exercise 4

    Write a program that (a) draws a regular N-sided polygon, for some large N and with some radius R, and (b) finds the perimeter. Divide that perimeter by 2R. Given the shape you see on the screen, would you expect that ratio to be close to π? Is it what you expected?

     

for loops

A for loop is a loop that counts through a range. Here’s a simple one:
for (int count=0; count < 10; count += 1)
    sout << ' ';         // print these numbers, separated by spaces

And here’s its output: 0 1 2 3 4 5 6 7 8 9

In Backus-Naur form, a for loop is
 for (<initialization section>; <continuing-condition>; <increment>)
   <action>

Let’s look at that piece by piece.

The initialization section – int count=0 – is done when the loop starts. As you can see, you can declare variables in it. The variables are only visible inside the loop.

As long as the continuing condition is true, the loop continues.

At the end of each time through the loop, each “iteration,” C++ does the increment part. This could be anything, but it usually increments an index variable (i.e., the variable we’re using to count with).

The order that the computer does the sections is
  1. 1.

    Do <initialization section>.

     
  2. 2.

    Is <continuing-condition> true? If not, leave the loop.

     
  3. 3.

    Do <action>.

     
  4. 4.

    Do <increment-section>.

     
  5. 5.

    Go back to step 2.

     

Increment operators

We often find we need to add 1 to a variable (or subtract 1). C++ provides operators for this. Here are two examples:
++y; //  adds 1 to y. This is called "increment."
--x; //  subtracts 1 from x. This is called "decrement."

Most computers have a built-in instruction to add 1 and another to subtract 1 – so we’re telling the compiler to use them. It’s efficient.

We often do this in for loops, like so:
for (int count=0; count < 10; ++count)
    sout << count << ' ';
We can also use decrement operators:
for (int count=10; count > 0; --count) // A countdown, 10 to 1...
    sout << count << ' ';
sout << "Liftoff!\n";
You can increment by other amounts, though it’s unusual:
for (int count=2; count <= 8; count += 2) // 2 4 6 8...
    sout << count << ' ';
sout << "Who do we appreciate?\n";

There’s another type of increment, called “post-increment,” and a corresponding post-decrement. It looks like this: count++, not ++count. Here’s how they differ:

Pre-increment: Y = ++X means X = X+1; Y = X. That is, add 1 to X; Y gets X’s new value.

Post-increment: Y = X++ means Y = X; X = X+1. That is, add 1 to X; Y gets X’s old value.

You won’t notice the difference unless you put the expression on the right side of an = or as an argument to a function.

An example: Averaging numbers

Suppose you want to average a list of ten numbers given by the user. I know: how exciting! But we can’t play games all the time; people will start thinking programming is too much fun and pay us less. Here’s my plan:
tell the user what we're doing
total = 0                                // so far, nothing in the total...
for ten times
    get a number from the user
    add that to the total
average = total/10.0   // floating-point division, for a floating-point answer
                      // better not use int division -- remember
                      // Example 3-3 (drawing a star), in which our division
                      //  answers kept showing up as zeroes...
print average
Example 5-2 shows this in real C++.
// Program to average numbers
//    -- from _C++20 for Lazy Programmers_
#include "SSDL.h"
int main (int argc, char** argv)
{
    constexpr int MAX_NUMBERS = 10;
    sout << "Enter " << MAX_NUMBERS
         << " numbers to get an average.\n";
    double total = 0.0;
    // Get the numbers
    for (int i = 0; i < MAX_NUMBERS; ++i)
    {
        double number;
        sout << "Enter the next number:  ";
        ssin >> number;
        total += number;
    }
    // Print the average
    double average = total / MAX_NUMBERS;
    sout << "The average is " << average << ".\n";
    sout << "Hit any key to end.\n";
    SSDL_WaitKey ();
    return 0;
}
Example 5-2

A program to average numbers, using a for loop

The keywords break and continue, by the way, work in for loops just as they do in while and do-while loops. They’re available, but not, in my experience, useful.

So we now have three kinds of loops. You know how to decide between while and do-while – in the Golden Rule of Loops (Version 1), earlier in this chapter. What about for loops?

By convention and by reason, we use for loops when we’re counting through a range – when we know what we’re counting from and to. Thus, we have the final Golden Rule of Loops.

Golden Rule of Loops (Final Version)

If you know in advance how many times you’ll do it, use for. Otherwise:

If you want the loop to execute at least once, use do-while.

If it makes any sense for it to execute zero times, use while.

Antibugging

  • Later actions are done once, not many times – a common problem. Here’s an example:

// Code to print several powers of 2
int product = 1;
sout << "Here are several successive powers of 2: ";
for (int i = 0; i < 10; ++i)
    sout << product << ' ';
    product *= 2;
I forgot the {}’s. I assumed the code would do this:
for i goes from 0 through 9
    print product
    multiply product by 2
but it actually does this:
for i goes from 0 through 9
    print product
multiply product by 2
To prevent this, let your editor indent for you as you go, thus catching the error.
  • No action gets repeated.

for (int i = 0; i < N; ++i);     // This loop prints only one *
    sout << '*';
There’s an extra ; at the end of the first line.
  • Your loop goes one step too far.

for (int i = 0; i <=4; ++i) ...

The last time through, ++i, makes i equal 4. But if you wanted four entries, you just got five: 0, 1, 2, 3, 4. Solution: Use < for the condition.

To be sure you have the right range, trace through the code before compiling. Or always use the form
for (int i = 0; i < howManyTimes; ++i) ...
Tip

For loops almost always start at 0 and use <, not <=, for the continuation condition: i < howManyTimes, not i <= howManyTimes.

Exercises
  1. 1.

    Print the numbers 1–10…and their squares (1, 4, 9, 16, etc.).

     
  2. 2.

    Using characters now, use a for loop to print the letters A–Z.

     
  3. 3.

    …backward.

     
  4. 4.

    Write a program like the average program in Example 5-2, but let it provide not average but maximum.

     
  5. 5.

    Adapt Example 3-3/Example 3-4 (drawing a star) so that it asks the user for the radius, center, and number of points – and use a loop rather than repeating code to draw the lines.

     
  6. 6.

    Write a program which asks the user for an integer and a power to raise it to and prints the result. Calculate with for, of course.

     
  7. 7.

    Write a program which has the user guess a number (that number can be a constexpr) and keeps taking guesses until the user runs out of turns – you decide how many – or gets it right. Then it reports success or failure. You’ll need a Boolean variable isSuccess.

     
  8. 8.

    (Hard) Draw a graph of some function (sine is a good one). Add X and Y axes and appropriate labels.

     

chars and cctype

Example 5-3 compares two input characters to see whether they’re given in alphabetical order.
// Program to tell if two letters are in alphabetical order
//    -- from _C++20 for Lazy Programmers_
#include "SSDL.h"
int main(int argc, char** argv)
{
    char char1, char2;
    sout << "Give me a letter: "; ssin >> char1;
    sout << "Give me another: ";  ssin >> char2;
    if      (char1 < char2)
        sout << "You gave me two characters in order.\n";
    else if (char1 > char2)
        sout << "They are in reverse order.\n";
    else
        sout << "It's the same letter.\n";
    SSDL_WaitKey();
    return 0;
}
Example 5-3

A program to compare characters

It mostly works. It’s a little strange that 'a' comes after 'Z'. But that’s how the computer thinks of it: lowercase letters, a–z, come after the uppercase range. The precise ordering of the characters was decided in 1967 and is maintained by the American National Standards Institute (ANSI). A complete listing of this American Standard Code for Information Interchange (ASCII) codes is in Appendix C.

I’d rather my comparison ignore capitalization. Here’s one way – convert it to uppercase:
char myChar           = 'b';
char upperCaseVersion = myChar - 'a' + 'A';

It looks weird, but…to get the uppercase version of 'b', we do this: subtract 'a' first. This gives us a difference of 1, of course. We then add this 1 to 'A', which gives us 'B'. This will work for any lowercase letter.

What if we aren’t sure it’s lowercase? We can use an if statement to be sure:
if (myChar >= 'a' && myChar <= 'z') // if it's lower case -- fix it
    upperCaseVersion = myChar - 'a' + 'A';
else                                // if not -- leave it alone
    upperCaseVersion = myChar;
This is so useful we’ll want to do it again and again. Fortunately, the makers of C and C++ agree. They’ve given us a suite of functions, some shown in Table 5-1, for handling capitalization and a few other qualities of characters; these are found in the include file cctype. For more such functions, see Appendix F.
Table 5-1

Some useful functions regarding capitalization

int islower (int ch);

Return whether ch is lowercase. (Non-letter characters are not lowercase.)

int isupper (int ch);

Return whether ch is uppercase. (Non-letter characters are not uppercase.)

int tolower (int ch);

Return the lowercase version of ch. If ch is not a letter, it returns ch.

int toupper (int ch);

Return the uppercase version of ch. If ch is not a letter, it returns ch.

These functions existed in the C, the language C++ grew out of. This explains something that looks odd: we’re dealing with characters, but the type is not char but int! Well, characters are like integers, in a way, so this is tolerable, if not absolutely clear.

The other odd thing about these functions is similar: islower and isupper return int. Shouldn’t they return true or false? Yes, but since C++ interprets 0 as false and other integers all as true, int will serve, as in this code snippet:
if (isupper (myChar))     sout << "You have an upper-case letter.\n";
Example 5-4 uses toupper to compare characters without regard to case.
// Program to tell if two letters are in alphabetical order,
//   regardless of upper or lower case
//     -- from _C++20 for Lazy Programmers_
#include <cctype>
#include "SSDL.h"
int main(int argc, char** argv)
{
   char char1, char2;
    sout << "Give me a letter: "; ssin >> char1;
    sout << "Give me another: ";  ssin >> char2;
    if      (toupper(char1) < toupper(char2))
        sout << "You gave me two characters in order.\n";
    else if (toupper(char1) > toupper(char2))
        sout << "They are in reverse order.\n";
    else
        sout << "It's the same letter.\n";
    SSDL_WaitKey();
    return 0;
}
Example 5-4

Example 5-3, using true alphabetical order rather than simple ASCII order

Antibugging

  • You try to print a char with converted case, and it prints a number instead:
    sout   << "The upper-case version of '" << char1
           << "' is ' << toupper (char1) << " '.\n";

    If we run this, the output will be something like

The upper-case version of 'a' is '65'.
The problem is that toupper returns not char but int – so sout prints that int. Here’s the fix: casting.
sout << "The upper-case version of '" << char1
     << "' is '" << char (toupper (char1)) << " '.\n";
  • You’re assigning to a char and get something like “Cannot convert from const char[2] to char.”

    This code looks right, but is not: char c = "x"; // wrong!

    chars need single quotes, like so: char c = 'x';

    The way to remember: Single quotes are for single chars. Double quotes are for two (or more) chars, that is, “quoted text.”

Extra

So far we’ve seen these types:

int double float bool char

Some can have modifiers. For example, a long double has more decimal places than a regular double; how many is compiler-dependent. int can be preceded by the keywords signed , unsigned, short, long, or long long (I guess the word “humongous” was taken), as in unsigned long int. As you can imagine, short and long refer to how big the number can be. int may be omitted: unsigned short x; or long y;.

If not specified, an int is signed. If a char is not specified as signed or unsigned, it is up to the compiler to decide which it is. It shouldn’t matter.

wchar_t (“wide character”) is a larger character type, used when char isn’t big enough, that is, for international characters. char8_t , char16_t , and char32_t are also for international characters.

Suffixes on the literal values – as in 5.0f or 42u  – are there to tell the compiler “this is a (f)loat, not a double,” “this is (u)nsigned,” and so on. Suffixes can be uppercase.

If you want to know just how big an int, long int, and so on can be, you can find out using #include <climits >, which defines constants for maximum and minimum values for the various types. You can get the size of one of these in bytes3 with sizeof : sizeof (int) or sizeof (myInt), where myInt is an int.

If you store values that are too big, they’ll wrap around; with signed, instead of a too-large positive number, you’ll have a negative number. This is almost never a problem. If it is, use a long int or a long long int.

For a complete list of basic types, see Appendix D.

Exercises
  1. 1.
    Write a program to determine whether the creature you just saw was a fairy, troll, elf, or some other magical creature, assuming you carried your computer into the Enchanted Forest. You pick the distinguishing features of each type of creature. A session might start like so:
    Welcome to the Enchanted Forest.
    This creature you have seen:
    Does it have wings (Y/N)? Y
    ...

    The user should be able to type either 'y' or 'Y' and either 'n' or 'N' to answer. (If the user types something that doesn’t make sense, you can assume that means “no.”)

     

switch

Consider this if statement , which prints whether a letter is as vowel, semivowel, or consonant:
// Print classification of letters as vowel, semivowel, consonant
if      (toupper (letter) == 'A') sout << "vowel";
else if (toupper (letter) == 'E') sout << "vowel";
else if (toupper (letter) == 'I') sout << "vowel";
else if (toupper (letter) == 'O') sout << "vowel";
else if (toupper (letter) == 'U') sout << "vowel";
else if (toupper (letter) == 'Y') sout << "semivowel";
else if (toupper (letter) == 'W') sout << "semivowel";
else                              sout << "consonant";

It will work, but there’s an easier way.

In BNF, a switch statement is
switch (<expression>)
{
case <value>: <action>*
    ...
[default : <action>*]
}

The * means “as many copies as you want, maybe zero.”

What this does: The expression in the parentheses is evaluated. (It has to be something you can count by – integers or characters, no floats, no doubles.) If it matches a particular value, the computer goes to that case <value> and executes whatever actions come after. If you specify a default action, that’s what happens if the expression doesn’t match anything.

Here’s that same piece of code, using a switch statement:
// Print classification of letters as vowel, semivowel, consonant
switch (toupper (letter))
{
case 'A':                    // if it's A, keep going...
case 'E':                    //    or if it's E (and so on)...
case 'I':
case 'O':
case 'U':   sout << "vowel"; //  ...and print "vowel" for all those cases
            break;
case 'Y':
case 'W':   sout << "semivowel";
            break;
default:    sout << "consonant";
}

If letter matches 'A', it does whatever action(s) it finds after case 'A', which in this case is sout << "vowel";. It keeps going till it finds break, which, just as before, means “leave this structure” – so at that point it leaves the switch statement. (Nobody will gripe at you for using break in this way; switch needs it.)

I usually include a default in a switch statement, to handle unexpected values (Example 5-5).
    sout << "Enter the class of a planet: ";
    ssin >> planetaryClassification;
    switch (planetaryClassification)
    {
    case 'J': sout << "Gas giant";       break;
    case 'M': sout << "Earthlike world"; break;
    case 'Y': sout << "'Demon' planet";  break;
        // ...
    default:  sout << "That's not a valid planetary classification.\n";
              sout << "Better watch some more Star Trek!";
    }
Example 5-5

Using a switch statement’s default to catch bad input

Antibugging

  • switch does what you wanted for that value; then it does the options that follow as well. This is the most common error with switch: forgetting the break. Solution: Go back and put breaks between the different options you wanted (in Example 5-5, 'J', 'M', and 'Y').

  • The compiler complains something about case labels and variables. This code has that problem:
    switch (myChar)
    {
    case 'P':
        int turns = MAXTURNS;
        playGame (turns);
        break;
    ...
    }
    It doesn’t like initializing a variable as part of a switch. No problem. We’ll just put {}’s around the area that needs the variable:
    switch (myChar)
    {
    case 'P':
          {
             int turns = MAXTURNS;
             playGame (turns);
             break;
          }
    ...
    }
Exercises
All these (of course) involve switch.
  1. 1.

    Write and test a function that, given a number, prints the associated ordinal: that is, for 1, print 1st; for 2, print 2nd; for 3, print 3rd; and for everything else, print the number plus “th.”

     
  2. 2.

    Have the user enter two single-digit numbers and return the sum. Here’s the twist: the numbers entered are in base 16 (“hexadecimal”). In base 16, we use 'A' to represent 10, 'B' for 11, 'C' for 12, and so on to 'F' for 15. You can give the result in base 10.

     
  3. 3.

    Menus are a time-honored (old-fashioned) way of getting user input. Make a menu offering to draw for the user a circle or a line or maybe some other shape and then draw the selected shape.