Chapter 1: Writing Your First C++ Program 13
Figure 1-1:
You must
uninstall earlier versions of Dev-C++ before you begin the installation process.
2. If you donÂ’t have to uninstall an old version of Dev-C++, skip to Step 4; if you do have to uninstall, abort the current installation process by closing the Run window.
DonÂ’t get upset if youÂ’ve never even heard of Dev-C++ and you still get the warning message. ItÂ’s just a reminder.
3. Okay, if youÂ’re on this step, youÂ’re uninstalling: Open the Dev-CPP
folder and double-click the Uninstall.exe file there.
The uninstall program does its thing, preparing the way for the new instal_ lation; the End User Legal Agreement (commonly known as the EULA) appears.
4. Read the EULA and then click OK if you can live with its provisions. Nope, the package really wonÂ’t install itself if you donÂ’t accept. Assuming you do click OK, Dev-C++ opens the window shown in Figure 1-2 and offers you some installation options. The defaults are innocuous, with two exceptions:
• You must leave the Mingw compiler system. . . option enabled.
• The Associate C and C++ Files to Dev-C++ option means that double- clicking a .CPP file automatically opens Dev-C++ rather than some other program (such as Visual C++ .NET, for example). It is possible, but difficult, to undo this association.
DonÂ’t check this option if you also have Visual Studio.NET installed. Dev- C++ and Visual Studio.NET coexist peacefully on the same machine, but what Visual Studio has done, let no man cast assunder. You can still open your .CPP files with Dev-C++ by right-clicking on the file and selecting Open With. Personally, I prefer to use this option, even with Visual Studio.NET installed. It doesnÂ’t cause any problems, and Dev-C++ starts a lot faster than Visual Studio.
14 Part I: Introduction to C++ Programming
Figure 1-2: The default installation options should be acceptable to most users.
5. Click the Next button.
The installation program asks where you want it to install Dev-C++, using a message like that shown in Figure 1-3.
Figure 1-3: The default location for the Dev-C++ environment is provided.
6. Accept the default directory, c:\Dev-CPP.
Don’t install Dev-C++ in the directory \Program Files with all the other executables. That’s because Dev-C++ doesn’t do well with directories that contain spaces in their names. I haven’t experimented much along these lines, but I believe you can use any other directory name without any special characters other than ‘_’. It’s safer just to accept the default.
7. Make sure you have enough room for the program, wherever you decide to put it.
The Dev-C++ environment uses only a paltry 45MB, but itÂ’s always good practice to check.
8. Click Install.
At first, nothing seems to happen. Then Dev-C++ gets going, copying a whole passel of files to the Dev-CPP directory — putting absolutely noth_ ing in the Windows home directory. Figure 1-4 displays the eventual result.
Chapter 1: Writing Your First C++ Program 15
Figure 1-4: The Dev- C++ installation process unzips a large number of mostly small files.
While the installation is going on, Dev-C++ presents a window that asks whether you want to install for all users once it’s done copying files onto your hard drive. That question boils down to this: If someone else logs on to your computer, do you want her or him to be able to execute Dev- C++? (The answer is “Yes” in my case.)
9. Choose whether you want to install for all users, and then click the
Close button to complete installation of the package.
Dev-C++ starts immediately, so you can set its options properly for your needs. (Yep, thereÂ’s more work to do. But you knew that. Read on.)
Setting the options
As you probably know if youÂ’ve spent more than a coffee breakÂ’s worth of time installing software, setting options is a procedure unto itself. In this case, Dev- C++ has two options that must be set before you can use it. Set Â’em as follows:
1. Choose Tools➪Compiler Options.
You can change these settings at any time, but now is as good as any.
2. Choose the Settings tab.
3. Choose Code Generation from the menu on the left.
Make sure that the Enable Exception Handling is enabled, as shown in Figure 1-5. (If it isnÂ’t, click on the option box to display the two choices and select Yes.)
4. Choose Linker and make sure the Generate Debugging Information option is enabled.
Figure 1-6 shows you what to look for.
16 Part I: Introduction to C++ Programming
Figure 1-5: The Enable Exception Handling option must be enabled.
Figure 1-6: The Generate Debugging Information option must be enabled.
5. Choose OK.
Installation is now complete! (Your options are saved automatically.)
Creating Your First C++ Program
In this section, you create your first C++ program. You first enter the C++ code into a file called CONVERT.CPP, and then convert the C++ code into an exe_ cutable program.
Chapter 1: Writing Your First C++ Program 17
Entering the C++ code
The first step to creating any C++ program is to enter C++ instructions using a text editor. The Dev-C++ user interface is built around a program editor specifi_ cally designed to create C++ programs.
1. Click Start➪Programs➪Bloodshed Dev-C++➪Dev-C++ to start up the
Dev-C++ tool.
The Dev-C++ interface looks fundamentally like that of any other Win_ dows program — perhaps a little clunkier, but a Windows application nonetheless.
This is a lot of clicking. My personal preference is to create a shortcut on the desktop. To create a shortcut, double-click My Computer. Now double-click the Local Disk (C. Finally, double-click Dev-CPP — whew! Right-click the file devcpp.exe and choose Create Shortcut from the drop down menu. Drag the Shortcut to devcpp.exe file onto your desktop (or some other easily accessible place). From now on, you can just double-click the shortcut to start Dev-C++.
2. Choose File➪New➪Source File.
Dev-C++ opens a blank window wherein you get to enter your new code. Don’t worry if you find yourself wishing you knew what to enter right now — that’s why you bought this book.
3. Enter the following program exactly as written.
Don’t worry too much about indentation or spacing — it isn’t critical whether a given line is indented two or three spaces, or whether there are one or two spaces between two words. C++ is case sensitive, how_ ever, so you need to make sure everything is lowercase.
You can cheat and copy the Conversion.cpp file contained on the
enclosed CD-ROM in directory \CPP_Programs\Chap01.
//
// Program to convert temperature from Celsius degree
// units into Fahrenheit degree units:
// Fahrenheit = Celsius * (212 - 32)/100 + 32
//
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
18 Part I: Introduction to C++ Programming
// enter the temperature in Celsius
int celsius;
cout << “Enter the temperature in Celsius:”;
cin >> celsius;
// calculate conversion factor for Celsius
// to Fahrenheit
int factor;
factor = 212 - 32;
// use conversion factor to convert Celsius
// into Fahrenheit values
int fahrenheit;
fahrenheit = factor * celsius/100 + 32;
// output the results (followed by a NewLine)
cout << “Fahrenheit value is:”;
cout << fahrenheit << endl;
// wait until user is ready before terminating program
// to allow the user to see the program results
system(“PAUSE”);
return 0;
}
4. Choose Save As under the File menu. Then type in the program name and press Enter.
I know that it may not seem all that exciting, but youÂ’ve just created your first C++ program!
For purposes of this book, I created a folder CPP_Programs. Within this, I created Chap01. Finally, I saved the program with the name Conversion. cpp. Note that Dev-C++ won’t work properly with directory names that contain spaces. (It doesn’t have a problem with names longer than eight characters in length — thank goodness!)
Building your program
After youÂ’ve saved your Conversion.cpp C++ source file to disk, itÂ’s time to generate the executable machine instructions.
To build your Conversion.cpp program, you choose Execute➪Compile from the menu or press F9 — or you can even click that cute little icon with four colored squares on the menu bar (use the Tool Tips to see which one I’m talk_ ing about). In response, Dev-C++ opens a compiling window. Nothing will happen at first (sshh . . . it’s thinking). After a second or two, Dev-C++ seems to take off, compiling your program with gusto. If all goes well, a window like that shown in Figure 1-7 appears.
Chapter 1: Writing Your First C++ Program 19
Figure 1-7: The user is rewarded with a simple Done message if his program is error free.
Dev-C++ generates a message if it finds any type of error in your C++ program — and coding errors are about as common as snow in Alaska. You’ll undoubtedly encounter numerous warnings and error messages, probably even when enter_ ing the simple Conversion.cpp. To demonstrate the error-reporting process, let’s change Line 17 from cin >> celsius; to cin >>> celsius;.
This seems an innocent enough offense — forgivable to you and me perhaps, but not to C++. Dev-C++ opens a Compiler tab, as shown in Figure 1-8. The mes_ sage parse error before ‘> is perhaps a little terse, but descriptive. To get rid of the message, remove the extra > and recompile.
Figure 1-8: Bad little programs generate error messages in the Compiler window.
20 Part I: Introduction to C++ Programming
Why is C++ so picky?
In the example given here, C++ could tell right away — and without a doubt — that I had
screwed up. However, if C++ can figure out what I did wrong, why doesnÂ’t it just fix the prob_
lem and go on?
The answer is simple but profound. C++ thinks that I mistyped the >> symbol, but it may be mistaken. What could have been a mistyped
command may actually be some other, com_
pletely unrelated error. Had the compiler simply
corrected the problem, C++ would have masked the real problem.
Finding an error buried in a program that
builds without complaining is difficult and time- consuming. ItÂ’s far better to let the compiler find
the error for you if at all possible. Generating
a compiler error is a waste of the computerÂ’s
time — forcing me to find a mistake that C++ could have caught is a waste of my time. Guess which one I vote for?
The term parse means to convert the C++ commands into something that the machine-code-generating part of the process can work with.
There was once a language that tried to fix simple mistakes like this for you.
From my personal experience, I can tell you it was a waste of time — because
(except for very simple cases) the compiler was almost always wrong. At least it warned me of the problem so I could fix it myself.
Executing Your Program
It’s now time to execute your new creation . . . that is, to run your program. You will run the CONVERT.EXE program file and give it input to see how well it works. To execute the Conversion program, click Execute➪Run or press Ctrl+F10.
(I have no idea how they selected function keys. I would think that an action as common as executing a program would warrant its own function key — something without a Control or Shift key to hold down — but maybe that’s just me.)
A window opens immediately, requesting a temperature in Celsius. Enter a known temperature, such as 100 degrees. After you press Enter, the program returns with the equivalent temperature of 212 degrees Fahrenheit as follows:
Enter the temperature in Celsius:100
Fahrenheit value is:212
Press any key to continue . . .
Chapter 1: Writing Your First C++ Program 21
The message Press any key gives you the opportunity to read what youÂ’ve entered before it goes away. Press Enter, and the window (along with its con_ tents) disappears. Congratulations! You just entered, built, and executed your first C++ program.
Dev-C++ is not Windows
Notice that Dev-C++ is not truly intended for developing Windows programs. In theory, you can write a Windows application by using Dev-C++, but it isnÂ’t easy. (ThatÂ’s so much easier in Visual Studio.NET.)
Windows programs show the user a very visually oriented output, all nicely arranged in onscreen windows. Convesion.exe is a 32-bit program that exe_ cutes under Windows, but it’s not a “Windows” program in the visual sense.
If you donÂ’t know what 32-bit program means, donÂ’t worry about it. As I said earlier, this book isnÂ’t about writing Windows programs. The C++ programs you write in this book have a command line interface executing within an MS_ DOS box.
Budding Windows programmers shouldn’t despair — you didn’t waste your money. Learning C++ is a prerequisite to writing Windows programs. I think that they should be mastered separately: C++ first, Windows second.
Dev-C++ help
Dev-C++ provides a Help menu item. Choose Help followed by Help on Dev C++ to open up a typical Windows help box. Help is provided on various aspects of the Dev-C++ development package but not much else. Noticeably lacking is help on the C++ language itself. Click a topic of interest to display help.
Reviewing the Annotated Program
Entering data in someone else’s program is about as exciting as watching some_ one else drive a car. You really need to get behind the wheel itself. Programs are a bit like cars as well. All cars are basically the same with small differences and additions — OK, French cars are a lot different than other cars, but the point is still valid. Cars follow the same basic pattern — steering wheel in front of you, seat below you, roof above you and stuff like that.
22 Part I: Introduction to C++ Programming Similarly, all C++ programs follow a common pattern. This pattern is already present in this very first program. We can review the Conversion program by looking for the elements that are common to all programs.
Examining the framework for all C++ programs
Every C++ program you write for this book uses the same basic framework, which looks a lot like this:
//
// Template - provides a template to be used as the starting
// point
//
// the following include files define the majority of
// functions that any given program will need
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// your C++ code starts here
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
Without going into all the boring details, execution begins with the code con_ tained in the open and closed braces immediately following the line begin_ ning main().
I have copied this code into a file called Template.cpp located in the main
CPP_Programs folder on the enclosed CD-ROM.
Clarifying source code with comments
The first few lines in Conversion.cpp appear to be freeform text. Either this code was meant for human eyes or C++ is a lot smarter than I give it credit for. These first six lines are known as comments. Comments are the programmerÂ’s
Chapter 1: Writing Your First C++ Program 23
explanation of what he or she is doing or thinking when writing a particular code segment. The compiler ignores comments. Programmers (good program_ mers, anyway) donÂ’t.
A C++ comment begins with a double slash (//) and ends with a newline. You can put any character you want in a comment. A comment may be as long as you want, but it’s customary to keep comment lines to no more than 80 char_ acters across. Back in the old days — “old” is relative here — screens were limited to 80 characters in width. Some printers still default to 80 characters across when printing text. These days, keeping a single line to under 80 char_ acters is just a good practical idea (easier to read, less likely to cause eye_ strain, the usual).
A newline was known as a carriage return back in the days of typewriters — when the act of entering characters into a machine was called typing and not keyboarding. A newline is the character that terminates a command line.
C++ allows a second form of comment in which everything appearing after a
/* and before a */ is ignored; however, this form of comment isnÂ’t normally used in C++ anymore. (Later in this book, I describe the one case in which this type of comment is applied.)
It may seem odd to have a command in C++ (or any other programming lan_ guage) thatÂ’s specifically ignored by the computer. However, all computer lan_ guages have some version of the comment. ItÂ’s critical that the programmer explain what was going through her mind when she wrote the code. A pro- grammerÂ’s thoughts may not be obvious to the next colleague who picks up her program and tries to use it or modify it. In fact, the programmer herself may forget what her program meant if she looks at it months after writing the original code and has left no clue.
Basing programs on C++ statements
All C++ programs are based on what are known as C++ statements. This sec_ tion reviews the statements that make up the program framework used by the Conversion.cpp program.
A statement is a single set of commands. All statements other than comments end with a semicolon. (ThereÂ’s a reason that comments donÂ’t end with a semicolon, but itÂ’s obscure. To my mind, comments should end in semicolons as well, for consistencyÂ’s sake. Why nobody asked me about that remains a mystery.)
24 Part I: Introduction to C++ Programming Program execution begins with the first C++ statement after the open brace and continues through the listing, one statement at a time.
As you look through the program, you can see that spaces, tabs, and newlines appear throughout the program. In fact, I place a newline after every state_
ment in this program. These characters are collectively known as white space
because you canÂ’t see them on the monitor.
You may add white space anywhere you like in your program to enhance readability — except in the middle of a word:
See wha
t I mean?
Although C++ may ignore white space, it doesnÂ’t ignore case. In fact, itÂ’s case sensitive to the point of obsession. The variable fullspeed and the variable FullSpeed have nothing to do with each other. While the command int may be understood completely, C++ has no idea what INT means.
Writing declarations
The line int nCelsius; is a declaration statement. A declaration is a state_ ment that defines a variable. A variable is a “holding tank” for a value of some type. A variable contains a value, such as a number or a character.
The term variable stems from algebra formulae of the following type:
x = 10
y = 3 * x
In the second expression, y is set equal to 3 times x, but what is x? The vari_ able x acts as a holding tank for a value. In this case, the value of x is 10, but we could have just as well set the value of x to 20 or 30 or –1. The second for_ mula makes sense no matter what the value of x.
In algebra, youÂ’re allowed to begin with a statement, such as x = 10. In C++, the programmer must first define the variable x before she can use it.
In C++, a variable has a type and a name. The variable defined on Line 11 is called celsius and declared to hold an integer. (Why they couldnÂ’t have just said integer instead of int, IÂ’ll never know. ItÂ’s just one of those things you learn to live with.)
Chapter 1: Writing Your First C++ Program 25
The name of a variable has no particular significance to C++. A variable must begin with the letters A through Z or a through z. All subsequent characters must be a letter, a digit 0 through 9 or an underscore (_). Variable names can be as long as you want to make them.
ItÂ’s convention that variable names begin with a lowercase letter. Each new word within a variable begins with a capital letter, as in myVariable.
Try to make variable names short but descriptive. Avoid names such as x because x has no particular meaning. A variable name such as lengthOfLine Segment is much more descriptive.
Generating output
The lines beginning with cout and cin are known as input/output statements, often contracted to I/O statements. (Like all engineers, programmers love con_ tractions and acronyms.)
The first I/O statement says output the phrase Enter the temperature in Celsius to cout (pronounced “see-out”). cout is the name of the standard C++ output device. In this case, the standard C++ output device is your monitor.
The next line is exactly the opposite. It says, in effect, Extract a value from the C++ input device and store it in the integer variable celsius. The C++ input device is normally the keyboard. What weÂ’ve got here is the C++ analog to the algebra formula x = 10 just mentioned. For the remainder of the program, the value of celsius is whatever the user enters there.
Calculating Expressions
All but the most basic programs perform calculations of one type or another. In C++, an expression is a statement that performs a calculation. Said another way, an expression is a statement that has a value. An operator is a command that generates a value.
For example, in the Conversion example program — specifically in the two lines marked as a calculation expression — the program declares a vari_ able factor and then assigns it the value resulting from a calculation. This par_ ticular command calculates the difference of 212 and 32; the operator is the minus sign (–), and the expression is 212–32.
26 Part I: Introduction to C++ Programming
Storing the results of expression
The spoken language can be very ambiguous. The term equals is one of those ambiguities. The word equals can mean that two things have the same value as in “5 cents equals a nickel.” Equals can also imply assignment, as in math when you say that “y equals 3 times x.”
To avoid ambiguity, C++ programmers call the assignment operator, which says
(in effect), Store the results of the expression to the right of the equal sign in the variable to the left. Programmers say that “factor is assigned the value
212 minus 32.”
Never say “factor is equal to 212 minus 32.” You’ll hear this from some lazy types, but you and I know better.
Examining the remainder of
Conversion.cpp
The second expression in Conversion.cpp presents a slightly more compli_ cated expression than the first. This expression uses the same mathematical symbols: * for multiplication, / for division and, + for addition. In this case, how_ ever, the calculation is performed on variables and not simply on constants.
The value contained in the variable called factor (calculated immediately prior, by the way) is multiplied by the value contained in celsius (which was input from the keyboard). The result is divided by 100 and summed with 32. The result of the total expression is assigned to the integer variable fahrenheit.
The final two commands output the string Fahrenheit value is: to the display, followed by the value of fahrenheit — and all so fast that the user scarcely knows it’s going on.
Chapter 2
Declaring Variables Constantly
In This Chapter
Declaring variables
Declaring different types of variables
Using floating-point variables
Declaring and using other variable types
The most fundamental of all concepts in C++ is the variable — a variable is like a small box. You can store things in the box for later use, particularly
numbers. The concept of a variable is borrowed from mathematics. A state_
ment such as
x = 1
stores the value 1 in the variable x. From that point forward, the mathemati_ cian can use the variable x in place of the constant 1 — until she changes the value of x to something else.
Variables work the same way in C++. You can make the assignment
x = 1;
From that point forward in the program, until the value of x is changed, any references to x are the same as referencing 1. That is, the value of x is 1.
Unfortunately, C++ has a few more concerns about variables than the mathe_
matician does. This chapter deals with the care and feeding of variables in C++.
Declaring Variables
C++ saves numeric values in small storage boxes known as variables. Mathe_ maticians throw variables around with abandon. A mathematician might (for example) write down something like the following:
28 Part I: Introduction to C++ Programming
(x + 2) = y / 2
x + 4 = y
solve for x and y
Any reader whoÂ’s had algebra realizes right off that the mathematician has introduced the variables x and y. But C++ isnÂ’t that smart (computers may be fast, but theyÂ’re stupid).
You have to announce each variable to C++ before you can use it. You have to say something soothing like this:
int x;
x = 10;
int y;
y = 5;
These lines of code declare that a variable x exists, that it is of type int, and that a variable y of type int also exists. (The next section discusses vari_ able types.) You can declare variables (almost) anywhere you want in your program — as long as you declare the variable before you use it.
Declaring Different Types of Variables
If youÂ’re on friendly terms with math (hey, arenÂ’t we all?), you probably think of a variable in mathematics as an amorphous box capable of holding what_ ever you might choose to store in it. You might easily write something like the following:
x = 1
x = 2.3
x = “this is a sentence”
x = Texas
Alas, C++ is not that flexible. (On the other hand, C++ can do things that people canÂ’t do, such as add a billion numbers or so in a second, so letÂ’s not get too uppity.) To C++, there are different types of variables just as there are different types of storage bins. Some storage bins are so small that they can only handle a single number. It takes a larger bin to handle a sentence. Of course, no bin is large enough to hold Texas (maybe Rhode Island or Delaware).
You have to tell C++ what size bin you need before you can use a C++ variable. In addition, different types of variables have different properties. So far, you have only seen the int type of variable in this chapter:
Chapter 2: Declaring Variables Constantly 29
int x;
x = 1;
The variable type int is the C++ equivalent of an integer — a number that has no fractional part. (Integers are also known as counting numbers or whole numbers.)
Integers are great for most calculations. You can make it up through most (if not all) of elementary school with integers. It isnÂ’t until you reach age 11 or so that they start mucking up the waters with fractions. The same is true in C++: More than 90 percent of all variables in C++ are declared to be of type int.
Unfortunately, int variables don’t always work properly in a program. If (for example) you worked through the temperature-conversion program in Chap_ ter 1, the program has a potential problem — it can only handle integer tem_ peratures — whole numbers that don’t have a fractional part. This limitation of using only integers doesn’t affect daily use because it isn’t likely that some_ one (other than a meteorologist) would get all excited about entering a frac_ tional temperature (such as 10.5 degrees). The lurking problem is not at all obvious: The conversion program lops off the fractional portion of tempera_ tures that it calculates, and just keeps going without complaint. This can result in a lapse of accuracy that can be serious — for example, you wouldn’t want to come up a half mile short of the runway on your next airplane trip due to a navigational round-off.
Reviewing the limitations of integers in C++
The int variable type is the C++ version of an integer. int variables suffer the same limitations as their counting-number integer equivalents in math do.
Integer round-off
Consider the problem of calculating the average of three numbers. Given three int variables — nValue1, nValue2, and nValue3 — an equation for calculat_ ing the average is
int nAverage; int nValue1; int nValue2; int nValue3;
nAverage =(nValue1 + nValue2 + nValue3) / 3;
Because all three values are integers, the sum is assumed to be an integer. Given the values 1, 2, and 2, the sum is 5. Divide that by 3, and you get 12â„3, or
1.666. Given that all three variables nValue1, nValue2, and nValue3 are inte_ gers, the sum is also assumed to be an integer. The result of the division is also an integer. The resulting value of nAverage is unreasonable but logical: 1.
30 Part I: Introduction to C++ Programming Lopping off the fractional part of a number is called truncation, or rounding off. For many applications, truncation isnÂ’t a big deal. Some folks might con_ sider its results reasonable (not mathematicians or bookies, of course), but integer truncation can create math mayhem in computer programs. Consider the following equivalent formulation:
int nAverage; int nValue1; int nValue2; int nValue3;
nAverage = nValue1/3 + nValue2/3 + nValue3/3;
Plugging in the same 1, 2, and 2 values, the resulting value of nAverage is (talk about logical-but-unreasonable) 0. To see how this can occur, consider that
13 truncates to 0, 23 truncates to 0, and 23 truncates to 0. The sum of 0, 0, and
0 is zero. (Sort of like that old song: “Nothing from nothing leaves nothing, ya gotta be something . . .”) You can see that integer truncation can be completely unacceptable.
Limited range
A second problem with the int variable type is its limited range. A normal int variable can store a maximum value of 2,147,483,647 and a minimum value of –2,147,483,648 — roughly from positive 2 billion to negative 2 billion, for a total range of about 4 billion.
Two billion is a very large number: plenty big enough for most uses. But it’s not large enough for some applications — for example, computer technology. In fact, your computer probably executes faster than 2 gigahertz, depending upon how old your computer is. (Giga is the prefix meaning billion.) A single strand of communications fiber — the kind that’s been strung from one end of the country to the other — can handle way more than 2 billion bits per second.
C++ offers a little help by allowing you declare an integer to be unsigned, mean_ ing that it cannot be negative. An unsigned int value type can represent a number from 0 to 4,294,967,295, should the need arise for some unimaginable reason.
You can declare a variable simply unsigned. The int is implied.
Solving the truncation problem
The limitations of int variables can be unacceptable in some applications. Fortunately, C++ understands decimal numbers. A decimal number can have a nonzero fractional part. (Mathematicians also call those real numbers.) Decimal numbers avoid many of the limitations of int type integers. Notice that a decimal number “can have” a nonzero fractional part. In C++, the
number 1.0 is just as much a decimal number as 1.5. The equivalent integer is written simply as 1. Decimals numbers can also be negative, like –2.3.
Chapter 2: Declaring Variables Constantly 31
When you declare variables in C++ that are decimal numbers, you identify them as double precision floating-point values. (Yes, there is such a critter as a
“single precision floating-point variable,” but stick with me here.) The term floating-point means the decimal point is allowed to float back and forth, iden_ tifying as many “decimal places” as necessary to express the value. Floating- point variables are declared in the same way as int variables:
double dValue1;
From this point forward, the variable dValue1 is declared to be a double. Once declared, you cannot change the type of a variable. dValue1 is now a double and will be a double for the remainder of its natural instructions. To see how floating-point numbers fix the truncation problem inherent with integers, con_ vert all the int variables to double. HereÂ’s what you get:
double dValue;
dValue = 1.0/3.0 + 2.0/3.0 + 2.0/3.0;
is equivalent to
dValue = 0.333... + 0.666... + 0.666...;
which results in the value
dValue = 1.666...;
I have written the value 1.6666 . . . as if the number of trailing 6s goes on forever. This is (not necessarily) the case. There’s a limit to the number of digits of accuracy of a double variable — but it’s a lot more than I can keep track of.
The programs IntAverage and FloatAverage are available on the enclosed CD in the CPP_Programs\Chap02 directory to demonstrate this averaging example.
Looking at the limits of floating-point numbers
Although floating-point variables can solve many calculation problems such as truncation, they have some limitations themselves — in effect, the reverse of those associated with integer variables. double variables can’t be used as counting numbers, they’re more difficult for the computer to handle, and they also suffer from round-off error (though not nearly to the same degree as int variables).
32 Part I: Introduction to C++ Programming
Counting
You cannot use floating-point variables in applications where counting is impor_ tant. This includes C++ constructs, which requires counting ability. C++ canÂ’t verify which whole number value is meant by a given floating-point number.
For example, itÂ’s clear that 1.0 is 1. But what about 0.9 or 1.1? Should these also be considered as 1? C++ simply avoids the problem by insisting on using int values when counting is involved.
Calculation speed
Historically, a computer processor can process integer arithmetic quicker than it can floating-point arithmetic. Thus, while a processor can add 1 million inte_ ger numbers in a given amount of time, the same processor may be able to perform only 200,000 floating-point calculations during the same period. (Not surprisingly, I couldnÂ’t even get around to reading the first value.)
Calculation speed is becoming less of a problem as microprocessors increase their capabilities. Most modern processors contain special calculation cir_ cuitry for performing floating-point calculations almost as fast as integer calculations.
Loss of accuracy
Floating-point variables cannot solve all computational problems. Floating- point variables have a limited precision of about 6 digits — an extra-economy size, double-strength version of float can handle some 15 significant digits with room left over for lunch.
To evaluate the problem, consider that 13 is expressed as 0.333 . . . in a con_ tinuing sequence. The concept of an infinite series makes sense in math, but not to a computer. The computer has a finite accuracy. Average 1, 2, and 2
(for example), and you get 1.666667.
C++ can correct for many forms of round-off error. For example, in output, C++ can determine that instead of 0.999999, that the user really meant 1. In other cases, even C++ cannot correct for round-off error.
Not-so-limited range
Although the double data type has a range much larger than that of an inte_ ger, itÂ’s still limited. The maximum value for an int is a skosh more than 2 bil_ lion. The maximum value of a double variable is roughly 10 to the 38th power. ThatÂ’s 1 followed by 38 zeroes; it eats 2 billion for breakfast. (ItÂ’s even more than the national debt, at least at the time of this writing.)
Only the first 13 digits or so have any meaning; the remaining 25 digits suffer from floating-point round-off error.
Chapter 2: Declaring Variables Constantly 33
Declaring Variable Types
So far this chapter has been trumpeting that variables must be declared and that they must be assigned a type. Fortunately (ta-dah!), C++ provides a num_ ber of different variable types. See Table 2-1 for a list of variables, their advan_ tages, and limitations.
Table 2-1 C++ Variables
Variable Example Purpose
int 1 A simple counting number, either positive or negative.
unsigned int 1U A counting number thatÂ’s only non-negative. long 10L A potentially larger version of int. There is no difference between long and int with
Dev-C++ and Microsoft Visual C++.NET.
unsigned long 10UL A nonnegative long integer.
float 1.0F A single precision real number. This smaller version takes less memory than a double but has less accuracy and a smaller range.
double 1.0 A standard floating-point variable.
char ‘c’ A single char variable stores a single alpha_ betic or digital character. Not suitable for arithmetic.
string “this is A string of characters forms a sentence or
a string” phrase.
bool true The only other value is false. No I mean, it’s really false. Logically false. Not “false” as in fake or ersatz or . . . never mind.
It may seem odd that the standard floating length variable is called double while the “off size” is float. In days gone by, memory was an expensive asset — you could reap significant space savings by using a float variable. This is no longer the case. That, combined with the fact that modern processors perform double precision calculations at the same speed as float, makes the double the default. Bigger is better, after all.
34 Part I: Introduction to C++ Programming The following statement declares a variable lVariable as type long and sets it equal to the value 1, while dVariable is a double set to the value 1.0:
// declare a variable and set it to 1
long lVariable;
lVariable = 1;
// declare a variable of type double and set it to 1.0
double dVariable;
dVariable = 1.0;
You can declare a variable and initialize it in the same statement:
int nVariable = 1; // declare a variable and
// initialize it to 1
Although such declarations are common, the only benefit to initializing a vari_
able in the declaration is that it saves typing.
A char variable can hold a single character; a string (which isnÂ’t really a vari_
able but works like one for most purposes) holds a string of characters. Thus,
‘C’ is a char that contains the character C, whereas “C” is a string with one character in it. A rough analogy is that a ‘C’ corresponds to a nail in your hand, whereas “C” corresponds to a nail gun with one nail left in the magazine. (Chap_ ter 9 describes strings in detail.)
If an application requires a string, youÂ’ve gotta provide one, even if the string contains only a single character. Providing nothing but the character just wonÂ’t do the job.
Types of constants
A constant is an explicit number or character (such as 1, 0.5, or ‘c’) that doesn’t change. As with variables, every constant has a type. In an expression such as n = 1; (for example), the constant 1 is an int. To make 1 a long integer,
write the statement as n = 1L;. The analogy is as follows: 1 represents a single ball in the bed of a pickup truck, whereas 1L is a single ball in the bed of a dump truck. The ball is the same, but the capacity of its container is much larger.
Following the int to long comparison, 1.0 represents the value 1, but in a floating-point container. Notice, however, that the default for floating-point constants is double. Thus, 1.0 is a double number and not a float.
true is a constant of type bool. However, “true” (note the quotation marks) is a string of characters that spell out the word true. In addition, in keeping with C++’s attention to case, true is a constant, but TRUE has no meaning.
Chapter 2: Declaring Variables Constantly 35
Special characters
You can store any printable character you want in a char or string vari_ able. You can also store a set of non-printable characters that is used as character constants. See Table 2-2 for a description of these important non- printable characters.
Table 2-2 Special Characters
Character Constant Action
‘\n’ newline
‘\t’ tab
‘\0’ null
‘\\’ backslash
You have already seen the newline character at the end of strings. This char_ acter breaks a string and puts the parts on separate lines. A newline charac_ ter may appear anywhere within a string. For example,
“This is line 1\nThis is line 2”
appears on the output as
This is line 1
This is line 2
Similarly, the \t tab character moves output to the next tab position. (This position can vary, depending on the type of computer youÂ’re using to run the program.) Because the backslash character is used to signify special charac_ ters, a character pair for the backslash itself is required. The character pair
\\ represents the backslash.
C++ collision with file names
Windows uses the backslash character to sep_ arate folder names in the path to a file. (This is a remnant of MS-DOS that Windows has not been able to shake.) Thus, Root\FolderA\ File represents File within FolderA, which is a subdirectory of Root.
Unfortunately, MS-DOSÂ’s use of backslash con_
flicts with the use of backslash to indicate an escape character in C++. The character \\ is a backslash in C++. The MS-DOS path Root\ FolderA\File is represented in C++ string as Root\\FolderA\\File.
36 Part I: Introduction to C++ Programming
Are These Calculations Really Logical?
C++ provides a logical variable called bool. The type bool comes from Boolean, the last name of the inventor of the logical calculus. There are two values for a boolean variable: true and false.
There are actually calculations that result in the value bool. For example, “x is equal to y” is either true or false.
Mixed Mode Expressions
C++ allows you to mix variable types in a single expression. That is, you are allowed to add an integer with a double precision floating-point value. In the following expression, for example, nValue1 is allowed to be an int:
// in the following expression the value of nValue1
// is converted into a double before performing the
// assignment int nValue1 = 1;
nValue1 + 1.0;
An expression in which the two operands are not the same type is called a mixed-mode expression. Mixed-mode expressions generate a value whose type is equal to the more capable of the two operands. In this case, nValue1 is con_ verted to a double before the calculation proceeds. Similarly, an expression of one type may be assigned to a variable of a different type, as in the following statement:
// in the following assignment, the whole
// number part of fVariable is stored into nVariable double dVariable = 1.0;
int nVariable;
nVariable = dVariable;
You can lose precision or range if the variable on the left side of the assignment is smaller. In the previous example, C++ truncates the value of dVariable
before storing it in nVariable.
Converting a larger value type into a smaller value type is called demotion, whereas converting values in the opposite direction is known as promotion. Programmers say that the value of int variable nVariable1 is promoted to a double as expressions such as the following:
int nVariable1 = 1;
double dVariable = nVariable1;
Chapter 2: Declaring Variables Constantly 37
Naming conventions
You may have noticed that the name of each
variable begins with a special character that seems to have nothing to do with the name. These special characters are shown in the fol_
lowing table. You can immediately recognize
dVariable as a variable of type double by using this convention.
These leading characters help the programmer keep track of the variable type. Thus, you can immediately identify the following as a mixed- mode assignment of a long variable to an int variable:
nVariable = lVariable;
These leading characters have no significance
Character
Type to C++. You donÂ’t need to adopt any naming scheme at all if you donÂ’t want to. HereÂ’s what
n int
l long
f float
d double
c character
sz string
you get:
double myVariable;
int someIntValue;
double nThisDoesntEvenMatch;
I used this first-letter-naming convention in this
chapter to simplify the discussion; many pro_
grammers use this naming scheme all the time.
Mixed-mode expressions are not a good idea. Avoid forcing C++ to do your conversions for you.
38 Part I: Introduction to C++ Programming
Chapter 3
Performing Mathematical
Operations
In This Chapter
Defining mathematical operators in C++ Using the C++ mathematical operators Identifying expressions
Increasing clarity with special mathematical operators
A mathematician uses more than just the variables described in Chap_
ter 2. A mathematician must do something with those variables: She can add them together, subtract them, multiply them, and perform an almost end_ less list of other operations.
C++ offers the same set of basic operations: C++ programs can multiply, add, divide, and so forth. Programs have to be able to perform these operations in order to get anything done. What good is an insurance program if it canÂ’t cal_ culate how much youÂ’re supposed to (over) pay?
C++ operations look like the arithmetic operations you would perform on a piece of paper, except you have to declare any variables before you can use them (as detailed in Chapter 2):
int var1;
int var2 = 1;
var1 = 2 * var2;
Two variables, var1 and var2, are declared. var2 is initialized to 1. var1 is assigned the value resulting from the calculation two times the value of var2.
This chapter describes the complete set of C++ mathematical operators.
40 Part I: Introduction to C++ Programming
Performing Simple Binary Arithmetic
A binary operator is one that has two arguments. If you can say var1 op var2, op must be a binary operator. The most common binary operators are the simple operations you performed in grade school. The binary operators are flagged in Table 3-1.
Table 3-1 Mathematical Operators in Order of Precedence
Precedence Operator Meaning
1 + (unary) Effectively does nothing
1 - (unary) Returns the negative of its argument
2 ++ (unary) Increment
2 -- (unary) Decrement
3 * (binary) Multiplication
3 / (binary) Division
3 % (binary) Modulo
4 + (binary) Addition
4 - (binary) Subtraction
5 =, *=,%=,+=,-= (special) Assignment types
Multiplication, division, modulus, addition, and subtraction are the operators used to perform arithmetic. In practice, they work just like the familiar arith_ metic operations as well. For example, using the binary operator for division with a float variable looks like this:
float var = 133 / 12;
Each of the binary operators has the conventional meaning that you studied in grammar school — with one exception. You may not have encountered modulus in your studies.
The modulus operator (%) works much like division, except it produces the remainder after division instead of the quotient. For example, 4 goes into 15 three times with a remainder of 3. Expressed in C++ terms, 15 modulus 4 is 3.
int var = 15 % 4; // var is initialized to 3
Chapter 3: Performing Mathematical Operations 41
Because programmers are always trying to impress nonprogrammers with the simplest things, C++ programmers define modulus as follows:
IntValue % IntDivisor
This expression is equal to
IntValue - (IntValue / IntDivisor) * IntDivisor
Try it out on this example:
15 % 4 is equal to 15 - (15/4) * 4
15 - 3 * 4
15 - 12
3
Modulus is not defined for floating-point variable because it depends on the
round-off error inherent in integers. (I discuss round-off errors in Chapter 2.)
Decomposing Expressions
The most common type of statement in C++ is the expression. An expression is a C++ statement with a value. Every expression has a type (such as int, double, char, and so on). A statement involving any mathematical operator is an expression since all these operators return a value. For example, 1 + 2 is an expression whose value is 3 and type is int. (Remember that constants without decimal points are ints.)
Expressions can be complex or extremely simple. In fact, the statement 1 is an expression because it has a value (1) and a type (int). There are five expres_ sions in the following statement:
z = x * y + w;
The expressions are
x * y + w x * y
x y
w
42 Part I: Introduction to C++ Programming An unusual aspect of C++ is that an expression is a complete statement. Thus, the following is a legal C++ statement:
1;
The type of the expression 1 is int.
Determining the Order of Operations
All operators perform some defined function. In addition, every operator has a precedence — a specified place in the order in which the expressions are evaluated. Consider, for example, how precedence affects solving the follow_ ing problem:
int var = 2 * 3 + 1;
If the addition is performed before the multiplication, the value of the expres_ sion is 2 times 4 or 8. If the multiplication is performed first, the value is 6 + 1 or 7.
The precedence of the operators determines who goes first. Table 3-1 shows that multiplication has higher precedence than addition, so the result is 7.
(The concept of precedence is also present in arithmetic. C++ adheres to the common arithmetic precedence.)
So what happens when we use two operators of the same precedence in the same expression? Well, it looks like this:
int var = 8 / 4 / 2;
But is this 8 divided by 2 or 4, or is it 2 divided by 2 or 1? When operators of the same precedence appear in the same expression, they are evaluated from left to right (the same rule applied in arithmetic). Thus, the answer is 8 divided by 4, which is 2 divided by 2 (which is 1).
The expression
x / 100 + 32
divides x by 100 before adding 32. But what if the programmer wanted to divide x by 100 plus 32? The programmer can change the precedence by bundling expressions together in parentheses (shades of algebra!), as follows:
x/(100 + 32)
Chapter 3: Performing Mathematical Operations 43
This expression has the same effect as dividing x by 132.
The original expression
x / 100 + 32
is identical to the expression
(x/100) + 32
In a given expression, C++ normally performs multiplication and division before addition or subtraction. Multiplication and division have higher precedence than addition and subtraction.
In summary: Precedence refers to the order in which operators are evaluated. An operator with higher precedence is executed first. You can override the precedence of an operator by using parentheses.
Performing Unary Operations
Arithmetic binary operators — those operators that take two arguments — are familiar to a lot of us from school days. You’ve probably been doing binary operations since the first grade in school. But consider the unary operators, which take a single argument (for example, –a). Many unary operations are not so well known.
The unary mathematical operators are plus, plus-plus, minus, and minus-minus
(respectively, +, –, ++, and – –). Thus
int var1 = 10;
int var2 = -var1;
The latter expression uses the minus unary operator (–) to calculate the value negative 10.
The minus operator changes the sign of its argument. Positive numbers become negative and vice versa. The plus operator does not change the sign of its argument. It wouldnÂ’t be weird to say the plus operator has no effect at all.
The ++ and the – – operators might be new to you. These operators (respec_ tively) add one to their arguments or subtract one from their arguments, so they’re known (also respectively) as the increment and decrement operators.
44 Part I: Introduction to C++ Programming Because theyÂ’re dependent upon numbers that can be counted, theyÂ’re lim_ ited to non-floating-point variables. For example, the value of var after exe_ cuting the following expression is 11.
int var = 10; // initalize var var++; // now increment it
// value of var is now 11
The increment and decrement operators are peculiar in that both come in two flavors: a prefix version and a postfix version (known as pre-increment and post-increment, respectively). Consider, for example, the increment operator
(the decrement works in exactly the same way).
Suppose that the variable n has the value 5. Both ++n and n++ increment n to the value 6. The difference between the two is that the value of ++n in an expression is 6 while the value of n++ is 5. The following example illustrates this difference:
// declare three integer variables int n1, n2, n3;
// the value of both n1 and n2 is 6
n1 = 5;
n2 = ++n1;
// the value of n1 is 6 but the value of n3 is 5
n1 = 5;
n3 = n1++;
Thus n2 is given the value of n1 after n1 has been incremented (using the pre-increment operator), whereas n3 gets the value of n1 before it is incre_ mented using the post-increment operator.
Why define a separate increment operator?
The authors of C++ noted that programmers add
1 more than any other constant. To provide
some convenience, a special add 1 instruction was added to the language.
In addition, most present-day computer proces_
sors have an increment instruction that is faster
than the addition instruction. Back when C++ was created, however — with microprocessors being what they were — saving a few instruc_ tions was a big deal.
Chapter 3: Performing Mathematical Operations 45
Using Assignment Operators
An assignment operator is a binary operator that changes the value of its left argument. The equal sign (=), a simple assignment operator, is an absolute necessity in any programming language. This operator puts the value of the right-hand argument into the left argument. The other assignment operators are odd enough that they seem to be someoneÂ’s whim.
The creators of C++ noticed that assignments often follow the form of
variable = variable # constant
where # is some binary operator. Thus, to increment an integer operator by
2, the programmer might write
nVariable = nVariable + 2;
This expression says, “add two to the value of nVariable and store the results back into nVariable.” Doing so changes the value of nVariable to 2 more than it was.
ItÂ’s common to see the same variable on both the right and left side of an assignment.
Because the same variable appears on both sides of the = sign, the same Fathers of the C++ Republic decided to create a version of the assignment operator in which a binary operator is attached. This says, in effect, “Thou shalt perform whatever operation on a variable and store the results right back into the same variable.”
Every binary operator has one of these nifty assignment versions. Thus, the assignment just given could have been written this way:
nVariable = nVariable + 2;
nVariable += 2;
Here the first line says (being very explicit now) “Take the value of nVariable, add 2, and store the results back into nVariable.” The line is a second form if the same expression, saying (a bit more abruptly), “Add 2 to the value of nVariable.”
Other than assignment itself, these assignment operators are not used all that often. However, as odd as they might look, sometimes they can actually make the resulting program easier to read.
46 Part I: Introduction to C++ Programming
Chapter 4
Performing Logical Operations
In This Chapter
Using sometimes-illogical logical operators
Defining logical variables
Operating with bitwise logical operators logically, a bit at a time
The most common statement in C++ is the expression. Most expressions involve the arithmetic operators such as addition (+), subtraction (–) and
multiplication (*). This chapter describes these types of expressions.
There is a whole other class of operators known as the logical operators. In comparison with the arithmetic operators, most people donÂ’t think nearly as much about operations.
It isn’t that people don’t deal with logical operations — after all, people compute AND and OR constantly. I won’t eat cereal unless the bowl contains cereal AND the bowl has milk in it AND the cereal is coated with sugar (lots of sugar). I’ll have a Scotch IF it’s single-malt AND someone else paid for it. People use such logical operations all the time, it’s just that they don’t write them down as machine instructions (or think of them in that light).
Logical operators fall into two types. The AND and OR operators are what I will call simple logical operators. There is a second type of logical operator that people don’t use in their daily business — the bitwise operator — that’s unique to the computer world. We’ll start with the simple and sneak up on the bitwise here.
Why Mess with Logical Operations?
C++ programs have to make decisions. A program that can’t make decisions is of limited use. The temperature-conversion program laid out in Chapter 1 is about as complex you can get without some type of decision-making. Invariably a computer program gets to the point where it has to figure out situations such as “Do this if the a variable is less than some value, do that other thing if it’s not.” That’s what makes a computer appear to be intelligent — that it can make
48 Part I: Introduction to C++ Programming decisions. (By the same token, that same property makes a computer look really stupid when the program makes the wrong decision.) Making deci_ sions, right or wrong, requires the use of logical operators.
Using the Simple Logical Operators
The simple logical operators, shown in Table 4-1, evaluate to true or false. Table 4-1 Simple Operators Representing Daily Logic Operator Meaning
== Equality; true if the left-hand argument has the same value as the right
!= Inequality; opposite of equality
>, < Greater than, less than; true if the left-hand argument is greater than or less than the right-hand argument
>=, <= Greater than or equal to, less than or equal to; true if either > or
== is true, OR either < or == is true
&& AND; true if both the left-and right-hand arguments are true
|| OR; true if either the left-or the right-hand argument is true
! NOT; true if its argument is false
The first six entries in Table 4-1 are comparison operators. The equality opera_ tor is used to compare two numbers. For example, the following is true if the value of n is 0, and is false otherwise:
n == 0;
Looks can be deceiving. Don’t confuse the equality operator (==) with the assignment operator (=). Not only is this a common mistake, but it’s a mis_ take that the C++ compiler generally cannot catch — that makes it more than twice as bad.
n = 0; // programmer meant to say n == 0
The greater-than (>) and less-than (<) operators are similarly common in everyday life. The following expression logical comparison is true:
Chapter 4: Performing Logical Operations 49
int n1 = 1;
int n2 = 2;
n1 < n2;
It’s easy to forget which operator is “greater than” and which is “less than.” Just remember that the operator is true if the arrow points to the smaller of the two.
You may think that n1 is greater than or less than n2; however, this ignores the possibility that n1 and n2 are equal. The greater-than-or-equal-to opera_ tor (<=) and the less-than-or-equal-to operator (>=) include that bit of mathe_ matical nuance. They are similar to the less-than and greater-than operators, with one major exception: They include equality; the other operators donÂ’t.
The && (AND) and || (OR) can combine with the other logic operators, like this:
// true if n2 is greater than n1 but n2 smaller than n3
// (this is the most common way determining that n2 is in
// the range of n1 to n3, exclusive)
(n1 < n2) && (n2 < n3);
Storing logical values
The result of a logical operation can be assigned to a variable of type bool:
int n1 = 1; int n2 = 2; bool b;
b = (n1 == n2);
This expression highlights the difference between the assignment operator = and the comparison operator ==. The expression says, “Compare the vari_ ables n1 and n2. Store the results of this comparison in the variable b.”
The assignment operators are about as low down on the precedence totem pole as you can get. The equality operator is executed before the assignment. The parentheses are not required — so the following is an equally valid form
of logical confusion:
b = n1 == n2; // compare n1 with n2; generate a true if n1
// if n1 has the same value as n2, false if not
// store the result, true or false, in b
Whoa. Better look at that again. Note the difference between the operators.
50 Part I: Introduction to C++ Programming
The following program demonstrates the use of a bool variable:
// BoolTest - compare variables input from the
// keyboard and store the results off
// into a logical variable
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// set output format for bool variables
// to true and false instead
// of 1 and 0
cout.setf(cout.boolalpha);
// initialize two arguments int nArg1;
cout << “Input value 1: “;
cin >> nArg1;
int nArg2;
cout << “Input value 2: “;
cin >> nArg2;
bool b;
b = nArg1 == nArg2;
cout << “The statement, “ << nArg1
<< “ equals “ << nArg2
<< “ is “ << b
<< endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
The first line cout.setf() makes sure that our bool variable b is output as
“true” or “false”. The next section explains why this is necessary.
The program inputs two values from the keyboard and displays the result of the equality comparison:
Input value 1: 5
Input value 2: 5
The statement, 5 equals 5 is true
Press any key to continue . . .
Chapter 4: Performing Logical Operations 51
The special value endl inserts a newline. The difference between the value endl and the character ‘\n’ as described in Chapter 2 is subtle and explained in Chapter 24.
Using logical int variables
C++ hasn’t always had a bool type variable. Back in the old days (before that guy on TV kept walking around saying “Can you hear me now?”), C++ used int variables to store logical values. A value of 0 was considered false and all other values true. By the same token, a logical operator generated a 0 for false and a 1 for true. (Thus, 0 was false while 10 > 5 returned a 1.)
C++ retains a high degree of compatibility between bool and int in order to support the older programs that still have that quirk. You get completely different output from the BitTest program if you remove the line cout. setf(cout.boolalpha):
Input value 1: 5
Input value 2: 5
The statement, 5 equals 5 is 1
Press any key to continue . . .
Variables of type int and bool can be mixed in expressions as well. For exam_
ple, Dev-C++ allows the following bizarre statement without batting an eyelid:
int n;
n = nArg1 == nArg2;
Continue to use type bool to hold logical values despite this wart that modern
C++ inherits from its forefathers. Other compilers may not be as forgiving. Be careful performing logical operations on floating-point variables
Real numbers are those numbers that can have a fractional part. Because of this, real numbers cannot be counting numbers. That is, you can say the first
(1st), second (2nd), third, fourth, and so on because the relationship of 1, 2, and 3 are known exactly. It does not make sense to speak of the 4.5th number in a sequence. (This brings to mind the number between the fourth and fifth, but it has no real meaning.)
Similarly the C++ type float, which is the C++ representation, is not a count_ ing number. Even worse (unlike a real number), a floating-point number canÂ’t have an infinite number of digits beyond the decimal point if a computer is
52 Part I: Introduction to C++ Programming going to make any use of it. Because of this limitation, be careful when you use comparison operators on floating-point numbers. Consider the following example:
float f1 = 10.0;
float f2 = f1 / 3;
f1 == (f2 * 3.0); // are these two equal?
The comparison in the preceding example is not necessarily true. A floating- point variable cannot hold an unlimited number of significant digits. Thus, f2 is not equal to the number we’d call “three-and-a-third,” but rather to 3.3333..., stopping after some number of decimal places.
A float variable supports about 6 digits of accuracy while a double sup_ ports 13 digits. I say “about” because the computer is likely to generate a number like 3.3333347 due to vagaries in floating point calculations.
Now, in pure math, the number of threes after the decimal point is infinite — but no computer built can handle infinity. So, after multiplying 3.3333 by 3, you get 9.9999 instead of the 10 you’d get if you multiplied “three-and-a-third” —
in effect, a round-off error. Such small differences may be unnoticeable to a person, but not to the computer. Equality means exactly that — exact equality.
Modern processors are very sophisticated in performing such calculations. The processor may, in fact, accommodate the round-off error, but from inside C++, you canÂ’t predict exactly what any given processor will do.
Problems can arise even in a straightforward calculation, such as the following:
float f1 = 10.0;
float f2 = 100 % 30;
f1 == f2; // are these two equal?
Theoretically, f1 and f2 should be equal (after you apply that percentlike operator that Chapter 3 identifies as modulus). There doesn’t appear to be any problem with round off. So far. But you can’t be sure — you have no idea how the computer that eventually runs your program is going to represent floating-point numbers internally. To flatly claim that there’s no round-off error lurking here makes unwarranted assumptions about CPU internals.
The safer comparison is as follows:
float f1 = 10.0; float f2 = f1 / 3; float f3 = f2 * 3.0;
(f1 - f3) < 0.0001 && (f3 - f1) < 0.0001;
Chapter 4: Performing Logical Operations 53
This comparison is true if f1 and f3 are within some small delta from each other, which should still be true even if you take some small round-off error into account.
Short circuits and C++
The& & and || perform what is called short-circuit evaluation. Consider the following:
condition1 && condition2
If condition1 is not true, the result is not true, no matter what the value of condition2. (For example, condition2 could be true or false without changing the result.) The same situation occurs in the following:
condition1 || condition2
If condition1 is true, the result is true, no matter what the value of
condition2.
To save time, C++ (wisely) cuts to the chase and evaluates condition1 first. C++ does not evaluate condition2 if condition1 is false (in the case of &
&) or condition1 is true (in the case of ||). This is known as short circuit evaluation.
Expressing Binary Numbers
C++ variables are stored internally as so-called binary numbers. Binary num_ bers are stored as a sequence of 1 and 0 values known as bits. Most of the time, you don’t really need to deal with which particular bits you use to rep_ resent numbers. Sometimes, however, it’s actually practical and convenient to tinker with numbers at the bit level — so C++ provides a set of operators for that purpose.
Fortunately, you wonÂ’t have to deal too often with C++ variables at the bit level, so itÂ’s pretty safe to consider the remainder of this chapter a Deep Techie excursion.
The so-called bitwise logical operators operate on their arguments at the bit level. To understand how they work, letÂ’s first examine how computers store variables.
54 Part I: Introduction to C++ Programming
The decimal number system
The numbers we’ve been familiar with from the time we could first count on our fingers are known as decimal numbers because they’re based on the number 10. (Coincidence? I don’t think so . . .) In general, the programmer expresses C++ variables as decimal numbers. Thus you could specify the value of var as (say) 123 — but consider the implications.
A number such as 123 refers to 1 * 100 + 2 * 10 + 3 * 1. Each of these base numbers — 100, 10, and 1 — are powers of 10.
123 = 1 * 100 + 2 * 10 + 3 * 1
Expressed in a slightly different (but equivalent) way, 123 looks like this:
123 = 1 * 102 + 2 * 101 + 3 * 100
Remember that any number to the zero power is 1.
Other number systems
Well, okay, using 10 as the basis (or base) of our counting system probably stems from those 10 human fingers, the original counting tools. An alterna_ tive base for a counting system could just as easily have been 20 (maybe the inventor of base 10 had shoes on at the time).
If our numbering scheme had been invented by dogs, it might well be based on
8 (one digit of each paw is out of sight on the back part of the leg). Mathemat_
ically, such an octal system would have worked just as well:
12310 = 1 * 8
2 + 7 * 81 + 3 * 80 = 173
8
The small 10 and 8 here refer to the numbering system, 10 for decimal
(base 10) and 8 for octal (base 8). A counting system may use any positive base.
The binary number system
Computers have essentially two fingers. (Maybe thatÂ’s why computers are so stupid: without an opposing thumb, they canÂ’t grasp anything. And then again, maybe not.) Computers prefer counting using base 2. The number 12310 would be expressed this way:
Chapter 4: Performing Logical Operations 55
12310 = 0*128 + 1*64 + 1*32 + 1*16 + 1*8 + 0*4 +1*2 + 1*1
= 011110112
Computer convention expresses binary numbers by using 4, 8, 16, 32 or even
64 binary digits even if the leading digits are zero. This is also because of the way computers are built internally.
Because the term digit refers to a multiple of ten, a binary digit is called a bit
(an abbreviation of binary digit). Eight bits make up a byte. (Calling a binary digit a byte-it didnÂ’t seem like a good idea.) A short word is two bytes; a long word is four bytes.
With such a small base, you have to use a large number of bits to express numbers. Human beings donÂ’t want the hassle of using an expression such as
011110112 to express such a mundane value as 12310. Programmers prefer to express numbers by using an even number of bits. The octal system — which is based on 3 bits — has been almost completely replaced by the hexadeci_ mal system, which is based on 4-bit digits.
Hexadecimal uses the same digits for the numbers 0 through 9. For the digits between 9 and 16, hexadecimal uses the first six letters of the alphabet: A for
10, B for 11, etc. Thus, 12310 becomes 7B16, like this:
123 = 7 * 161 + B (i.e. 11) * 160 = 7B
16
Programmers prefer to express hexadecimal numbers in 2, 4, or 8 hexadeci_
mal digits even when the leading digit in each case is 0.
Finally, who wants to express a hexadecimal number such as 7B16 by using a subscript? Terminals donÂ’t even support subscripts. Even on a word proces_ sor such as the one IÂ’m using now, itÂ’s a drag to change fonts to and from sub_ script mode just to type two lousy digits. Therefore, programmers (no fools they) use the convention of beginning a hexadecimal number with a 0x. (Why? Well, the reason for such a strange convention goes back to the early days of C, in a galaxy far, far, away . . . never mind.) Thus, 7B becomes 0x7B. Using this convention, the hexadecimal number 0x7B is equal to 123 decimal while
0x123 hexadecimal is equal to 291 decimal.
You can use all the mathematical operators on hexadecimal numbers, in the same way youÂ’d apply them to decimal numbers. (Well, okay, most of us canÂ’t perform a multiplication such as 0xC * 0xE in our heads, but that has more to do with the multiplication tables we learned in school than it has to do with any limitation in the number system.)
56 Part I: Introduction to C++ Programming
Roman numeral expressions
On a historical note, I should mention that some numbering systems actually hinder computa_ tions. The Roman numeral system is a (so to speak) classic example that greatly hindered the development of math.
Adding two Roman numerals isnÂ’t too difficult: XIX + XXVI = XLV
Think this one out:
a) IX + VI: The I after the V cancels out the I
before the X so the result is V carry the X.
b) X + XX: Plus the carry X is XXXX, which is expressed as XL.
Subtraction is only slightly more difficult.
Ah, but multiplying two Roman numerals all but
requires a bachelorÂ’s degree in mathematics.
(You end up with rules like X promotes the digits on the right by 1 letter so that X –* IV becomes XL.) Division practically required a Ph.D., and higher operations such as integration would have been completely impossible.
Love those Arabic numerals . . .
Performing Bitwise Logical Operations
All C++ numbers can be expressed in binary form. Binary numbers use only the digits 1 and 0 to represent a value. The following Table 4-2 defines the set of operations that work on numbers one bit at a time, hence the term bitwise operators.
Table 4-2 Bitwise Operators
Operator Function
~ NOT: Toggle each bit from 1 to 0 and from 0 to 1
& AND each bit of the left-hand argument with that on the right
| OR each bit of the left-hand argument with that on the right
^ XOR (exclusive OR) each bit of the left-hand argument with that on the right
Bitwise operations can potentially store a lot of information in a small amount of memory. There are a lot of traits in the world that have only two (or, at most, four) possibilities — that are either this way or that way. You are either married or you’re not (you might be divorced but you are still not currently
Chapter 4: Performing Logical Operations 57
married). You are either male or female (at least that’s what my driver’s license says). In C++, you can store each of these traits in a single bit — in this way, you can pack 32 separate properties into a single 32-bit int.
In addition, bit operations can be extremely fast. There is no performance penalty paid for that 32-to-1 saving.
Even though memory is cheap these days, itÂ’s not unlimited. Sometimes, when youÂ’re storing large amounts of data, this ability to pack a whole lot of properties into a single word is a big advantage.
The single bit operators
The bitwise operators — AND (&), OR (|) and NOT (~) — perform logic oper_ ations on single bits. If you consider 0 to be false and 1 to be true (it doesn’t have to be this way, but it’s a common convention), you can say things like the following for the NOT operator:
NOT 1 (true) is 0 (false)
NOT 0 (false) is 1 (true)
The AND operator is defined as following:
1 (true) AND 1 (true) is 1 (true)
1 (true) AND 0 (false) is 0 (false)
ItÂ’s a similar situation for the OR operator:
1 (true) OR 0 (false) is 1 (true)
0 (false) OR 0 (false) is 0 (false)
The definition of the AND operator appears in Table 4-3.
Table 4-3 Truth Table for the AND Operator
AND 1 0
1 1 0
0 0 0
You read this table as the column corresponding to the value of one of the arguments while the row corresponds to the other. Thus, 1 & 0 is 0. (Column 1
58 Part I: Introduction to C++ Programming
and row 0.) The only combination that returns anything other than 0 is 1 & 1.
(This is known as a truth table.)
Similarly, the truth table for the OR operator is shown in Table 4-4. Table 4-4 Truth Table for the OR Operator XOR 1 0
1 1 1
0 1 0
One other logical operation that is not so commonly used in day-to-day living is the OR ELSE operator commonly contracted to XOR. XOR is true if either argument is true but not if both are true. The truth table for XOR is shown in Table 4-5.
Table 4-5 Truth Table for the XOR Operator
XOR 1 0
1 0 1
0 1 0
Armed with these single bit operators, we can take on the C++ bitwise logical operations.
Using the bitwise operators
The bitwise operators operate on each bit separately.
The bitwise operators are used much like any other binary arithmetic operator. The NOT operator is the easiest to understand. To NOT a number is to NOT each bit that makes up that number (and to a programmer, that sentence makes perfect sense — honest). Consider this example:
~01102 (0x6)
10012 (0x9)
Thus we say that ~0x6 equals 0x9.
The following calculation demonstrates the & operator:
Chapter 4: Performing Logical Operations 59
01102
&
00112
00102
Beginning with the most significant bit, 0 AND 0 is 0. In the next bit, 1 AND 0
is 0. In bit 3, 1 AND 1 is 1. In the least significant bit, 0 AND 1 is 0.
The same calculation can be performed in hexadecimal by first converting the number in binary, performing the operation and then converting the result back.
0x6 01102
& &
0x3 00112
0x2 00102
In shorthand, we say that 0x6 & 0x3 equals 0x2.
(Try this test: What is the value of 0x6 | 0x3? Get this in 7 seconds, and you can give yourself 7 pats on the back.)
A simple test
The following program illustrates the bitwise operators in action. The pro_ gram initializes two variables and outputs the result of ANDing, ORing, and XORing them.
// BitTest - initialize two variables and output the
// results of applying the ~,& , | and ^
// operations
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// set output format to hexadecimal cout.setf(cout.hex);
// initialize two arguments int nArg1;
nArg1 = 0x1234;
int nArg2;
nArg2 = 0x00ff;
// now perform each operation in turn
// first the unary NOT operator
60 Part I: Introduction to C++ Programming
cout << “Arg1 = 0x” << nArg1 << “\n”; cout << “Arg2 = 0x” << nArg2 << “\n”; cout << “~nArg1 = 0x” << ~nArg1 << “\n”; cout << “~nArg2 = 0x” << ~nArg2 << “\n”;
// now the binary operators cout << “nArg1 & nArg2 = 0x”
<< (nArg1 & nArg2)
<< “\n”;
cout << “nArg1 | nArg2 = 0x”
<< (nArg1 | nArg2)
<< “\n”;
cout << “nArg1 ^ nArg2 = 0x”
<< (nArg1 ^ nArg2)
<< “\n”;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
The first expression in our program, cout.setf(ios::hex), sets the output format from the default decimal to hexadecimal (youÂ’ll have to trust me for now that it works).
The remainder of the program is straightforward. The program assigns nArg1 the test value 0x1234 and nArg2 the value 0x00ff. The program then out_ puts all combinations of bitwise calculations. The process looks like this:
Arg1 = 0x1234
Arg2 = 0xff
~nArg1 = 0xffffedcb
~nArg2 = 0xffffff00
nArg1 & nArg2 = 0x34 nArg1 | nArg2 = 0x12ff nArg1 ^ nArg2 = 0x12cb
Press any key to continue . . .
Do something logical with logical calculations
Running through simple and bitwise logical calculations in your head at par_ ties is fun (well, okay, for some of us), but a program has to make actual, prac_ tical use of these values to make them worth the trouble. Coming right up: Chapter 5 demonstrates how logical calculations are used to control program flow.
Chapter 5
Controlling Program Flow
In This Chapter
Controlling the flow through the program Executing a group of statements repetitively Avoiding infinite loops
The simple programs that appear in Chapters 1 through 4 process a fixed number of inputs, output the result of that calculation, and quit. However,
these programs lack any form of flow control. They cannot make tests of any sort. Computer programs are all about making decisions. If the user presses a key, the computer responds to the command.
For example, if the user presses Ctrl+C, the computer copies the currently selected area to the Clipboard. If the user moves the mouse, the pointer moves on the screen. If the user clicks the right mouse button with the Windows key depressed, the computer crashes. The list goes on and on. Programs that donÂ’t make decisions are necessarily pretty boring.
Flow-control commands allow the program to decide what action to take, based on the results of the C++ logical operations performed (see Chapter 4). There are basically three types of flow-control statements: the branch, the loop, and the switch.
Controlling Program Flow with the
Branch Commands
The simplest form of flow control is the branch statement. This instruction allows the program to decide which of two paths to take through C++ instruc_ tions, based on the results of a logical expression (see Chapter 4 for a descrip_ tion of logical expressions).
62 Part I: Introduction to C++ Programming
In C++, the branch statement is implemented using the if statement:
if (m > n)
{
}
else
{
}
// Path 1
// ...instructions to be executed if
// m is greater than n
// Path 2
// ...instructions to be executed if not
First, the logical expression m > n is evaluated. If the result of the expression is true, control passes down the path marked Path 1 in the previous snip_ pet. If the expression is false, control passes to Path 2. The else clause is optional. If it is not present, C++ acts as if it is present but empty.
Actually, the braces are optional (sort of) if there’s only one statement to exe_ cute as part of the if. If you lose the braces, however, it’s embarrassingly easy to make a mistake that the C++ compiler can’t catch. The braces serve as a guide marker; it’s much safer to include ’em. (If your friends try to entice you into not using braces, “Just say No.”)
The following program demonstrates the if statement (note all the lovely braces):
// BranchDemo - input two numbers. Go down one path of the
// program if the first argument is greater than
// the first or the other path if not
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// input the first argument... int arg1;
cout << “Enter arg1: “;
cin >> arg1;
// ...and the second int arg2;
cout << “Enter arg2: “;
cin >> arg2;
// now decide what to do:
if (arg1 > arg2)
Chapter 5: Controlling Program Flow 63
{
}
else
{
}
cout << “Argument 1 is greater than argument 2”
<< endl;
cout << “Argument 1 is not greater than argument 2”
<< endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
Here the program reads two integers from the keyboard and compares them. If the expression “arg1 is greater than arg2” is true, control flows to the output statement cout << “Argument 1 is greater than argument 2”. If arg1 is not greater than arg2, control flows to the else clause where the statement cout << “Argument 1 is not greater than argument 2\n” is executed. Here’s what that operation looks like:
Enter arg1: 5
Enter arg2: 6
Argument 1 is not greater than argument 2
Press any key to continue . . .
Executing Loops in a Program
Branch statements allow you to control the flow of a programÂ’s execution from one path of a program or another. This is a big improvement, but still not enough to write full-strength programs.
Consider the problem of updating the computer display. On the typical PC display, 1 million pixels are drawn to update the entire display. A program that canÂ’t execute the same code repetitively would need to include the same set of instructions over and over 1,000 times.
What we really need is a way for the computer to execute the same sequence of instructions thousands and millions of times. Executing the same command multiple times requires some type of looping statements.
64 Part I: Introduction to C++ Programming
Looping while a condition is true
The simplest form of looping statement is the while loop. HereÂ’s what the
while loop looks like:
while(condition)
{
// ... repeatedly executed as long as condition is true
}
The condition is tested. This condition could be if var > 10 or if var1 == var2 or anything else you might think of. If it is true, the state_
ments within the braces are executed. Upon encountering the closed brace, C++ returns control to the beginning, and the process starts over. The effect is that the C++ code within the braces is executed repeatedly as long as the condition is true. (Kind of reminds me of how I get to walk around the yard with my dog until she . . . well, until weÂ’re done.)
If the condition were true the first time, what would make it be false in the future? Consider the following example program:
// WhileDemo - input a loop count. Loop while
// outputting astring arg number of times.
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// input the loop count int loopCount;
cout << “Enter loopCount: “;
cin >> loopCount;
// now loop that many times while (loopCount > 0)
{
loopCount = loopCount - 1;
cout << “Only “ << loopCount << “ loops to go\n”;
}
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
WhileDemo begins by retrieving a loop count from the user, which it stores in the variable loopCount. The program then executes a while loop. The while first tests loopCount. If loopCount is greater than zero, the program enters
Chapter 5: Controlling Program Flow 65
the body of the loop (the body is the code between the braces) where it decre_ ments loopCount by 1 and outputs the result to the display. The program then returns to the top of the loop to test whether loopCount is still positive.
When executed, the program WhileDemo outputs the results shown in this next snippet. Here I entered a loop count of 5. The result is that the program loops 5 times, each time outputting a countdown.
Enter loopCount: 5
Only 4 loops to go Only 3 loops to go Only 2 loops to go Only 1 loops to go Only 0 loops to go
Press any key to continue . . .
If the user enters a negative loop count, the program skips the loop entirely. ThatÂ’s because the specified condition is never true, so control never enters the loop. In addition, if the user enters a very large number, the program loops for a long time before completing.
A separate, less frequently used version of the while loop known as the do . . . while appears identical except the condition isnÂ’t tested until the bottom of the loop:
do
{
// ...the inside of the loop
} while (condition);
Because the condition isnÂ’t tested until the end, the body of the do . . . while is always executed at least once.
The condition is only checked at the beginning of the while loop or at the end of the do . . . while loop. Even if the condition ceases to be true at some time during the execution of the loop, control does not exit the loop until the condition is retested.
Using the autoincrement/
autodecrement feature
Programmers very often use the autoincrement ++ or the autodecrement -- operators with loops that count something. Notice, from the following snip_ pet extracted from the WhileDemo example, that the program decrements the loop count by using assignment and subtraction statements, like this:
66 Part I: Introduction to C++ Programming
// now loop that many times while (loopCount > 0)
{
loopCount = loopCount - 1;
cout << “Only “ << loopCount << “ loops to go\n”;
}
A more compact version uses the autodecrement feature, which does what you may well imagine:
while (loopCount > 0)
{
loopCount--;
cout << “Only “ << loopCount << “ loops to go\n”;
}
The logic in this version is the same as in the original. The only difference is the way that loopCount is decremented.
Because the autodecrement both decrements its argument and returns its value, the decrement operation can actually be combined with the while loop. In particular, the following version is the smallest loop yet.
while (loopCount-- > 0)
{
cout << “Only “ << loopCount << “ loops to go\n”;
}
Believe it or not, the loopcount— > 0 is the version that most C++ program_ mers would use. It’s not that C++ programmers like being cute (although they do). In fact, the more compact version (which embeds the autoincrement or autodecrement feature in the logical comparison) is easier to read, especially as you gain experience.
Both loopCount— and —loopCount expressions decrement loopCount. The former expression, however, returns the value of loopCount before being decremented; the latter expression does so after being decremented.
How often should the autodecrement version of WhileDemo execute when the user enters a loop count of 1? If you use the pre-decrement version, the value of —loopCount is 0, and the body of the loop is never entered. With the post- decrement version, the value of loopCount— is 1, and control enters the loop.
Beware thinking that the version of the program with the autodecrement command executes faster (since it contains fewer statements). It probably executes exactly the same. Modern compilers are pretty good at getting the number of machine-language instructions down to a minimum, no matter which of the decrement instructions shown here you actually use
Chapter 5: Controlling Program Flow 67
Using the for loop
The most common form of loop is the for loop. The for loop is preferred over the more basic while loop because itÂ’s generally easier to read (thereÂ’s really no other advantage).
The for loop has the following format:
for (initialization; conditional; increment)
{
// ...body of the loop
}
Execution of the for loop begins with the initialization clause, which got its name because itÂ’s normally where counting variables are initialized. The ini_ tialization clause is only executed once when the for loop is first encountered.
Execution continues with the conditional clause. This clause works a lot like the while loop: as long as the conditional clause is true, the for loop con_ tinues to execute.
After the code in the body of the loop finishes executing, control passes to the increment clause before returning to check the conditional clause — thereby repeating the process. The increment clause normally houses the autoincrement or autodecrement statements used to update the counting variables.
The following while loop is equivalent to the for loop:
{
initialization;
while(conditional)
{
{
// ...body of the loop
}
increment;
}
}
All three clauses are optional. If the initialization or increment clauses are missing, C++ ignores them. If the conditional clause is missing, C++ performs the for loop forever (or until something else passes control outside the loop).
The for loop is best understood by example. The following ForDemo program is nothing more than the WhileDemo converted to use the for loop construct:
68 Part I: Introduction to C++ Programming
// ForDemo1 - input a loop count. Loop while
// outputting astring arg number of times.
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// input the loop count int loopCount;
cout << “Enter loopCount: “;
cin >> loopCount;
// count up to the loop count limit for (; loopCount > 0
{
loopCount = loopCount - 1;
cout << “Only “ << loopCount << “ loops to go\n”;
}
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
The program reads a value from the keyboard into the variable loopCount. The for starts out comparing loopCount to zero. Control passes into the
for loop if loopCount is greater than zero. Once inside the for loop, the pro_ gram decrements loopCount and displays the result. That done, the program returns to the for loop control. Control skips to the next line after the for loop as soon as loopCount has been decremented to zero.
All three sections of a for loop may be empty. An empty initialization or increment section does nothing. An empty comparison section is treated like a comparison that returns true.
This for loop has two small problems. First, it’s destructive — not in the sense of what my puppy does to a slipper, but in the sense that it changes the value of loopCount, “destroying” the original value. Second, this for loop counts “backward” from large values down to smaller values. These two problems are addressed if you add a dedicated counting variable to the for loop. Here’s what it looks like:
// ForDemo2 - input a loop count. Loop while
// outputting astring arg number of times.
#include <cstdio>
#include <cstdlib>
Chapter 5: Controlling Program Flow 69
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// input the loop count int loopCount;
cout << “Enter loopCount: “;
cin >> loopCount;
// count up to the loop count limit for (int i = 1; i <= loopCount; i++)
{
cout << “We’ve finished “ << i << “ loops\n”;
}
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
This modified version of WhileDemo loops the same as it did before. Instead of modifying the value of loopCount, however, this ForDemo2 version uses a counter variable.
Control begins by declaring a variable and initializing it to the value contained in loopCount. It then checks the variable i to make sure that it is positive. If so, the program executes the output statement, decrements i and starts over.
When declared within the initialization portion of the for loop, the index variable is only known within the for loop itself. Nerdy C++ programmers say that the scope of the variable is the for loop. In the example just given, the variable i is not accessible from the return statement because that state_ ment is not within the loop. Note, however, that not all compilers are strict about sticking to this rule. The Dev-C++ compiler (for example) generates a warning if you use i outside the for loop — but it uses the variable anyway.
Avoiding the dreaded infinite loop
An infinite loop is an execution path that continues forever. An infinite loop occurs any time the condition that would otherwise terminate the loop can’t occur — usually the result of a coding error.
Consider the following minor variation of the earlier loop:
70 Part I: Introduction to C++ Programming
while (loopCount > 0)
{
cout << “Only “ << loopCount << “ loops to go\n”;
}
The programmer forgot to decrement the variable loopCount as in the loop example below. The result is a loop counter that never changes. The test con_ dition is either always false or always true. The program executes in a never- ending (infinite) loop.
I realize that nothingÂ’s infinite. Eventually the power will fail, the computer will break, Microsoft will go bankrupt, and dogs will sleep with cats. . . . Either the loop will stop executing, or you wonÂ’t care anymore.
You can create an infinite loop in many more ways than shown here, most of which are much more difficult to spot than this one.
Applying special loop controls
C++ defines two special flow-control commands known as break and continue. Sometimes the condition for terminating the loop occurs at neither the beginning nor the end of the loop, but in the middle. Consider a program that accumulates numbers of values entered by the user. The loop terminates when the user enters a negative number.
The challenge with this problem is that the program canÂ’t exit the loop until the user has entered a value, but must exit before the value is added to the sum.
For these cases, C++ defines the break command. When encountered, the break causes control to exit the current loop immediately. Control passes from the break statement to the statement immediately following the closed brace at the end of the loop.
The format of the break commands is as follows:
while(condition) // break works equally well in for loop
{
if (some other condition)
{
break; // exit the loop
}
} // control passes here when the
// program encounters the break
Armed with this new break command, my solution to the accumulator prob_
lem appears as the program BreakDemo.
Chapter 5: Controlling Program Flow 71
// BreakDemo - input a series of numbers.
// Continue to accumulate the sum
// of these numbers until the user
// enters a negative number.
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// input the loop count int accumulator = 0;
cout << “This program sums values entered”
<< “by the user\n”;
cout << “Terminate the loop by entering “
<< “a negative number\n”;
// loop “forever”
for(;
{
// fetch another number int value = 0;
cout << “Enter next number: “;
cin >> value;
// if itÂ’s negative... if (value < 0)
{
// ...then exit break;
}
// ...otherwise add the number to the
// accumulator
accumulator = accumulator + value;
}
// now that weÂ’ve exited the loop
// output the accumulated result cout << “\nThe total is “
<< accumulator
<< “\n”;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
After explaining the rules to the user (entering a negative number to termi_
nate, and so on), the program enters what looks like an infinite for loop.
72 Part I: Introduction to C++ Programming
Once within the loop, BreakDemo retrieves a number from the keyboard. Only after the program has read a number can it test to see whether the number it just read matches the exit criteria. If the input number is negative, control passes to the break, causing the program to exit the loop. If the input number is not negative, control skips over the break command to the expression that sums the new value into the accumulator. After the program exits the loop, it outputs the accumulated value and then exits.
When performing an operation on a variable repeatedly in a loop, make sure that the variable is initialized properly before entering the loop. In this case, the program zeros accumulator before entering the loop where value is added to it.
The result of an example run appears as follows:
This program sums values entered by the user Terminate the loop by entering a negative number Enter next number: 1
Enter next number: 2
Enter next number: 3
Enter next number: -1
The total is 6
Press any key to continue . . .
The continue command is used less frequently. When the program encoun_ ters the continue command, it immediately moves back to the top of the loop. The rest of the statements in the loop are ignored for the current iteration.
The following example snippet ignores negative numbers that the user might input. Only a zero terminates this version (the complete program appears on
the CD-ROM as ContinueDemo):
while(true) // this while() has the same effect as for(;
{
// input a value
cout << “Input a value:”;
cin >> value;
// if the value is negative... if (value < 0)
{
// ...output an error message...
cout << “Negative numbers are not allowed\n”;
// ...and go back to the top of the loop continue;
}
// ...continue to process input like normal
}
Chapter 5: Controlling Program Flow 73
Nesting Control Commands
Return to our PC-screen-repaint problem. Surely it must need a loop struc_ ture of some type to write each pixel from left to right on a single line. (Do Middle Eastern terminals scan from right to left? I have no idea.) What about repeatedly repainting each scan line from top to bottom? (Do PC screens in Australia scan from bottom to top? Beats me.) For this particular task, you need to include the left-to-right scan loop within the top-to-bottom scan loop.
A loop command within another loop is known as a nested loop. As an exam_ ple, you can modify the BreakDemo program into a program that accumulates any number of sequences. In this NestedDemo program, the inner loop sums numbers entered from the keyboard until the user enters a negative number. The outer loop continues accumulating sequences until the sum is 0. HereÂ’s what it looks like:
// NestedDemo - input a series of numbers.
// Continue to accumulate the sum
// of these numbers until the user
// enters a 0. Repeat the process
// until the sum is 0.
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// the outer loop
cout << “This program sums multiple series\n”
<< “of numbers. Terminate each sequence\n”
<< “by entering a negative number.\n”
<< “Terminate the series by entering two\n”
<< “negative numbers in a row\n”;
// continue to accumulate sequences int accumulator;
do
{
// start entering the next sequence
// of numbers accumulator = 0;
cout << “Start the next sequence\n”;
// loop forever for(;
{
// fetch another number
int value = 0;
74 Part I: Introduction to C++ Programming
cout << “Enter next number: “;
cin >> value;
// if itÂ’s negative... if (value < 0)
{
// ...then exit break;
}
// ...otherwise add the number to the
// accumulator
accumulator = accumulator + value;
}
// output the accumulated result...
cout << “The total for this sequence is “
<< accumulator
<< endl << endl;
// ...and start over with a new sequence
// if the accumulated sequence was not zero
} while (accumulator != 0);
// weÂ’re about to quit
cout << “Thank you” << endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
Switching to a Different Subject?
One last control statement is useful in a limited number of cases. The switch statement resembles a compound if statement by including a number of dif_ ferent possibilities rather than a single test:
switch(expression)
{
case c1:
// go here if the expression == c1
break;
case c2:
// go here if expression == c2
break;
default:
// go here if there is no match
}
Chapter 5: Controlling Program Flow 75
The value of expression must be an integer (int, long, or char). The case values c1, c2, and c3 must be constants. When the switch statement is encountered, the expression is evaluated and compared to the various case constants. Control branches to the case that matches. If none of the cases match, control passes to the default clause.
Consider the following example code snippet:
int choice;
cout << “Enter a 1, 2 or 3:”;
cin >> choice;
switch(choice)
{
case 1:
// do “1” processing break;
case 2:
// do “2” processing break;
case 3:
// do “3” processing break;
default:
cout << “You didn’t enter a 1, 2 or 3\n”;
}
Once again, the switch statement has an equivalent, in this case multiple if statements; however, when there are more than two or three cases, the switch structure is easier to understand.
The break statements are necessary to exit the switch command. Without the break statements, control falls through from one case to the next. (Look out below!)
76 Part I: Introduction to C++ Programming
Part II
Becoming a Functional C++ Programmer
In this part . . .
I
t’s one thing to perform operations such as addition and multiplication — even when we’re logical (AND and OR or other operations). It’s another thing to write
real programs. This section introduces the features neces_
sary to make the leap into programmerdom.
YouÂ’ll ï¬nd the program BUDGET1 on the enclosed
CD-ROM. This largish program demonstrates the concepts of functional programming. You may want to visit this pro_ gram and its documentation once youÂ’ve mastered func_ tional programming concepts.
Chapter 6
Creating Functions
In This Chapter
Writing functions
Passing data to functions
Naming functions with different arguments
Creating function prototypes
Working with include files
The programs developed in prior chapters have been small enough that they can be easily read as a single unit. Larger, real-world programs can be many thousands (or millions!) of lines long. Developers need to break up
these monster programs into smaller chunks that are easier to conceive, develop, and maintain.
C++ allows programmers to divide their code into exactly such chunks
(known as functions). As long as a function has a simple description and a well-defined interface to the outside world, it can be written and debugged without worrying about the code that surrounds it.
This divide-and-conquer approach reduces the difficulty of creating a work_ ing program of significant size. This is a simple form of encapsulation — see Chapter 15 for more details on encapsulation.
Writing and Using a Function
Functions are best understood by example. This section starts with the example program FunctionDemo, which simplifies the NestedDemo program I discussed in Chapter 5 by defining a function to contain part of the logic. Then this section explains how the function is defined and how it is invoked, using FunctionDemo as a pattern for understanding both the problem and the solution.
80 Part II: Becoming a Functional C++ Programmer The NestedDemo program in Chapter 5 contains an inner loop (which accu_ mulates a sequence of numbers) surrounded by an outer loop (which repeats the process until the user quits). Separating the two loops would simplify the program by allowing the reader to concentrate on each loop independently.
The following FunctionDemo program shows how NestedDemo can be simpli_
fied by creating the function sumSequence().
Function names are normally written with a set of parentheses immediately following the term, like this:
// FunctionDemo - demonstrate the use of functions
// by breaking the inner loop of the
// NestedDemo program off into its own
// function
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
// sumSequence - add a sequence of numbers entered from
// the keyboard until the user enters a
// negative number.
// return - the summation of numbers entered int sumSequence(void)
{
// loop forever
int accumulator = 0;
for(;
{
// fetch another number int value = 0;
cout << “Enter next number: “;
cin >> value;
// if itÂ’s negative... if (value < 0)
{
// ...then exit from the loop break;
}
// ...otherwise add the number to the
// accumulator
accumulator= accumulator + value;
}
// return the accumulated value return accumulator;
}
int main(int nNumberofArgs, char* pszArgs[])
{
Chapter 6: Creating Functions 81
cout << “This program sums multiple series\n”
<< “of numbers. Terminate each sequence\n”
<< “by entering a negative number.\n”
<< “Terminate the series by entering two\n”
<< “negative numbers in a row\n”
<< endl;
// accumulate sequences of numbers... int accumulatedValue;
for(;
{
// sum a sequence of numbers entered from
// the keyboard
cout << “Enter next sequence” << endl;
accumulatedValue = sumSequence();
// terminate the loop if sumSequence() returns
// a zero
if (accumulatedValue == 0)
{
break;
}
// now output the accumulated result cout << “The total is “
<< accumulatedValue
<< “\n”
<< endl;
}
cout << “Thank you” << endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
Defining the sumSequence() function The statement int sumSequence(void) begins the definition of the sumSequence() function. The block of code contained in the braces is the function body. The function sumSequence() accumulates a sequence of values entered from the keyboard. This code section is identical to that found in the inner loop of NestedDemo.
82 Part II: Becoming a Functional C++ Programmer
Calling the function sumSequence()
LetÂ’s concentrate on the main program contained in the braces following
main(). This section of code looks similar to the outer loop in NestedDemo. The main difference is the expression accumulatedValue = sumSequence(); that appears where the inner loop would have been. The sumSequence() statement invokes the function of that name. A value returned by the function is stored in the variable accumulatedValue. Then this value is displayed.
The main program continues to loop until sumSequence() returns a sum of zero, which indicates that the user has finished calculating sums.
Divide and conquer
The FunctionDemo program has split the outer loop in main() from the inner loop into a function sumSequence(). This division wasn’t arbitrary: sumSequence() performs a separate role — worth considering by itself — apart from the control features within FunctionDemo.
A good function is easy to describe. You shouldnÂ’t have to use more than a single sentence, with a minimum of such words as and, or, unless, or but. For example, hereÂ’s a simple, straightforward definition:
“The function sumSequence accumulates a sequence of integer values entered by the user.”
This definition is concise and clear. ItÂ’s a world away from the ContinueDemo
program description:
“sums a sequence of positive values AND generates an error if the user enters a negative number AND displays the sum AND starts over again until the user enters two zero-length sums.”
The output of a sample run of this program appears much like that generated by the NestedDemo program, as follows:
This program sums multiple series of numbers. Terminate each sequence by entering a negative number.
Terminate the series by entering two negative numbers in a row
Enter next sequence
Enter next number: 1
Enter next number: 2
Chapter 6: Creating Functions 83
Enter next number: 3
Enter next number: -1
The total is 6
Enter next sequence
Enter next number: 1
Enter next number: 2
Enter next number: -1
The total is 3
Enter next sequence
Enter next number: -1
Thank you
Press any key to continue . . .
Understanding the Details of Functions Functions are so fundamental to creating C++ programs that getting a handle on the details of defining, creating, and testing them is critical. Armed with the example FunctionDemo program, consider the following definition of function:
A function is a logically separated block of C++ code. The function construct has the following form:
<return type> name(<arguments to the function>)
{
// ...
return <expression>;
}
The arguments to a function are values that can be passed to the function to be used as input information. The return value is a value that the function returns. For example, in the call to the function square(10), the value 10 is an argument to the function square(). The returned value is 100.
Both the arguments and the return value are optional. If either is absent, the keyword void is used instead. That is, if a function has a void argument list, the function does not take any arguments when called (this was the case with the FunctionDemo program). If the return type is void, the function does not return a value to the caller.
In the example FunctionDemo program, the name of the function is
sumSequence(), the return type is int, and no arguments exist.
84 Part II: Becoming a Functional C++ Programmer
The default argument type to a function is void, meaning that it takes no arguments. A function int fn(void) may be declared as int fn().
The function construct made it possible for me to write two distinct parts of the FunctionDemo program separately. I concentrated on creating the sum of a sequence of numbers when writing the sumSequence() function. I didnÂ’t think about other code that may call the function.
Similarly, when writing main(), I concentrated on handling the summation returned by sumSequence() while thinking only of what the function did — not how it worked.
Understanding simple functions
The simple function sumSequence() returns an integer value that it calcu_ lates. Functions may return any of the regular types of variables. For exam_ ple, a function might return a double or a char (int, double, and char are a few of the variable types discussed in Chapter 2).
If a function returns no value, the return type of the function is labeled void. A function may be labeled by its return type — for example, a function that returns an int is often known as an integer function. A function that returns no value is known as a void function.
For example, the following void function performs an operation, but returns no value:
void echoSquare()
{
int value;
cout << “Enter a value:”;
cin >> value;
cout << “\n The square is:” << (value * value) << “\n”;
return;
}
Control begins at the open brace and continues through to the return state_
ment. The return statement in a void function is not followed by a value. The return statement in a void function is optional. If it isnÂ’t present, execu_ tion returns to the calling function when control encounters the close brace.
Chapter 6: Creating Functions 85
Understanding functions with arguments Simple functions are of limited use because the communication from such functions is one-way — through the return value. Two-way communication is through function arguments.
Functions with arguments
A function argument is a variable whose value is passed to the calling function during the call operation. The following SquareDemo example program defines and uses a function square() that returns the square of a double precision float passed to it:
// SquareDemo - demonstrate the use of a function
// which processes arguments
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
// square - returns the square of its argument
// doubleVar - the value to be squared
// returns - square of doubleVar double square(double doubleVar)
{
return doubleVar * doubleVar;
}
// sumSequence - accumulate the square of the number
// entered at the keyboard into a sequence
// until the user enters a negative number double sumSequence(void)
{
// loop forever
double accumulator= 0.0;
for(;
{
// fetch another number double dValue = 0;
cout << “Enter next number: “;
cin >> dValue;
// if itÂ’s negative...
if (dValue < 0)
{
// ...then exit from the loop
86 Part II: Becoming a Functional C++ Programmer
break;
}
// ...otherwise calculate the square double value = square(dValue);
// now add the square to the
// accumulator
accumulator= accumulator + value;
}
// return the accumulated value return accumulator;
}
int main(int nNumberofArgs, char* pszArgs[])
{
cout << “This program sums multiple series\n”
<< “of numbers squared. Terminate each sequence\n”
<< “by entering a negative number.\n”
<< “Terminate the series by entering two\n”
<< “negative numbers in a row\n”
<< endl;
// Continue to accumulate numbers... double accumulatedValue;
for(;
{
// sum a sequence of numbers entered from
// the keyboard
cout << “Enter next sequence” << endl;
accumulatedValue = sumSequence();
// terminate if the sequence is zero or negative if (accumulatedValue <= 0.0)
{
break;
}
// now output the accumulated result
cout << “\nThe total of the values squared is “
<< accumulatedValue
<< endl;
}
cout << “Thank you” << endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
Chapter 6: Creating Functions 87
This is the same FunctionDemo() program, except that SquareDemo() accumulates the square of the values entered. The function square() returns the value of its one argument multiplied by itself. The change to
the sumSequence() function is simple — rather than accumulate the value entered, the function now accumulates the result returned from square().
Functions with multiple arguments
Functions may have multiple arguments that are separated by commas. Thus, the following function returns the product of its two arguments:
int product(int arg1, int arg2)
{
return arg1 * arg2;
}
main() exposed
The “keyword” main() from our standard program template is nothing more than a function — albeit a function with strange arguments — but a function nonetheless.
When a program is built, C++ adds some boilerplate code that executes before your program ever starts (you canÂ’t see this code without digging into the bowels of the C++ library functions). This code sets up the environment in which your program operates. For example, this boilerplate code opens the default input and output channels cin and cout.
After the environment has been established, the C++ boilerplate code calls the function main(), thereby beginning execution of your code. When your program finishes, it exits from main(). This enables the C++ boilerplate to clean up a few things before turning control over to the operating system that kills the program.
Overloading Function Names
C++ allows the programmer to assign the same name to two or more functions. This multiple use of names is known as overloading functions.
In general, two functions in a single program cannot share the same name. If they did, C++ would have no way to distinguish them. Note, however, that the name of the function includes the number and type of its arguments — but does not include its return argument. Thus the following are not the same functions:
88 Part II: Becoming a Functional C++ Programmer
void someFunction(void)
{
// ....perform some function
}
void someFunction(int n)
{
// ...perform some different function
}
void someFunction(double d)
{
// ...perform some very different function
}
void someFunction(int n1, int n2)
{
// ....do something different yet
}
C++ still knows that the functions someFunction(void), someFunction(int), someFunction(double), and someFunction(int, int) are not the same. Like so many things that deal with computers, this has an analogy in the human world.
void as an argument type is optional. sumFunction(void) and sumFunction() are the same function. A function has a shorthand name, such as someFunction(), in same way that I have the shorthand name Stephen (actually, my nickname is Randy, but work with me on this one). If there arenÂ’t any other Stephens around, people can talk about Stephen
behind his back. If, however, there are other Stephens, no matter how hand_ some they might be, people have to use their full names — in my case, Stephen Davis. As long as we use the entire name, no one gets confused — however many Stephens might be milling around. Similarly, the full name for one of the someFunctions() is someFunction(int). As long as this full name is unique, no confusion occurs.
The analogies between the computer world (wherever that is) and the human world are hardly surprising because humans build computers. (I wonder . . .
if dogs had built computers, would the standard unit of memory be a gnaw instead of a byte? Would requests group in packs instead of queues?)
HereÂ’s a typical application that uses overloaded functions with unique full names:
int intVariable1, intVariable2; // equivalent to
// int Variable1;
// int Variable2;
double doubleVariable;
// functions are distinguished by the type of
// the argument passed
Chapter 6: Creating Functions 89
someFunction(); // calls someFunction(void) someFunction(intVariable1); // calls someFunction(int) someFunction(doubleVariable); // calls someFunction(double) someFunction(intVariable1, intVariable2); // calls
// someFunction(int, int)
// this works for constants as well
someFunction(1); // calls someFunction(int)
someFunction(1.0); // calls someFunction(double)
someFunction(1, 2); // calls someFunction(int, int) In each case, the type of the arguments matches the full name of the three functions.
The return type is not part of the extended name (which is also known as the
function signature) of the function. The following two functions have the same
name — so they can’t be part of the same program:
int someFunction(int n); // full name of the function
// is someFunction(int)
double someFunction(int n); // same name
YouÂ’re allowed to mix variable types as long as the source variable type is
more restrictive than the target type. Thus an int can be promoted to a
double. The following is acceptable:
int someFunction(int n);
double d = someFunction(10); // promote returned value
The int returned by someFunction() is promoted into a double. Thus the following would be confusing:
int someFunction(int n);
double someFunction(int n);
double d = someFunction(10);// promote returned int?
// or use returned double as is Here C++ does not know whether to use the value returned from the double version of someFunction() or promote the value returned from int version.
Defining Function Prototypes
The programmer may provide the remainder of a C++ source file, or module, the extended name (the name and functions) during the definition of the function.
90 Part II: Becoming a Functional C++ Programmer The target functions sumSequence() and square() — appearing earlier in this chapter — are both defined in the code that appears before the actual call. This doesn’t have to be the case: A function may be defined anywhere
in the module. (A module is another name for a C++ source file.)
However, something has to tell the calling function the full name of the func_
tion to be called. Consider the following code snippet:
int main(int nNumberofArgs, char* pszArgs[])
{
someFunc(1, 2);
}
int someFunc(double arg1, int arg2)
{
// ...do something
}
main() doesn’t know the full name of the function someFunc() at the time of the call. It may surmise from the arguments that the name is someFunc(int, int) and that its return type is void — but as you can see, this is incorrect.
I know, I know — C++ could be less lazy and look ahead to determine the full name of someFunc()s on its own, but it doesn’t. It’s like my crummy car; it gets me there, and I’ve learned to live with it.
What is needed is some way to inform main() of the full name of someFunc()
before it is used. This is handled by what we call a function prototype.
A prototype declaration appears the same as a function with no body. In use, a prototype declaration looks like this:
int someFunc(double, int);
int main(int nNumberofArgs, char* pszArgs[])
{
someFunc(1, 2);
}
int someFunc(double arg1, int arg2)
{
// ...do something
}
The prototype declaration tells the world (at least that part of the world after the declaration) that the extended name for someFunc() is someFunction(double, int). The call in main() now knows to cast
the 1 to a double before making the call. In addition, main() knows that the value returned by someFunc() is an int.
Chapter 6: Creating Functions 91
Variable Storage Types
Function variables are stored in three different places. Variables declared within a function are said to be local. In the following example, the variable localVariable is local to the function fn():
int globalVariable;
void fn()
{
int localVariable;
static int staticVariable;
}
The variable localVariable doesnÂ’t exist until execution passes through its declaration within the function fn(). localVariable ceases to exist when the function returns. Upon return, whatever value that is stored in
localVariable is lost. In addition, only fn() has access to localVariable —
other functions cannot reach into the function to access it.
By comparison, the variable globalVariable is created when the program begins execution — and exists as long as the program is running. All func_ tions have access to globalVariable all the time.
The static variable staticVariable is a sort of mix between a local and a global variable. The variable staticVariable is created when execution first reaches the declaration — at roughly when the function fn() is called.
The variable is not destroyed when program execution returns from the func_ tion. If fn() assigns a value to staticVariable once, itÂ’ll still be there the next time fn() is called. The declaration is ignored every subsequent time execution passes through.
In case anyone asks, there is a fourth type, auto, but today it has the same meaning as local, so you rarely (if ever) see that declaration type anymore. So whoever asked you about it is probably just being a show off (or showing his age).
Including Include Files
It’s common to place function prototypes in a separate file (called an include file) that the programmer can then include in her C++ source files. Doing so sets the stage for a C++ preprocessor program (which runs before the actual compiler takes over) to insert the contents of a file such as filename, at the point of a statement such as #include “filename”.
92 Part II: Becoming a Functional C++ Programmer
A definition for a typical math include file looks like this:
// math include file:
// provide prototypes for functions that might be useful
// in more than one program
// abs - return the absolute value of the argument double abs(double d);
// square - return the square of the argument
double square(double d);
A program uses the math include file like this:
// MyProgram -
#include “math”
using namespace std;
// my code continues here
The #include directive says, in effect, Replace this directive with the contents of the math file.
The #include directive doesnÂ’t have the format of a C++ statement because itÂ’s interpreted by a separate interpreter that executes before the C++ com_ piler starts doing its thing. In particular, the # must be in column one and an end-of-line terminates the include statement. The actual file name must be enclosed in either quotes or brackets. Brackets are used for C++ library func_ tions. Use the quotes for any includes that you create.
The C++ environment provides include files such as cstdio and iostream. In fact, itÂ’s iostream that contains the prototype for the setf() function used in Chapter 4 to set output to hex mode.
For years, programmers followed the custom of using the extension .h to designate include files. In more recent years, the C++ ISO standard removed the .h extension from standard include files. (For example, the include file cstdio was previously known as stdio.h.) Many programmers still stub_ bornly cling to the “.h gold standard” for their own programs. (What’s in a name? Evidence that even high-tech folks have traditions.)
Chapter 7
Storing Sequences in Arrays
In This Chapter
Considering the need for something like an array
Introducing the array data type
Using an array
Using the most common type of array — the character string
An array is a sequence of variables that shares the same name and that is referenced using an index. Arrays are useful little critters that allow
you to store a large number of values of the same type and that are related in some way — for example, the batting averages of all the players on the same team might be a good candidate for storage within an array. Arrays can be multidimensional, too, allowing you, for example, to store an array of batting averages within an array of months, which allows you to work with the bat_ ting averages of the team as they occur by month. If you think about it long enough, you get a headache.
In this chapter, you find out how to initialize and use arrays for fun and profit. You also find out about an especially useful form of array, a string, which in C++ is really just an array of type char.
Considering the Need for Arrays
Consider the following problem. You need a program that can read a sequence of numbers from the keyboard. You guessed it — the program stops reading in numbers as soon as you enter a negative number. Unlike similar programs in Chapters 5 and 6, this program is to output all the numbers entered before displaying the average.
94 Part II: Becoming a Functional C++ Programmer
You could try to store numbers in a set of independent variables, as in
cin >> value1;
if (value1 >= 0)
{
cin >> value2;
if (value2 >= 0)
{
...
You can see that this approach canÂ’t handle sequences involving more than just a few numbers. Besides, itÂ’s ugly. What we need is some type of structure that has a name like a variable but that can store more than one value. May I present to you, Ms. A. Ray.
An array solves the problem of sequences nicely. For example, the following snippet declares an array valueArray that has storage for up to 128 int values. It then populates the array with numbers entered from the keyboard.
int value;
// declare an array capable of holding up to 128 ints int valueArray[128];
// define an index used to access subsequent members of
// of the array; donÂ’t exceed the 128 int limit for (int i = 0; i < 128; i++)
{
cin >> value;
// exit the loop when the user enters a negative
// number
if (value < 0)
{
break;
}
valueArray = value;
}
The second line of this snippet declares an array valueArray. Array declara_ tions begin with the type of the array members: in this case, int. This is fol_ lowed by the name of the array. The last element of an array declaration is an open and closed bracket containing the maximum number of elements that the array can hold. In this code snippet, valueArray can store up to 128 integers.
This snippet reads a number from the keyboard and stores it into each sub_ sequent member of the array valueArray. You access an individual element of an array by providing the name of the array followed by brackets contain_ ing the index. The first integer in the array is valueArray[0], the second is valueArray[1], and so on.
Chapter 7: Storing Sequences in Arrays 95
In use, valueArray represents the ith element in the array. The index variable i must be a counting variable — that is, i must be a char, an int, or a long. If valueArray is an array of ints, valueArray is an int.
Using an array
The following program inputs a sequence of integer values from the keyboard until the user enters a negative number. The program then displays the num_ bers input and reports their sum.
// ArrayDemo - demonstrate the use of arrays
// by reading a sequence of integers
// and then displaying them in order
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
// prototype declarations
int sumArray(int integerArray[], int sizeOfloatArray);
void displayArray(int integerArray[], int sizeOfloatArray);
int main(int nNumberofArgs, char* pszArgs[])
{
// input the loop count int nAccumulator = 0;
cout << “This program sums values entered “
<< “by the user\n”;
cout << “Terminate the loop by entering “
<< “a negative number\n”;
cout << endl;
// store numbers into an array int inputValues[128];
int numberOfValues;
for(numberOfValues = 0; numberOfValues < 128; numberOfValues++)
{
// fetch another number int integerValue;
cout << “Enter next number: “;
cin >> integerValue;
// if itÂ’s negative...
if (integerValue < 0)
96 Part II: Becoming a Functional C++ Programmer
{
// ...then exit break;
}
// ... otherwise store the number
// into the storage array inputValues[numberOfValues] = integerValue;
}
// now output the values and the sum of the values displayArray(inputValues, numberOfValues);
cout << “The sum is “
<< sumArray(inputValues, numberOfValues)
<< endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
// displayArray - display the members of an
// array of length sizeOfloatArray
void displayArray(int integerArray[], int sizeOfArray)
{
cout << “The value of the array is:” << endl;
for (int i = 0; i < sizeOfArray; i++)
{
cout.width(3);
cout << i << “: “ << integerArray << endl;
}
cout << endl;
}
// sumArray - return the sum of the members of an
// integer array
int sumArray(int integerArray[], int sizeOfArray)
{
int accumulator = 0;
for (int i = 0; i < sizeOfArray; i++)
{
accumulator += integerArray;
}
return accumulator;
}
The program ArrayDemo begins with a prototype declaration of the functions sumArray() and displayArray() that it will need later. The main body of the program contains an input loop (boring). This time, however, the input values are stored off in the array inputValues.
Chapter 7: Storing Sequences in Arrays 97
If the input value is negative, control exits the loop by executing the break. If not, integerValue is copied into the array. The int variable numberOfValues is used as an index into the array. numberOfValues was initialized to 0 up
at the beginning of the for loop. The index is incremented on each iteration of the loop. The test in the for loop keeps the program from storing more than the 128 integers allocated by the program. (The program goes immedi_ ately to the output portion after 128 entries whether or not the user enters a negative number.)
The array inputValues is declared as 128 integers long. If youÂ’re thinking that this is enough, donÂ’t count on it. Writing more data than an array causes your program to perform erratically and often to crash. No matter how large you make the array, always put a check to make sure that you do not exceed the limits of the array.
The main function ends by calling displayArray() to print the contents of the array and the sum.
The Dev-C++ environment can help keep you and your functions straight. Figure 7-1 shows the contents of the Classes tab. The name and prototype of each function appear there. Double-clicking a function name takes you straight to the function in the .CPP source file.
Figure 7-1: The Classes tab displays information about the functions that make up the program.
The displayArray() function contains the typical for loop used to traverse an array. Each entry in the array is added to the variable accumulator. The sizeOfArray passed to the function indicates the number of values contained in integerArray.
Notice, yet again, that the index is initialized to 0 and not to 1. In addition, notice how the for loop terminates before i is equal to sizeOfArray. None of the elements after the sizeOfArray element contains valid data. The output appears as follows:
98 Part II: Becoming a Functional C++ Programmer
This program sums values entered by the user
Terminate the loop by entering a negative number
Enter next number: 1
Enter next number: 2
Enter next number: 3
Enter next number: -1
The value of the array is:
0: 1
1: 2
2: 3
The sum is 6
Press any key to continue . . .
Just to keep nonprogrammers guessing, the term iterate means to tra_
verse through a set of objects such as an array. Programmers say that
the sumArray() function iterates through the array. In a similar fashion, I “get irate” when my dog iterates from one piece of furniture to another.
Initializing an array
A local variable does not start life with a valid value, not even the value 0. Said another way, a local variable contains garbage until you actually store something in it. Locally declared arrays are the same — each element con_ tains garbage until you actually assign something to it. You should initialize local variables when you declare them. This rule is even truer for arrays. It is far too easy to access uninitialized array elements thinking that they are valid values.
Fortunately, a small array may be initialized at the time it is declared. The fol_
lowing code snippet demonstrates how this is done:
float floatArray[5] = {0.0, 1.0, 2.0, 3.0, 4.0};
This initializes floatArray[0] to 0, floatArray[1] to 1, floatArray[2]
to 2, and so on.
The number of initialization constants can determine the size of the array. For example, you could have determined that floatArray has five elements just by counting the values within the braces. C++ can count as well (hereÂ’s at least one thing C++ can do for itself).
The following declaration is identical to the preceding one.
float floatArray[] = {0.0, 1.0, 2.0, 3.0, 4.0};
Chapter 7: Storing Sequences in Arrays 99
You may initialize all the elements in an array to a common value by listing only that value. For example, the following initializes all 25 locations in floatArray to 1.0.
float floatArray[25] = {1.0};
Accessing too far into an array
Mathematicians start counting arrays with 1. Most program languages start with an offset of 1 as well. C++ arrays begin counting at 0. The first member of a C++ array is valueArray[0]. That makes the last element of a 128-integer array integerArray[127] and not integerArray[128].
Unfortunately for the programmer, C++ does not check to see whether the index you are using is within the range of the array. C++ is perfectly happy giving you access to integerArray[200]. Our yard is only 128 integers long — that’s 72 integers into someone else’s yard. No telling who lives there and what he’s storing at that location. Reading from integerArray[200] will return some unknown and unpredictable value. Writing to that location gen_ erates unpredictable results. It may do nothing — the house may be aban_ doned and the yard unused. On the other hand, it might overwrite some data, thereby confusing the neighbor and making the program act in a seemingly random fashion. Or it might crash the program.
The most common wrong way to access an array is to read or write loca_ tion integerArray[128]. This is one integer beyond the end of the array. Although itÂ’s only one element beyond the end of the array, reading or writ_ ing this location is just as dangerous as using any other incorrect address.
Using arrays
On the surface, the ArrayDemo program doesnÂ’t do anything more than our earlier, non-array-based programs did. True, this version can replay its input by displaying the set of input numbers before calculating their sum, but this feature hardly seems earth shattering.
Yet, the ability to redisplay the input values hints at a significant advantage to using arrays. Arrays allow the program to process a series of numbers multiple times. The main program was able to pass the array of input values to displayArray() for display and then repass the same numbers to sumArray() for addition.
100 Part II: Becoming a Functional C++ Programmer
Defining and using arrays of arrays
Arrays are adept at storing sequences of numbers. Some applications require sequences of sequences. A classic example of this matrix configuration is the spreadsheet. Laid out like a chessboard, each element in the spreadsheet has both an x and a y offset.
C++ implements the matrix as follows:
int intMatrix[10][5];
This matrix is 10 elements in 1 dimension, and 5 in another, for a total of 50 elements. In other words, intMatrix is a 10-element array, each element of which is a 5-int array. As you might expect, one corner of the matrix is in intMatrix[0][0], while the other corner is intMatrix[9][4].
Whether you consider intMatrix to be 10 elements long in the x dimension and in the y dimension is a matter of taste. A matrix can be initialized in the same way that an array is
int intMatrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
This line initializes the 3-element array intMatrix[0] to 1, 2, and 3 and the
3-element array intMatrix[1] to 4, 5, and 6, respectively.
Using Arrays of Characters
The elements of an array are of any type. Arrays of floats, doubles, and longs are all possible; however, arrays of characters have particular significance.
Creating an array of characters
Human words and sentences can be expressed as an array of characters. An array of characters containing my first name would appear as
char sMyName[] = {‘S’, ‘t’, ‘e’, ‘p’, ‘h’, ‘e’, ‘n’};
The following small program displays my name:
// CharDisplay - output a character array to
// standard output, the MS-DOS window
Chapter 7: Storing Sequences in Arrays 101
#include <stdio.h>
#include <iostream.h>
// prototype declarations
void displayCharArray(char stringArray[], int sizeOfloatArray);
int main(int nArg, char* pszArgs[])
{
char charMyName[] = {‘S’, ‘t’, ‘e’, ‘p’, ‘h’, ‘e’, ‘n’};
displayCharArray(charMyName, 7);
cout << “\n”;
return 0;
}
// displayCharArray - display an array of characters
// by outputing one character at
// a time
void displayCharArray(char stringArray[], int sizeOfloatArray)
{
for(int i = 0; i< sizeOfloatArray; i++)
{
cout << stringArray;
}
}
The program declares a fixed array of characters charMyName containing — you guessed it — my name (what better name?). This array is passed to the function displayCharArray() along with its length. The displayCharArray() function is identical to the displayArray() function in the earlier example program except that this version displays chars rather than ints.
This program works fine; however, it is inconvenient to pass the length of the array around with the array itself. If we could come up with a rule for deter_ mining the end of the array, we wouldn’t need to pass its length — you would know that the array was complete when you encountered the special rule that told you so.
Creating a string of characters
In many cases, all values for each element are possible. However, C++ reserves the special “character” 0 as the non-character. We can use ‘\0’ to mark the end of a character array. (The numeric value of ‘\0’ is zero; how_ ever, the type of ‘\0’ is char.)
102 Part II: Becoming a Functional C++ Programmer
The character \y is the character whose numeric value is y. The character \0 is the character with a value of 0, otherwise known as the null character. Using that rule, the previous small program becomes
// DisplayString - output a character array to
// standard output, the MS-DOS window
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
using namespace std;
// prototype declarations
void displayString(char stringArray[]);
int main(int nNumberofArgs, char* pszArgs[])
{
char charMyName[] =
{‘S’, ‘t’, ‘e’, ‘p’, ‘h’, ‘e’, ‘n’, 0};
displayString(charMyName);
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
// displayString - display a character string
// one character at a time void displayString(char stringArray[])
{
for(int i = 0; stringArray != ‘\0’; i++)
{
cout << stringArray;
}
}
The declaration of charMyName declares the character array with the extra null character \0 on the end. The displayString program iterates through the character array until a null character is encountered.
The function displayString() is simpler to use than its displayCharArray() predecessor because it is no longer necessary to pass along the length of the character array. This secret handshake of terminating a character array with
a null is so convenient that it is used throughout C++ language. C++ even gives such an array a special name.
A string of characters is a null terminated character array. Confusingly enough, this is often shortened to simply string, even though C++ defines the separate type string.
Chapter 7: Storing Sequences in Arrays 103
The choice of ‘\0’ as the terminating character was not random. Remember that zero is the only numeric value that converts to false; all other values translate to true. This means that the for loop could (and usually is) writ_ ten as:
for(int i = 0; stringArray; i++)
This whole business of null terminated character strings is so ingrained into the C++ language psyche that C++ uses a string of characters surrounded by double quotes to be an array of characters automatically terminated with a
‘\0’ character. The following are identical declarations:
char szMyName[] = “Stephen”;
char szAlsoMyName[] =
{‘S’, ‘t’, ‘e’, ‘p’, ‘h’, ‘e’, ‘n’, ‘\0’};
The naming convention used here is exactly that, a convention. C++ does not care. The prefix sz stands for zero-terminated string.
The string Stephen is eight characters long and not seven — the null charac_ ter after the n is assumed. The string “” is one character long consisting of just the null character.
Manipulating Strings with Character
The C++ programmer is often required to manipulate strings. C++ provides a number of standard string-manipulation functions to make the job easier. A few of these functions are listed in Table 7-1.
Table 7-1 String-Handling Functions
Name Operation
int strlen(string) Returns the number of characters in a string.
void strcpy(target, source, n) Copies the source string into a target array.
void strcat(target, source, n) Concatenates the source string onto the end of the target string.
void strncpy(target, source, n) Copies a string up to n characters from the source string into a target array.
(continued)
104 Part II: Becoming a Functional C++ Programmer
Table 7-1 (continued)
Name Operation
void strncat(target, source, n) Concatenates the source string onto the end of the target string or
‘n’ characters, whichever comes first.
int strnstr(string, pattern, n) Finds the first occurrence of one pattern string in another.
int strncmp(source1, source2, n) Compares the first n characters in two strings. Returns a zero if the two strings match exactly.
int strnicmp(source1, source2) Compares up to n characters in two strings without regard to case.
You need to add the statement #include <strings.h> to the beginning of any program that uses a str... function.
The current ANSI C++ standard suggests that you avoid the str...() func_ tions. ANSI C++ says that these functions are deprecated, meaning that ANSI will leave them alone for now, but don’t be surprised if they go away some day. That’s why strings.h uses the older standard of ending all include files with a “.h”. The ANSI standard suggests that you use the string type as defined in the next section. However, you will see a large number of programs that continue to use these functions.
The following Concatenate program inputs two strings from the keyboard and concatenates them into a single string.
// Concatenate - concatenate two strings
// with a “ - “ in the middle
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
// the following include file is deprecated;
// however, it is required for the str functions
#include <strings.h>
int main(int nNumberofArgs, char* pszArgs[])
{
// read first string... char szString1[256];
cout << “Enter string #1:”;
cin >> szString1;
Chapter 7: Storing Sequences in Arrays 105
// safer alternative
// cin.getline(szString1, 128);
// ...now the second string... char szString2[128];
cout << “Enter string #2:”;
cin >> szString2;
// safer alternative
// cin.getline(szString1, 128);
// accumulate both strings into a single buffer char szString[260];
// copy the first string into the buffer... strncpy(szString, szString1, 128);
// ...concatenate a “ - “ onto the first... strncat(szString, “ - “, 4);
// ...now add the second string... strncat(szString, szString2, 128);
// ...and display the result
cout << “\n” << szString << endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
The Concatenate program reads two character strings and appends them together with a “ - “ in the middle.
The arguments to the str...() functions appear backward to any reason_ able individual (you might consider this an acid test for “reasonable”). For example, the function strncat(target, source, count) tacks the second string source onto the end of the first argument target.
An example output from the program appears as follows:
Enter string #1:Chester
Enter string #2og
Chester - Dog
Press any key to continue . . .
The program begins by reading a string from the keyboard. cin >> szString1 stops when any type of whitespace is encountered. Characters up to the first whitespace are read, the whitespace character is tossed, and
106 Part II: Becoming a Functional C++ Programmer the remaining characters are left in the input hopper for the next cin>> statement. Thus, if I were to enter “the Dog”, szString2 would be filled
with “the”, and the word “Dog” would be left in the input buffer.
The cin >> extractor knows nothing about the length of the string. cin is perfectly willing to read thousands of characters and stuff them into szString1, even though it is declared 256 characters long. This causes a dangerous overflow condition that hackers can (and will) exploit to put a virus in your program.
C++ provides work-arounds for many of the string overflow problems. For example, the function getline() inputs a line of text; however, this function accepts the length of the string as one of its arguments:
cin.getline(string, lengthOfTheString);
(Ignore the strange looking cin. format for now.)
The strncpy() and strncat() functions accept the length of the target buffer as one of their arguments. The call strncpy(szString, szString1,
128) says “copy the characters in szString1 into szString until you copy a null character or until you’ve copied 128 characters, whichever comes first.” The call specifically does not mean copy 128 characters every time.
There are both “counting” and “noncounting” versions of most of the str...() functions. The noncounting versions don’t require the maximum number of characters to process as an argument. You can use these when you don’t know the buffer size, but be aware that they are perfectly happy to write beyond the end of the target string.
String-ing Along Variables
ANSI C++ includes a type string designed to make it easier to manipulate strings of text.
I use the term string to refer to an array of characters terminated by a null and string type to refer to the type string. The string type includes opera_ tions for copying, concatenating, capitalizing, knotting, and simple magic tricks. string avoids the overrun problems inherent with null terminated strings. The functions that manipulate string objects are defined in the include file <string>.
The string type based StringConcatenate program appears as follows:
Chapter 7: Storing Sequences in Arrays 107
// StringConcatenate - concatenate two string type
// variables with a “ - “ in the middle
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// read first string... string string1;
cout << “Enter string #1:”;
cin >> string1;
// ...now the second string... string string2;
cout << “Enter string #2:”;
cin >> string2;
// accumulate both strings into a single buffer string buffer;
string divider = “ - “;
buffer = string1 + divider + string2;
// ...and display the result cout << “\n” << buffer << endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
This concatenate function defines two variables, string1 and string2
(clever, no?). A string type variable is not defined of any specified length — it can grow and shrink to fit the number of characters it contains (up to avail_ able memory, of course, or the cows come home, whichever is first). Not only do you not have to guess how big to make a target character array, but some nefarious user can’t crash your program by inputting too many characters. The StringConcatenate program manipulates the string type variables as it would any other.
Notice that some operations have to be understood in a slightly different way from their arithmetic equivalent. For example, to add two string type vari_ ables together means to concatenate them. In addition, notice how C++ can convert a null terminated character string into a string type variable with_ out being told to.
108 Part II: Becoming a Functional C++ Programmer
The string type is not intrinsic to C++ like int or float, meaning that its operations are not built into the syntax of the language. Operations on string type variables are defined in the string include file. The string class is discussed further in Chapter 27. I mention string here to demon_ strate that it is often easier to use than manipulating null terminated charac_ ter arrays yourself.
Chapter 8
Taking a First Look at C++ Pointers
In This Chapter
Addressing variables in memory
Declaring and using pointer variables Recognizing the inherent dangers of pointers Passing pointers to functions
Allocating objects off the heap (whatever that is)
So far, the C++ language has been fairly conventional compared with other programming languages. Sure, some computer languages lack (il-)logical operators (see Chapter 4 for more) and C++ has its own unique symbols, but thereÂ’s nothing new in the way of concepts. C++ really separates itself from the
crowd in definition and use of pointer variables. A pointer is a variable that
“points at” other variables. I realize that’s a circular argument, but let me put it this way: A pointer variable contains the address of a variable in memory.
This chapter introduces the pointer variable type. It begins with some con_ cept definitions, flows through pointer syntax, and then introduces some of the reasons for the pointer mania that grips the C++ programming world.
Variable Size
My weight goes up and down all the time, but here IÂ’m really referring to the size of a variable, not my own variable size. Memory is measured in bytes or bits. The following program gives you the size of the different variable types:
// VariableSize - output the size of each type of
// variable
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
110 Part II: Becoming a Functional C++ Programmer
{
bool b; char c; int n; long l; float f; double d;
cout << “sizeof a bool = “ << sizeof b << endl; cout << “sizeof a char = “ << sizeof c << endl; cout << “sizeof an int = “ << sizeof n << endl; cout << “sizeof a long = “ << sizeof l << endl; cout << “sizeof a float = “ << sizeof f << endl; cout << “sizeof a double= “ << sizeof d << endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
sizeof is actually a C++ construct that returns the size of its argument in bytes. The variable size program generates the following output:
sizeof a bool = 1 sizeof a char = 1 sizeof an int = 4 sizeof a long = 4 sizeof a float = 4 sizeof a double= 8
Press any key to continue . . .
DonÂ’t be concerned if the compiler that youÂ’re using generates different results. For example, you may find that an int is smaller than a long. C++ doesnÂ’t say exactly how big a variable type must be; it just says that a long is the same
size as or larger than an int and that a double is the same size as or larger than a float. The sizes output by the VariableSize program are typical for a
32-bit processor such as the Pentium class processors.
WhatÂ’s in an Address?
Like the saying goes: “Everyone has to be somewhere.” Every C++ variable is stored somewhere in the computer’s memory. Memory is broken into individ_ ual bytes with each byte carrying its own address numbered 0, 1, 2, and so on.
A variable intReader might be at address 0x100, whereas floatReader might be over at location 0x180. (By convention, memory addresses are expressed in hexadecimal.) Of course, intReader and floatReader might
Chapter 8: Taking a First Look at C++ Pointers 111
be somewhere else in memory entirely — only the computer knows for sure and only at the time that the program is executed.
This is somewhat analogous to a hotel. When you make your reservation, you may be assigned room 0x100. (I know that suite numbers are normally not expressed in hexadecimal, but bear with me.) Your buddy may be assigned
80 doors down in room 0x180. Each variable is assigned an address when it is created (more on that later in this chapter when we talk about scope).
Address Operators
The two pointer-related operators are shown in Table 8-1. The & operator says “tell me your hotel address,” and * says “his address is.”
Table 8-1 Pointer Operators
Operator Meaning
& (unary) The address of
* (unary) (In an expression) the thing pointed at by
* (unary) (In a declaration) pointer to
The following Layout program demonstrates how the & operator displays the layout of memory variables in memory:
// Layout - this program tries to give the
// reader an idea of the layout of
// local memory in her compiler
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
int end; int n; long l; float f; double d;
// set output to hex mode cout.setf(ios::hex); cout.unsetf(ios::dec);
112 Part II: Becoming a Functional C++ Programmer
// output the address of each variable
// in order to get an idea of how variables are
// laid out in memory
cout << “--- = 0x” << &end << “\n”; cout << “&n = 0x” << &n << “\n”; cout << “&l = 0x” << &l << “\n”; cout << “&f = 0x” << &f << “\n”; cout << “&d = 0x” << &d << “\n”;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
The program declares a set of variables. It then applies the & operator to each one to find out where it lies in memory. The results of one execution of this program with Dev-C++ appear as follows:
--- = 0x0x22ff6c
&n = 0x0x22ff68
&l = 0x0x22ff64
&f = 0x0x22ff60
&d = 0x0x22ff58
Press any key to continue . . .
Your results may vary. The absolute address of program variables depends on a lot of factors. In general, it may even vary from one execution of the pro_ gram to the next.
Notice how the variable n is exactly 4 bytes from the first variable declared
(m2). The variable l appears 4 bytes down from that. The double variable d is a full 8 bytes from its neighboring variable f. Each variable has been allo_ cated just the space needed for its type.
There is no requirement that the C++ compiler pack variables in memory with no spaces between them. Dev-C++ could have laid out the variables in memory in any other reasonable fashion.
Using Pointer Variables
A pointer variable is a variable that contains an address, usually the address of another variable. Returning to my hotel analogy for a moment, I might tell my son that I will be in room 0x100 on my trip. My son is a pointer variable of sorts. Anyone can ask him at any time, “Where’s your father staying?” and he’ll spill his guts without hesitation.
Chapter 8: Taking a First Look at C++ Pointers 113
The following pseudo-C++ demonstrates how the two address operators shown in Table 8-1 are used:
mySon = &DadsRoom; // tell mySon the address of DadÂ’s Room
room = *mySon; // “Dad’s room number is”
The following C++ code snippet shows these operators used correctly:
void fn()
{
int intVar;
int* pintVar;
pintVar = &intVar; // pintVar now points to intVar
*pintVar = 10; // stores 10 into int location
// pointed at by pintVar
}
The function fn() begins with the declaration of intVar. The next statement declares the variable pintVar to be a variable of type pointer to an int. (By the way, pintVar is pronounced pee-int-Var, not pint-Var.)
Pointer variables are declared like normal variables except for the addition of the unary * character. This * character can appear anywhere between the base type name — in this case int — and the variable name; however, it is becoming increasingly common to add the * to the end of the type.
The * character is called the asterisk character (thatÂ’s logical enough), but because asterisk is hard to say, many programmers have come to call it the splat character. Thus, they would say splat pintVar.
Many programmers adopt a naming convention in which the first character of the variable name indicates the type of the variable, such as n for int, d for double, and so on. A further aspect of this naming convention is to place a p at the beginning of a pointer variable name.
In an expression, the unary operator & means the address of. Thus, we would read the first assignment as store the address of intVar in pintVar.
To make this more concrete, assume that the memory for function fn() starts at location 0x100. In addition, assume that intVar is at address 0x102 and that pintVar is at 0x106. The layout here is simpler than the actual results from the Layout program; however, the concepts are identical.
The first assignment stores the value of & intVar (0x102) in the pointer vari_
able pintVar. The second assignment in the small program snippet says
“store 10 in the location pointed at by pintVar.” The value 10 is stored in the address contained in pintVar, which is 0x102 (the address of intVar).
114 Part II: Becoming a Functional C++ Programmer
Comparing pointers and houses
A pointer is much like a house address. Your house has a unique address. Each byte in memory has an address that is unique. A house address is made up of both numbers and letters. For example, my address is 123 Main Street.
You can store a couch in the house at 123 Main Street — you can store a number in the byte located at 0x123456. Alternatively, you can take a piece of paper and write an address — I don’t know, say, 123 Main Street. You can now store a couch at the house with the address written on the piece of paper. In fact, this is the way delivery people work — their job is to deliver a couch to the address written down on the shipping orders whether it’s 123 Main Street or not. (I’m not maligning delivery people — they have brains — it’s just that this is more or less the way things work.)
In C++, the following code snippet finds the address of myHouse and stores a couch at that houseAddress (loosely speaking):
House myHouse; House* houseAddress;
houseAddress = &myHouse;
*houseAddress = couch;
In humanspeak, you would say myHouse is a House. houseAddress is the address of a House. Assign the address of myHouse to the House pointer, houseAddress. Now store a couch at the house located at the address stored in houseAddress.
Having said all that, take a look at the int and int* version of the earlier example code snippet:
int myInt;
int* intAddress;
intAddress = &myInt;
*intAddress = 10;
That is, myInt is an int. intAddress is a pointer to an int. Assign the address of myInt to the pointer intAddress. Finally, assign 10 to the int that intAddress points to.
Using different types of pointers
Every expression has a type as well as a value. The type of the expression intVar is pointer to an integer, written as int*. Comparing this with the dec_ laration of pintVar, you see that the types match exactly:
int* pintVar = &intVar; // both sides of the assignment are
// of type int*
Chapter 8: Taking a First Look at C++ Pointers 115
Similarly, because pintVar is of type int*, the type of *pintVar is int:
*pintVar = 10; // both sides of the assignment are
// of type int
The type of the thing pointed to by pintVar is int. This is equivalent to saying that, if houseAddress is the address of a house, the thing pointed at by houseAddress must be a house. Amazing, but true.
Pointers to other types of variables are expressed the same way:
double doubleVar;
double* pdoubleVar = &doubleVar;
*pdoubleVar = 10.0;
A pointer on a Pentium class machine takes 4 bytes no matter what it points to. That is, an address on a Pentium is 4 bytes long, period.
Matching pointer types is extremely important. Consider what might happen if the following were allowed:
int n1;
int* pintVar;
pintVar = &n1;
*pintVar = 100.0;
The second assignment attempts to store the 8-byte double value 100.0 into the 4-byte space allocated for n1. Actually, this isn’t as bad as it looks — C++ is smart enough to demote the constant 100.0 to an int before making the assignment.
It is possible to cast one type of variable into another:
int iVar;
double dVar = 10.0;
iVar = (int)dVar;
Similarly, it is possible to cast one pointer type into another.
int* piVar;
double dVar = 10.0;
double* pdVar;
piVar = (int*)pdVar;
Consider, however, what catastrophes can arise if this type of casting about of pointers were to get loose. Save a variable into an area of the wrong size, and nearby variables can be wiped out. This is demonstrated graphically in the following LayoutError program.
116 Part II: Becoming a Functional C++ Programmer
// LayoutError - demonstrate the results of
// a messing up a pointer usage
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
int upper = 0; int n = 0; int lower = 0;
// output the values of the three variables before... cout << “the initial values are” << endl;
cout << “upper = “ << upper << endl; cout << “n = “ << n << endl; cout << “lower = “ << lower << endl;
// now store a double into the space
// allocated for an int
cout << “\nStoring 13.0 into the location &n” << endl;
double* pD = (double*)&n;
*pD = 13.0;
// display the results
cout << “\nThe final results are:” << endl;
cout << “upper = “ << upper << endl; cout << “n = “ << n << endl; cout << “lower = “ << lower << endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
The first three lines in main() declare three integers in the normal fashion. The assumption made here is that these three variables are laid out next to each other.
The next three executable lines output the value of the three variables. Not surprisingly, all three variables display as 0. The assignment *pD = 13.0; stores the double value 13.0 in the integer variable n. The three output state_ ments display the values of all three variables after the assignment.
After assigning the double value 13.0 in the integer variable n, n is not modi_ fied at all; however, the nearby variable upper is filled with a garbage value. This is not good, as the output from the program shows:
the initial values are
upper = 0
Chapter 8: Taking a First Look at C++ Pointers 117
n = 0
lower = 0
Storing 13.0 into the location &n
The final results are:
upper = 1076494336
n = 0
lower = 0
Press any key to continue . . .
The house equivalent goes something like this:
House* houseAddress = &”123 Main Street”; Hotel* hotelAddress;
hotelAddress = (Hotel*)houseAddress;
*hotelAddress = TheRitz;
houseAddress is initialized to point to my house. The variable hotelAddress is a pointer to a hotel. Now, the house address is cast into the address of a hotel and saved off into the variable hotelAddress. Finally, TheRitz is plopped down on top of my house. Because TheRitz is slightly bigger than my house (okay, a lot bigger than my house), it isnÂ’t surprising that TheRitz wipes out my neighborsÂ’ houses as well.
The type of pointer saves the programmer from stuffing an object into a space that is too big or too small. The assignment *pintVar = 100.0; actu_ ally causes no problem — because C++ knows that pintVar points to an int, C++ knows to demote the 100.0 to an int before making the assignment.
Passing Pointers to Functions
One of the uses of pointer variables is in passing arguments to functions. To understand why this is important, you need to understand how arguments are passed to a function.
Passing by value
You may have noticed that it is not normally possible to change the value of a variable passed to a function from within the function. Consider the following example code segment:
118 Part II: Becoming a Functional C++ Programmer
void fn(int intArg)
{
intArg = 10;
// value of intArg at this point is 10
}
void parent(void)
{
int n1 = 0;
fn(n1);
// value of n1 at this point is 0
}
Here the parent() function initializes the integer variable n1 to zero. The value of n1 is then passed to fn(). Upon entering the function, intArg is equal to 10, the value passed. fn() changes the value of intArg before returning to parent(). Perhaps surprisingly, upon returning to parent(), the value of n1 is still 0.
The reason is that C++ doesnÂ’t pass a variable to a function. Instead, C++ passes the value contained in the variable at the time of the call. That is, the expression is evaluated, even if it is just a variable name, and the result is passed.
It is easy for a speaker to get lazy and say something like, “Pass the variable x
to the function fn().” This really means to pass the value of the expression x.
Passing pointer values
Like any other intrinsic type, a pointer may be passed as an argument to a function:
void fn(int* pintArg)
{
*pintArg = 10;
}
void parent(void)
{
int n = 0;
fn(&n); // this passes the address of i
// now the value of n is 10
}
In this case, the address of n is passed to the function fn() rather than the value of n. The significance of this difference is apparent when you consider the assignment within fn().
Figure 1-1:
You must
uninstall earlier versions of Dev-C++ before you begin the installation process.
2. If you donÂ’t have to uninstall an old version of Dev-C++, skip to Step 4; if you do have to uninstall, abort the current installation process by closing the Run window.
DonÂ’t get upset if youÂ’ve never even heard of Dev-C++ and you still get the warning message. ItÂ’s just a reminder.
3. Okay, if youÂ’re on this step, youÂ’re uninstalling: Open the Dev-CPP
folder and double-click the Uninstall.exe file there.
The uninstall program does its thing, preparing the way for the new instal_ lation; the End User Legal Agreement (commonly known as the EULA) appears.
4. Read the EULA and then click OK if you can live with its provisions. Nope, the package really wonÂ’t install itself if you donÂ’t accept. Assuming you do click OK, Dev-C++ opens the window shown in Figure 1-2 and offers you some installation options. The defaults are innocuous, with two exceptions:
• You must leave the Mingw compiler system. . . option enabled.
• The Associate C and C++ Files to Dev-C++ option means that double- clicking a .CPP file automatically opens Dev-C++ rather than some other program (such as Visual C++ .NET, for example). It is possible, but difficult, to undo this association.
DonÂ’t check this option if you also have Visual Studio.NET installed. Dev- C++ and Visual Studio.NET coexist peacefully on the same machine, but what Visual Studio has done, let no man cast assunder. You can still open your .CPP files with Dev-C++ by right-clicking on the file and selecting Open With. Personally, I prefer to use this option, even with Visual Studio.NET installed. It doesnÂ’t cause any problems, and Dev-C++ starts a lot faster than Visual Studio.
14 Part I: Introduction to C++ Programming
Figure 1-2: The default installation options should be acceptable to most users.
5. Click the Next button.
The installation program asks where you want it to install Dev-C++, using a message like that shown in Figure 1-3.
Figure 1-3: The default location for the Dev-C++ environment is provided.
6. Accept the default directory, c:\Dev-CPP.
Don’t install Dev-C++ in the directory \Program Files with all the other executables. That’s because Dev-C++ doesn’t do well with directories that contain spaces in their names. I haven’t experimented much along these lines, but I believe you can use any other directory name without any special characters other than ‘_’. It’s safer just to accept the default.
7. Make sure you have enough room for the program, wherever you decide to put it.
The Dev-C++ environment uses only a paltry 45MB, but itÂ’s always good practice to check.
8. Click Install.
At first, nothing seems to happen. Then Dev-C++ gets going, copying a whole passel of files to the Dev-CPP directory — putting absolutely noth_ ing in the Windows home directory. Figure 1-4 displays the eventual result.
Chapter 1: Writing Your First C++ Program 15
Figure 1-4: The Dev- C++ installation process unzips a large number of mostly small files.
While the installation is going on, Dev-C++ presents a window that asks whether you want to install for all users once it’s done copying files onto your hard drive. That question boils down to this: If someone else logs on to your computer, do you want her or him to be able to execute Dev- C++? (The answer is “Yes” in my case.)
9. Choose whether you want to install for all users, and then click the
Close button to complete installation of the package.
Dev-C++ starts immediately, so you can set its options properly for your needs. (Yep, thereÂ’s more work to do. But you knew that. Read on.)
Setting the options
As you probably know if youÂ’ve spent more than a coffee breakÂ’s worth of time installing software, setting options is a procedure unto itself. In this case, Dev- C++ has two options that must be set before you can use it. Set Â’em as follows:
1. Choose Tools➪Compiler Options.
You can change these settings at any time, but now is as good as any.
2. Choose the Settings tab.
3. Choose Code Generation from the menu on the left.
Make sure that the Enable Exception Handling is enabled, as shown in Figure 1-5. (If it isnÂ’t, click on the option box to display the two choices and select Yes.)
4. Choose Linker and make sure the Generate Debugging Information option is enabled.
Figure 1-6 shows you what to look for.
16 Part I: Introduction to C++ Programming
Figure 1-5: The Enable Exception Handling option must be enabled.
Figure 1-6: The Generate Debugging Information option must be enabled.
5. Choose OK.
Installation is now complete! (Your options are saved automatically.)
Creating Your First C++ Program
In this section, you create your first C++ program. You first enter the C++ code into a file called CONVERT.CPP, and then convert the C++ code into an exe_ cutable program.
Chapter 1: Writing Your First C++ Program 17
Entering the C++ code
The first step to creating any C++ program is to enter C++ instructions using a text editor. The Dev-C++ user interface is built around a program editor specifi_ cally designed to create C++ programs.
1. Click Start➪Programs➪Bloodshed Dev-C++➪Dev-C++ to start up the
Dev-C++ tool.
The Dev-C++ interface looks fundamentally like that of any other Win_ dows program — perhaps a little clunkier, but a Windows application nonetheless.
This is a lot of clicking. My personal preference is to create a shortcut on the desktop. To create a shortcut, double-click My Computer. Now double-click the Local Disk (C. Finally, double-click Dev-CPP — whew! Right-click the file devcpp.exe and choose Create Shortcut from the drop down menu. Drag the Shortcut to devcpp.exe file onto your desktop (or some other easily accessible place). From now on, you can just double-click the shortcut to start Dev-C++.
2. Choose File➪New➪Source File.
Dev-C++ opens a blank window wherein you get to enter your new code. Don’t worry if you find yourself wishing you knew what to enter right now — that’s why you bought this book.
3. Enter the following program exactly as written.
Don’t worry too much about indentation or spacing — it isn’t critical whether a given line is indented two or three spaces, or whether there are one or two spaces between two words. C++ is case sensitive, how_ ever, so you need to make sure everything is lowercase.
You can cheat and copy the Conversion.cpp file contained on the
enclosed CD-ROM in directory \CPP_Programs\Chap01.
//
// Program to convert temperature from Celsius degree
// units into Fahrenheit degree units:
// Fahrenheit = Celsius * (212 - 32)/100 + 32
//
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
18 Part I: Introduction to C++ Programming
// enter the temperature in Celsius
int celsius;
cout << “Enter the temperature in Celsius:”;
cin >> celsius;
// calculate conversion factor for Celsius
// to Fahrenheit
int factor;
factor = 212 - 32;
// use conversion factor to convert Celsius
// into Fahrenheit values
int fahrenheit;
fahrenheit = factor * celsius/100 + 32;
// output the results (followed by a NewLine)
cout << “Fahrenheit value is:”;
cout << fahrenheit << endl;
// wait until user is ready before terminating program
// to allow the user to see the program results
system(“PAUSE”);
return 0;
}
4. Choose Save As under the File menu. Then type in the program name and press Enter.
I know that it may not seem all that exciting, but youÂ’ve just created your first C++ program!
For purposes of this book, I created a folder CPP_Programs. Within this, I created Chap01. Finally, I saved the program with the name Conversion. cpp. Note that Dev-C++ won’t work properly with directory names that contain spaces. (It doesn’t have a problem with names longer than eight characters in length — thank goodness!)
Building your program
After youÂ’ve saved your Conversion.cpp C++ source file to disk, itÂ’s time to generate the executable machine instructions.
To build your Conversion.cpp program, you choose Execute➪Compile from the menu or press F9 — or you can even click that cute little icon with four colored squares on the menu bar (use the Tool Tips to see which one I’m talk_ ing about). In response, Dev-C++ opens a compiling window. Nothing will happen at first (sshh . . . it’s thinking). After a second or two, Dev-C++ seems to take off, compiling your program with gusto. If all goes well, a window like that shown in Figure 1-7 appears.
Chapter 1: Writing Your First C++ Program 19
Figure 1-7: The user is rewarded with a simple Done message if his program is error free.
Dev-C++ generates a message if it finds any type of error in your C++ program — and coding errors are about as common as snow in Alaska. You’ll undoubtedly encounter numerous warnings and error messages, probably even when enter_ ing the simple Conversion.cpp. To demonstrate the error-reporting process, let’s change Line 17 from cin >> celsius; to cin >>> celsius;.
This seems an innocent enough offense — forgivable to you and me perhaps, but not to C++. Dev-C++ opens a Compiler tab, as shown in Figure 1-8. The mes_ sage parse error before ‘> is perhaps a little terse, but descriptive. To get rid of the message, remove the extra > and recompile.
Figure 1-8: Bad little programs generate error messages in the Compiler window.
20 Part I: Introduction to C++ Programming
Why is C++ so picky?
In the example given here, C++ could tell right away — and without a doubt — that I had
screwed up. However, if C++ can figure out what I did wrong, why doesnÂ’t it just fix the prob_
lem and go on?
The answer is simple but profound. C++ thinks that I mistyped the >> symbol, but it may be mistaken. What could have been a mistyped
command may actually be some other, com_
pletely unrelated error. Had the compiler simply
corrected the problem, C++ would have masked the real problem.
Finding an error buried in a program that
builds without complaining is difficult and time- consuming. ItÂ’s far better to let the compiler find
the error for you if at all possible. Generating
a compiler error is a waste of the computerÂ’s
time — forcing me to find a mistake that C++ could have caught is a waste of my time. Guess which one I vote for?
The term parse means to convert the C++ commands into something that the machine-code-generating part of the process can work with.
There was once a language that tried to fix simple mistakes like this for you.
From my personal experience, I can tell you it was a waste of time — because
(except for very simple cases) the compiler was almost always wrong. At least it warned me of the problem so I could fix it myself.
Executing Your Program
It’s now time to execute your new creation . . . that is, to run your program. You will run the CONVERT.EXE program file and give it input to see how well it works. To execute the Conversion program, click Execute➪Run or press Ctrl+F10.
(I have no idea how they selected function keys. I would think that an action as common as executing a program would warrant its own function key — something without a Control or Shift key to hold down — but maybe that’s just me.)
A window opens immediately, requesting a temperature in Celsius. Enter a known temperature, such as 100 degrees. After you press Enter, the program returns with the equivalent temperature of 212 degrees Fahrenheit as follows:
Enter the temperature in Celsius:100
Fahrenheit value is:212
Press any key to continue . . .
Chapter 1: Writing Your First C++ Program 21
The message Press any key gives you the opportunity to read what youÂ’ve entered before it goes away. Press Enter, and the window (along with its con_ tents) disappears. Congratulations! You just entered, built, and executed your first C++ program.
Dev-C++ is not Windows
Notice that Dev-C++ is not truly intended for developing Windows programs. In theory, you can write a Windows application by using Dev-C++, but it isnÂ’t easy. (ThatÂ’s so much easier in Visual Studio.NET.)
Windows programs show the user a very visually oriented output, all nicely arranged in onscreen windows. Convesion.exe is a 32-bit program that exe_ cutes under Windows, but it’s not a “Windows” program in the visual sense.
If you donÂ’t know what 32-bit program means, donÂ’t worry about it. As I said earlier, this book isnÂ’t about writing Windows programs. The C++ programs you write in this book have a command line interface executing within an MS_ DOS box.
Budding Windows programmers shouldn’t despair — you didn’t waste your money. Learning C++ is a prerequisite to writing Windows programs. I think that they should be mastered separately: C++ first, Windows second.
Dev-C++ help
Dev-C++ provides a Help menu item. Choose Help followed by Help on Dev C++ to open up a typical Windows help box. Help is provided on various aspects of the Dev-C++ development package but not much else. Noticeably lacking is help on the C++ language itself. Click a topic of interest to display help.
Reviewing the Annotated Program
Entering data in someone else’s program is about as exciting as watching some_ one else drive a car. You really need to get behind the wheel itself. Programs are a bit like cars as well. All cars are basically the same with small differences and additions — OK, French cars are a lot different than other cars, but the point is still valid. Cars follow the same basic pattern — steering wheel in front of you, seat below you, roof above you and stuff like that.
22 Part I: Introduction to C++ Programming Similarly, all C++ programs follow a common pattern. This pattern is already present in this very first program. We can review the Conversion program by looking for the elements that are common to all programs.
Examining the framework for all C++ programs
Every C++ program you write for this book uses the same basic framework, which looks a lot like this:
//
// Template - provides a template to be used as the starting
// point
//
// the following include files define the majority of
// functions that any given program will need
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// your C++ code starts here
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
Without going into all the boring details, execution begins with the code con_ tained in the open and closed braces immediately following the line begin_ ning main().
I have copied this code into a file called Template.cpp located in the main
CPP_Programs folder on the enclosed CD-ROM.
Clarifying source code with comments
The first few lines in Conversion.cpp appear to be freeform text. Either this code was meant for human eyes or C++ is a lot smarter than I give it credit for. These first six lines are known as comments. Comments are the programmerÂ’s
Chapter 1: Writing Your First C++ Program 23
explanation of what he or she is doing or thinking when writing a particular code segment. The compiler ignores comments. Programmers (good program_ mers, anyway) donÂ’t.
A C++ comment begins with a double slash (//) and ends with a newline. You can put any character you want in a comment. A comment may be as long as you want, but it’s customary to keep comment lines to no more than 80 char_ acters across. Back in the old days — “old” is relative here — screens were limited to 80 characters in width. Some printers still default to 80 characters across when printing text. These days, keeping a single line to under 80 char_ acters is just a good practical idea (easier to read, less likely to cause eye_ strain, the usual).
A newline was known as a carriage return back in the days of typewriters — when the act of entering characters into a machine was called typing and not keyboarding. A newline is the character that terminates a command line.
C++ allows a second form of comment in which everything appearing after a
/* and before a */ is ignored; however, this form of comment isnÂ’t normally used in C++ anymore. (Later in this book, I describe the one case in which this type of comment is applied.)
It may seem odd to have a command in C++ (or any other programming lan_ guage) thatÂ’s specifically ignored by the computer. However, all computer lan_ guages have some version of the comment. ItÂ’s critical that the programmer explain what was going through her mind when she wrote the code. A pro- grammerÂ’s thoughts may not be obvious to the next colleague who picks up her program and tries to use it or modify it. In fact, the programmer herself may forget what her program meant if she looks at it months after writing the original code and has left no clue.
Basing programs on C++ statements
All C++ programs are based on what are known as C++ statements. This sec_ tion reviews the statements that make up the program framework used by the Conversion.cpp program.
A statement is a single set of commands. All statements other than comments end with a semicolon. (ThereÂ’s a reason that comments donÂ’t end with a semicolon, but itÂ’s obscure. To my mind, comments should end in semicolons as well, for consistencyÂ’s sake. Why nobody asked me about that remains a mystery.)
24 Part I: Introduction to C++ Programming Program execution begins with the first C++ statement after the open brace and continues through the listing, one statement at a time.
As you look through the program, you can see that spaces, tabs, and newlines appear throughout the program. In fact, I place a newline after every state_
ment in this program. These characters are collectively known as white space
because you canÂ’t see them on the monitor.
You may add white space anywhere you like in your program to enhance readability — except in the middle of a word:
See wha
t I mean?
Although C++ may ignore white space, it doesnÂ’t ignore case. In fact, itÂ’s case sensitive to the point of obsession. The variable fullspeed and the variable FullSpeed have nothing to do with each other. While the command int may be understood completely, C++ has no idea what INT means.
Writing declarations
The line int nCelsius; is a declaration statement. A declaration is a state_ ment that defines a variable. A variable is a “holding tank” for a value of some type. A variable contains a value, such as a number or a character.
The term variable stems from algebra formulae of the following type:
x = 10
y = 3 * x
In the second expression, y is set equal to 3 times x, but what is x? The vari_ able x acts as a holding tank for a value. In this case, the value of x is 10, but we could have just as well set the value of x to 20 or 30 or –1. The second for_ mula makes sense no matter what the value of x.
In algebra, youÂ’re allowed to begin with a statement, such as x = 10. In C++, the programmer must first define the variable x before she can use it.
In C++, a variable has a type and a name. The variable defined on Line 11 is called celsius and declared to hold an integer. (Why they couldnÂ’t have just said integer instead of int, IÂ’ll never know. ItÂ’s just one of those things you learn to live with.)
Chapter 1: Writing Your First C++ Program 25
The name of a variable has no particular significance to C++. A variable must begin with the letters A through Z or a through z. All subsequent characters must be a letter, a digit 0 through 9 or an underscore (_). Variable names can be as long as you want to make them.
ItÂ’s convention that variable names begin with a lowercase letter. Each new word within a variable begins with a capital letter, as in myVariable.
Try to make variable names short but descriptive. Avoid names such as x because x has no particular meaning. A variable name such as lengthOfLine Segment is much more descriptive.
Generating output
The lines beginning with cout and cin are known as input/output statements, often contracted to I/O statements. (Like all engineers, programmers love con_ tractions and acronyms.)
The first I/O statement says output the phrase Enter the temperature in Celsius to cout (pronounced “see-out”). cout is the name of the standard C++ output device. In this case, the standard C++ output device is your monitor.
The next line is exactly the opposite. It says, in effect, Extract a value from the C++ input device and store it in the integer variable celsius. The C++ input device is normally the keyboard. What weÂ’ve got here is the C++ analog to the algebra formula x = 10 just mentioned. For the remainder of the program, the value of celsius is whatever the user enters there.
Calculating Expressions
All but the most basic programs perform calculations of one type or another. In C++, an expression is a statement that performs a calculation. Said another way, an expression is a statement that has a value. An operator is a command that generates a value.
For example, in the Conversion example program — specifically in the two lines marked as a calculation expression — the program declares a vari_ able factor and then assigns it the value resulting from a calculation. This par_ ticular command calculates the difference of 212 and 32; the operator is the minus sign (–), and the expression is 212–32.
26 Part I: Introduction to C++ Programming
Storing the results of expression
The spoken language can be very ambiguous. The term equals is one of those ambiguities. The word equals can mean that two things have the same value as in “5 cents equals a nickel.” Equals can also imply assignment, as in math when you say that “y equals 3 times x.”
To avoid ambiguity, C++ programmers call the assignment operator, which says
(in effect), Store the results of the expression to the right of the equal sign in the variable to the left. Programmers say that “factor is assigned the value
212 minus 32.”
Never say “factor is equal to 212 minus 32.” You’ll hear this from some lazy types, but you and I know better.
Examining the remainder of
Conversion.cpp
The second expression in Conversion.cpp presents a slightly more compli_ cated expression than the first. This expression uses the same mathematical symbols: * for multiplication, / for division and, + for addition. In this case, how_ ever, the calculation is performed on variables and not simply on constants.
The value contained in the variable called factor (calculated immediately prior, by the way) is multiplied by the value contained in celsius (which was input from the keyboard). The result is divided by 100 and summed with 32. The result of the total expression is assigned to the integer variable fahrenheit.
The final two commands output the string Fahrenheit value is: to the display, followed by the value of fahrenheit — and all so fast that the user scarcely knows it’s going on.
Chapter 2
Declaring Variables Constantly
In This Chapter
Declaring variables
Declaring different types of variables
Using floating-point variables
Declaring and using other variable types
The most fundamental of all concepts in C++ is the variable — a variable is like a small box. You can store things in the box for later use, particularly
numbers. The concept of a variable is borrowed from mathematics. A state_
ment such as
x = 1
stores the value 1 in the variable x. From that point forward, the mathemati_ cian can use the variable x in place of the constant 1 — until she changes the value of x to something else.
Variables work the same way in C++. You can make the assignment
x = 1;
From that point forward in the program, until the value of x is changed, any references to x are the same as referencing 1. That is, the value of x is 1.
Unfortunately, C++ has a few more concerns about variables than the mathe_
matician does. This chapter deals with the care and feeding of variables in C++.
Declaring Variables
C++ saves numeric values in small storage boxes known as variables. Mathe_ maticians throw variables around with abandon. A mathematician might (for example) write down something like the following:
28 Part I: Introduction to C++ Programming
(x + 2) = y / 2
x + 4 = y
solve for x and y
Any reader whoÂ’s had algebra realizes right off that the mathematician has introduced the variables x and y. But C++ isnÂ’t that smart (computers may be fast, but theyÂ’re stupid).
You have to announce each variable to C++ before you can use it. You have to say something soothing like this:
int x;
x = 10;
int y;
y = 5;
These lines of code declare that a variable x exists, that it is of type int, and that a variable y of type int also exists. (The next section discusses vari_ able types.) You can declare variables (almost) anywhere you want in your program — as long as you declare the variable before you use it.
Declaring Different Types of Variables
If youÂ’re on friendly terms with math (hey, arenÂ’t we all?), you probably think of a variable in mathematics as an amorphous box capable of holding what_ ever you might choose to store in it. You might easily write something like the following:
x = 1
x = 2.3
x = “this is a sentence”
x = Texas
Alas, C++ is not that flexible. (On the other hand, C++ can do things that people canÂ’t do, such as add a billion numbers or so in a second, so letÂ’s not get too uppity.) To C++, there are different types of variables just as there are different types of storage bins. Some storage bins are so small that they can only handle a single number. It takes a larger bin to handle a sentence. Of course, no bin is large enough to hold Texas (maybe Rhode Island or Delaware).
You have to tell C++ what size bin you need before you can use a C++ variable. In addition, different types of variables have different properties. So far, you have only seen the int type of variable in this chapter:
Chapter 2: Declaring Variables Constantly 29
int x;
x = 1;
The variable type int is the C++ equivalent of an integer — a number that has no fractional part. (Integers are also known as counting numbers or whole numbers.)
Integers are great for most calculations. You can make it up through most (if not all) of elementary school with integers. It isnÂ’t until you reach age 11 or so that they start mucking up the waters with fractions. The same is true in C++: More than 90 percent of all variables in C++ are declared to be of type int.
Unfortunately, int variables don’t always work properly in a program. If (for example) you worked through the temperature-conversion program in Chap_ ter 1, the program has a potential problem — it can only handle integer tem_ peratures — whole numbers that don’t have a fractional part. This limitation of using only integers doesn’t affect daily use because it isn’t likely that some_ one (other than a meteorologist) would get all excited about entering a frac_ tional temperature (such as 10.5 degrees). The lurking problem is not at all obvious: The conversion program lops off the fractional portion of tempera_ tures that it calculates, and just keeps going without complaint. This can result in a lapse of accuracy that can be serious — for example, you wouldn’t want to come up a half mile short of the runway on your next airplane trip due to a navigational round-off.
Reviewing the limitations of integers in C++
The int variable type is the C++ version of an integer. int variables suffer the same limitations as their counting-number integer equivalents in math do.
Integer round-off
Consider the problem of calculating the average of three numbers. Given three int variables — nValue1, nValue2, and nValue3 — an equation for calculat_ ing the average is
int nAverage; int nValue1; int nValue2; int nValue3;
nAverage =(nValue1 + nValue2 + nValue3) / 3;
Because all three values are integers, the sum is assumed to be an integer. Given the values 1, 2, and 2, the sum is 5. Divide that by 3, and you get 12â„3, or
1.666. Given that all three variables nValue1, nValue2, and nValue3 are inte_ gers, the sum is also assumed to be an integer. The result of the division is also an integer. The resulting value of nAverage is unreasonable but logical: 1.
30 Part I: Introduction to C++ Programming Lopping off the fractional part of a number is called truncation, or rounding off. For many applications, truncation isnÂ’t a big deal. Some folks might con_ sider its results reasonable (not mathematicians or bookies, of course), but integer truncation can create math mayhem in computer programs. Consider the following equivalent formulation:
int nAverage; int nValue1; int nValue2; int nValue3;
nAverage = nValue1/3 + nValue2/3 + nValue3/3;
Plugging in the same 1, 2, and 2 values, the resulting value of nAverage is (talk about logical-but-unreasonable) 0. To see how this can occur, consider that
13 truncates to 0, 23 truncates to 0, and 23 truncates to 0. The sum of 0, 0, and
0 is zero. (Sort of like that old song: “Nothing from nothing leaves nothing, ya gotta be something . . .”) You can see that integer truncation can be completely unacceptable.
Limited range
A second problem with the int variable type is its limited range. A normal int variable can store a maximum value of 2,147,483,647 and a minimum value of –2,147,483,648 — roughly from positive 2 billion to negative 2 billion, for a total range of about 4 billion.
Two billion is a very large number: plenty big enough for most uses. But it’s not large enough for some applications — for example, computer technology. In fact, your computer probably executes faster than 2 gigahertz, depending upon how old your computer is. (Giga is the prefix meaning billion.) A single strand of communications fiber — the kind that’s been strung from one end of the country to the other — can handle way more than 2 billion bits per second.
C++ offers a little help by allowing you declare an integer to be unsigned, mean_ ing that it cannot be negative. An unsigned int value type can represent a number from 0 to 4,294,967,295, should the need arise for some unimaginable reason.
You can declare a variable simply unsigned. The int is implied.
Solving the truncation problem
The limitations of int variables can be unacceptable in some applications. Fortunately, C++ understands decimal numbers. A decimal number can have a nonzero fractional part. (Mathematicians also call those real numbers.) Decimal numbers avoid many of the limitations of int type integers. Notice that a decimal number “can have” a nonzero fractional part. In C++, the
number 1.0 is just as much a decimal number as 1.5. The equivalent integer is written simply as 1. Decimals numbers can also be negative, like –2.3.
Chapter 2: Declaring Variables Constantly 31
When you declare variables in C++ that are decimal numbers, you identify them as double precision floating-point values. (Yes, there is such a critter as a
“single precision floating-point variable,” but stick with me here.) The term floating-point means the decimal point is allowed to float back and forth, iden_ tifying as many “decimal places” as necessary to express the value. Floating- point variables are declared in the same way as int variables:
double dValue1;
From this point forward, the variable dValue1 is declared to be a double. Once declared, you cannot change the type of a variable. dValue1 is now a double and will be a double for the remainder of its natural instructions. To see how floating-point numbers fix the truncation problem inherent with integers, con_ vert all the int variables to double. HereÂ’s what you get:
double dValue;
dValue = 1.0/3.0 + 2.0/3.0 + 2.0/3.0;
is equivalent to
dValue = 0.333... + 0.666... + 0.666...;
which results in the value
dValue = 1.666...;
I have written the value 1.6666 . . . as if the number of trailing 6s goes on forever. This is (not necessarily) the case. There’s a limit to the number of digits of accuracy of a double variable — but it’s a lot more than I can keep track of.
The programs IntAverage and FloatAverage are available on the enclosed CD in the CPP_Programs\Chap02 directory to demonstrate this averaging example.
Looking at the limits of floating-point numbers
Although floating-point variables can solve many calculation problems such as truncation, they have some limitations themselves — in effect, the reverse of those associated with integer variables. double variables can’t be used as counting numbers, they’re more difficult for the computer to handle, and they also suffer from round-off error (though not nearly to the same degree as int variables).
32 Part I: Introduction to C++ Programming
Counting
You cannot use floating-point variables in applications where counting is impor_ tant. This includes C++ constructs, which requires counting ability. C++ canÂ’t verify which whole number value is meant by a given floating-point number.
For example, itÂ’s clear that 1.0 is 1. But what about 0.9 or 1.1? Should these also be considered as 1? C++ simply avoids the problem by insisting on using int values when counting is involved.
Calculation speed
Historically, a computer processor can process integer arithmetic quicker than it can floating-point arithmetic. Thus, while a processor can add 1 million inte_ ger numbers in a given amount of time, the same processor may be able to perform only 200,000 floating-point calculations during the same period. (Not surprisingly, I couldnÂ’t even get around to reading the first value.)
Calculation speed is becoming less of a problem as microprocessors increase their capabilities. Most modern processors contain special calculation cir_ cuitry for performing floating-point calculations almost as fast as integer calculations.
Loss of accuracy
Floating-point variables cannot solve all computational problems. Floating- point variables have a limited precision of about 6 digits — an extra-economy size, double-strength version of float can handle some 15 significant digits with room left over for lunch.
To evaluate the problem, consider that 13 is expressed as 0.333 . . . in a con_ tinuing sequence. The concept of an infinite series makes sense in math, but not to a computer. The computer has a finite accuracy. Average 1, 2, and 2
(for example), and you get 1.666667.
C++ can correct for many forms of round-off error. For example, in output, C++ can determine that instead of 0.999999, that the user really meant 1. In other cases, even C++ cannot correct for round-off error.
Not-so-limited range
Although the double data type has a range much larger than that of an inte_ ger, itÂ’s still limited. The maximum value for an int is a skosh more than 2 bil_ lion. The maximum value of a double variable is roughly 10 to the 38th power. ThatÂ’s 1 followed by 38 zeroes; it eats 2 billion for breakfast. (ItÂ’s even more than the national debt, at least at the time of this writing.)
Only the first 13 digits or so have any meaning; the remaining 25 digits suffer from floating-point round-off error.
Chapter 2: Declaring Variables Constantly 33
Declaring Variable Types
So far this chapter has been trumpeting that variables must be declared and that they must be assigned a type. Fortunately (ta-dah!), C++ provides a num_ ber of different variable types. See Table 2-1 for a list of variables, their advan_ tages, and limitations.
Table 2-1 C++ Variables
Variable Example Purpose
int 1 A simple counting number, either positive or negative.
unsigned int 1U A counting number thatÂ’s only non-negative. long 10L A potentially larger version of int. There is no difference between long and int with
Dev-C++ and Microsoft Visual C++.NET.
unsigned long 10UL A nonnegative long integer.
float 1.0F A single precision real number. This smaller version takes less memory than a double but has less accuracy and a smaller range.
double 1.0 A standard floating-point variable.
char ‘c’ A single char variable stores a single alpha_ betic or digital character. Not suitable for arithmetic.
string “this is A string of characters forms a sentence or
a string” phrase.
bool true The only other value is false. No I mean, it’s really false. Logically false. Not “false” as in fake or ersatz or . . . never mind.
It may seem odd that the standard floating length variable is called double while the “off size” is float. In days gone by, memory was an expensive asset — you could reap significant space savings by using a float variable. This is no longer the case. That, combined with the fact that modern processors perform double precision calculations at the same speed as float, makes the double the default. Bigger is better, after all.
34 Part I: Introduction to C++ Programming The following statement declares a variable lVariable as type long and sets it equal to the value 1, while dVariable is a double set to the value 1.0:
// declare a variable and set it to 1
long lVariable;
lVariable = 1;
// declare a variable of type double and set it to 1.0
double dVariable;
dVariable = 1.0;
You can declare a variable and initialize it in the same statement:
int nVariable = 1; // declare a variable and
// initialize it to 1
Although such declarations are common, the only benefit to initializing a vari_
able in the declaration is that it saves typing.
A char variable can hold a single character; a string (which isnÂ’t really a vari_
able but works like one for most purposes) holds a string of characters. Thus,
‘C’ is a char that contains the character C, whereas “C” is a string with one character in it. A rough analogy is that a ‘C’ corresponds to a nail in your hand, whereas “C” corresponds to a nail gun with one nail left in the magazine. (Chap_ ter 9 describes strings in detail.)
If an application requires a string, youÂ’ve gotta provide one, even if the string contains only a single character. Providing nothing but the character just wonÂ’t do the job.
Types of constants
A constant is an explicit number or character (such as 1, 0.5, or ‘c’) that doesn’t change. As with variables, every constant has a type. In an expression such as n = 1; (for example), the constant 1 is an int. To make 1 a long integer,
write the statement as n = 1L;. The analogy is as follows: 1 represents a single ball in the bed of a pickup truck, whereas 1L is a single ball in the bed of a dump truck. The ball is the same, but the capacity of its container is much larger.
Following the int to long comparison, 1.0 represents the value 1, but in a floating-point container. Notice, however, that the default for floating-point constants is double. Thus, 1.0 is a double number and not a float.
true is a constant of type bool. However, “true” (note the quotation marks) is a string of characters that spell out the word true. In addition, in keeping with C++’s attention to case, true is a constant, but TRUE has no meaning.
Chapter 2: Declaring Variables Constantly 35
Special characters
You can store any printable character you want in a char or string vari_ able. You can also store a set of non-printable characters that is used as character constants. See Table 2-2 for a description of these important non- printable characters.
Table 2-2 Special Characters
Character Constant Action
‘\n’ newline
‘\t’ tab
‘\0’ null
‘\\’ backslash
You have already seen the newline character at the end of strings. This char_ acter breaks a string and puts the parts on separate lines. A newline charac_ ter may appear anywhere within a string. For example,
“This is line 1\nThis is line 2”
appears on the output as
This is line 1
This is line 2
Similarly, the \t tab character moves output to the next tab position. (This position can vary, depending on the type of computer youÂ’re using to run the program.) Because the backslash character is used to signify special charac_ ters, a character pair for the backslash itself is required. The character pair
\\ represents the backslash.
C++ collision with file names
Windows uses the backslash character to sep_ arate folder names in the path to a file. (This is a remnant of MS-DOS that Windows has not been able to shake.) Thus, Root\FolderA\ File represents File within FolderA, which is a subdirectory of Root.
Unfortunately, MS-DOSÂ’s use of backslash con_
flicts with the use of backslash to indicate an escape character in C++. The character \\ is a backslash in C++. The MS-DOS path Root\ FolderA\File is represented in C++ string as Root\\FolderA\\File.
36 Part I: Introduction to C++ Programming
Are These Calculations Really Logical?
C++ provides a logical variable called bool. The type bool comes from Boolean, the last name of the inventor of the logical calculus. There are two values for a boolean variable: true and false.
There are actually calculations that result in the value bool. For example, “x is equal to y” is either true or false.
Mixed Mode Expressions
C++ allows you to mix variable types in a single expression. That is, you are allowed to add an integer with a double precision floating-point value. In the following expression, for example, nValue1 is allowed to be an int:
// in the following expression the value of nValue1
// is converted into a double before performing the
// assignment int nValue1 = 1;
nValue1 + 1.0;
An expression in which the two operands are not the same type is called a mixed-mode expression. Mixed-mode expressions generate a value whose type is equal to the more capable of the two operands. In this case, nValue1 is con_ verted to a double before the calculation proceeds. Similarly, an expression of one type may be assigned to a variable of a different type, as in the following statement:
// in the following assignment, the whole
// number part of fVariable is stored into nVariable double dVariable = 1.0;
int nVariable;
nVariable = dVariable;
You can lose precision or range if the variable on the left side of the assignment is smaller. In the previous example, C++ truncates the value of dVariable
before storing it in nVariable.
Converting a larger value type into a smaller value type is called demotion, whereas converting values in the opposite direction is known as promotion. Programmers say that the value of int variable nVariable1 is promoted to a double as expressions such as the following:
int nVariable1 = 1;
double dVariable = nVariable1;
Chapter 2: Declaring Variables Constantly 37
Naming conventions
You may have noticed that the name of each
variable begins with a special character that seems to have nothing to do with the name. These special characters are shown in the fol_
lowing table. You can immediately recognize
dVariable as a variable of type double by using this convention.
These leading characters help the programmer keep track of the variable type. Thus, you can immediately identify the following as a mixed- mode assignment of a long variable to an int variable:
nVariable = lVariable;
These leading characters have no significance
Character
Type to C++. You donÂ’t need to adopt any naming scheme at all if you donÂ’t want to. HereÂ’s what
n int
l long
f float
d double
c character
sz string
you get:
double myVariable;
int someIntValue;
double nThisDoesntEvenMatch;
I used this first-letter-naming convention in this
chapter to simplify the discussion; many pro_
grammers use this naming scheme all the time.
Mixed-mode expressions are not a good idea. Avoid forcing C++ to do your conversions for you.
38 Part I: Introduction to C++ Programming
Chapter 3
Performing Mathematical
Operations
In This Chapter
Defining mathematical operators in C++ Using the C++ mathematical operators Identifying expressions
Increasing clarity with special mathematical operators
A mathematician uses more than just the variables described in Chap_
ter 2. A mathematician must do something with those variables: She can add them together, subtract them, multiply them, and perform an almost end_ less list of other operations.
C++ offers the same set of basic operations: C++ programs can multiply, add, divide, and so forth. Programs have to be able to perform these operations in order to get anything done. What good is an insurance program if it canÂ’t cal_ culate how much youÂ’re supposed to (over) pay?
C++ operations look like the arithmetic operations you would perform on a piece of paper, except you have to declare any variables before you can use them (as detailed in Chapter 2):
int var1;
int var2 = 1;
var1 = 2 * var2;
Two variables, var1 and var2, are declared. var2 is initialized to 1. var1 is assigned the value resulting from the calculation two times the value of var2.
This chapter describes the complete set of C++ mathematical operators.
40 Part I: Introduction to C++ Programming
Performing Simple Binary Arithmetic
A binary operator is one that has two arguments. If you can say var1 op var2, op must be a binary operator. The most common binary operators are the simple operations you performed in grade school. The binary operators are flagged in Table 3-1.
Table 3-1 Mathematical Operators in Order of Precedence
Precedence Operator Meaning
1 + (unary) Effectively does nothing
1 - (unary) Returns the negative of its argument
2 ++ (unary) Increment
2 -- (unary) Decrement
3 * (binary) Multiplication
3 / (binary) Division
3 % (binary) Modulo
4 + (binary) Addition
4 - (binary) Subtraction
5 =, *=,%=,+=,-= (special) Assignment types
Multiplication, division, modulus, addition, and subtraction are the operators used to perform arithmetic. In practice, they work just like the familiar arith_ metic operations as well. For example, using the binary operator for division with a float variable looks like this:
float var = 133 / 12;
Each of the binary operators has the conventional meaning that you studied in grammar school — with one exception. You may not have encountered modulus in your studies.
The modulus operator (%) works much like division, except it produces the remainder after division instead of the quotient. For example, 4 goes into 15 three times with a remainder of 3. Expressed in C++ terms, 15 modulus 4 is 3.
int var = 15 % 4; // var is initialized to 3
Chapter 3: Performing Mathematical Operations 41
Because programmers are always trying to impress nonprogrammers with the simplest things, C++ programmers define modulus as follows:
IntValue % IntDivisor
This expression is equal to
IntValue - (IntValue / IntDivisor) * IntDivisor
Try it out on this example:
15 % 4 is equal to 15 - (15/4) * 4
15 - 3 * 4
15 - 12
3
Modulus is not defined for floating-point variable because it depends on the
round-off error inherent in integers. (I discuss round-off errors in Chapter 2.)
Decomposing Expressions
The most common type of statement in C++ is the expression. An expression is a C++ statement with a value. Every expression has a type (such as int, double, char, and so on). A statement involving any mathematical operator is an expression since all these operators return a value. For example, 1 + 2 is an expression whose value is 3 and type is int. (Remember that constants without decimal points are ints.)
Expressions can be complex or extremely simple. In fact, the statement 1 is an expression because it has a value (1) and a type (int). There are five expres_ sions in the following statement:
z = x * y + w;
The expressions are
x * y + w x * y
x y
w
42 Part I: Introduction to C++ Programming An unusual aspect of C++ is that an expression is a complete statement. Thus, the following is a legal C++ statement:
1;
The type of the expression 1 is int.
Determining the Order of Operations
All operators perform some defined function. In addition, every operator has a precedence — a specified place in the order in which the expressions are evaluated. Consider, for example, how precedence affects solving the follow_ ing problem:
int var = 2 * 3 + 1;
If the addition is performed before the multiplication, the value of the expres_ sion is 2 times 4 or 8. If the multiplication is performed first, the value is 6 + 1 or 7.
The precedence of the operators determines who goes first. Table 3-1 shows that multiplication has higher precedence than addition, so the result is 7.
(The concept of precedence is also present in arithmetic. C++ adheres to the common arithmetic precedence.)
So what happens when we use two operators of the same precedence in the same expression? Well, it looks like this:
int var = 8 / 4 / 2;
But is this 8 divided by 2 or 4, or is it 2 divided by 2 or 1? When operators of the same precedence appear in the same expression, they are evaluated from left to right (the same rule applied in arithmetic). Thus, the answer is 8 divided by 4, which is 2 divided by 2 (which is 1).
The expression
x / 100 + 32
divides x by 100 before adding 32. But what if the programmer wanted to divide x by 100 plus 32? The programmer can change the precedence by bundling expressions together in parentheses (shades of algebra!), as follows:
x/(100 + 32)
Chapter 3: Performing Mathematical Operations 43
This expression has the same effect as dividing x by 132.
The original expression
x / 100 + 32
is identical to the expression
(x/100) + 32
In a given expression, C++ normally performs multiplication and division before addition or subtraction. Multiplication and division have higher precedence than addition and subtraction.
In summary: Precedence refers to the order in which operators are evaluated. An operator with higher precedence is executed first. You can override the precedence of an operator by using parentheses.
Performing Unary Operations
Arithmetic binary operators — those operators that take two arguments — are familiar to a lot of us from school days. You’ve probably been doing binary operations since the first grade in school. But consider the unary operators, which take a single argument (for example, –a). Many unary operations are not so well known.
The unary mathematical operators are plus, plus-plus, minus, and minus-minus
(respectively, +, –, ++, and – –). Thus
int var1 = 10;
int var2 = -var1;
The latter expression uses the minus unary operator (–) to calculate the value negative 10.
The minus operator changes the sign of its argument. Positive numbers become negative and vice versa. The plus operator does not change the sign of its argument. It wouldnÂ’t be weird to say the plus operator has no effect at all.
The ++ and the – – operators might be new to you. These operators (respec_ tively) add one to their arguments or subtract one from their arguments, so they’re known (also respectively) as the increment and decrement operators.
44 Part I: Introduction to C++ Programming Because theyÂ’re dependent upon numbers that can be counted, theyÂ’re lim_ ited to non-floating-point variables. For example, the value of var after exe_ cuting the following expression is 11.
int var = 10; // initalize var var++; // now increment it
// value of var is now 11
The increment and decrement operators are peculiar in that both come in two flavors: a prefix version and a postfix version (known as pre-increment and post-increment, respectively). Consider, for example, the increment operator
(the decrement works in exactly the same way).
Suppose that the variable n has the value 5. Both ++n and n++ increment n to the value 6. The difference between the two is that the value of ++n in an expression is 6 while the value of n++ is 5. The following example illustrates this difference:
// declare three integer variables int n1, n2, n3;
// the value of both n1 and n2 is 6
n1 = 5;
n2 = ++n1;
// the value of n1 is 6 but the value of n3 is 5
n1 = 5;
n3 = n1++;
Thus n2 is given the value of n1 after n1 has been incremented (using the pre-increment operator), whereas n3 gets the value of n1 before it is incre_ mented using the post-increment operator.
Why define a separate increment operator?
The authors of C++ noted that programmers add
1 more than any other constant. To provide
some convenience, a special add 1 instruction was added to the language.
In addition, most present-day computer proces_
sors have an increment instruction that is faster
than the addition instruction. Back when C++ was created, however — with microprocessors being what they were — saving a few instruc_ tions was a big deal.
Chapter 3: Performing Mathematical Operations 45
Using Assignment Operators
An assignment operator is a binary operator that changes the value of its left argument. The equal sign (=), a simple assignment operator, is an absolute necessity in any programming language. This operator puts the value of the right-hand argument into the left argument. The other assignment operators are odd enough that they seem to be someoneÂ’s whim.
The creators of C++ noticed that assignments often follow the form of
variable = variable # constant
where # is some binary operator. Thus, to increment an integer operator by
2, the programmer might write
nVariable = nVariable + 2;
This expression says, “add two to the value of nVariable and store the results back into nVariable.” Doing so changes the value of nVariable to 2 more than it was.
ItÂ’s common to see the same variable on both the right and left side of an assignment.
Because the same variable appears on both sides of the = sign, the same Fathers of the C++ Republic decided to create a version of the assignment operator in which a binary operator is attached. This says, in effect, “Thou shalt perform whatever operation on a variable and store the results right back into the same variable.”
Every binary operator has one of these nifty assignment versions. Thus, the assignment just given could have been written this way:
nVariable = nVariable + 2;
nVariable += 2;
Here the first line says (being very explicit now) “Take the value of nVariable, add 2, and store the results back into nVariable.” The line is a second form if the same expression, saying (a bit more abruptly), “Add 2 to the value of nVariable.”
Other than assignment itself, these assignment operators are not used all that often. However, as odd as they might look, sometimes they can actually make the resulting program easier to read.
46 Part I: Introduction to C++ Programming
Chapter 4
Performing Logical Operations
In This Chapter
Using sometimes-illogical logical operators
Defining logical variables
Operating with bitwise logical operators logically, a bit at a time
The most common statement in C++ is the expression. Most expressions involve the arithmetic operators such as addition (+), subtraction (–) and
multiplication (*). This chapter describes these types of expressions.
There is a whole other class of operators known as the logical operators. In comparison with the arithmetic operators, most people donÂ’t think nearly as much about operations.
It isn’t that people don’t deal with logical operations — after all, people compute AND and OR constantly. I won’t eat cereal unless the bowl contains cereal AND the bowl has milk in it AND the cereal is coated with sugar (lots of sugar). I’ll have a Scotch IF it’s single-malt AND someone else paid for it. People use such logical operations all the time, it’s just that they don’t write them down as machine instructions (or think of them in that light).
Logical operators fall into two types. The AND and OR operators are what I will call simple logical operators. There is a second type of logical operator that people don’t use in their daily business — the bitwise operator — that’s unique to the computer world. We’ll start with the simple and sneak up on the bitwise here.
Why Mess with Logical Operations?
C++ programs have to make decisions. A program that can’t make decisions is of limited use. The temperature-conversion program laid out in Chapter 1 is about as complex you can get without some type of decision-making. Invariably a computer program gets to the point where it has to figure out situations such as “Do this if the a variable is less than some value, do that other thing if it’s not.” That’s what makes a computer appear to be intelligent — that it can make
48 Part I: Introduction to C++ Programming decisions. (By the same token, that same property makes a computer look really stupid when the program makes the wrong decision.) Making deci_ sions, right or wrong, requires the use of logical operators.
Using the Simple Logical Operators
The simple logical operators, shown in Table 4-1, evaluate to true or false. Table 4-1 Simple Operators Representing Daily Logic Operator Meaning
== Equality; true if the left-hand argument has the same value as the right
!= Inequality; opposite of equality
>, < Greater than, less than; true if the left-hand argument is greater than or less than the right-hand argument
>=, <= Greater than or equal to, less than or equal to; true if either > or
== is true, OR either < or == is true
&& AND; true if both the left-and right-hand arguments are true
|| OR; true if either the left-or the right-hand argument is true
! NOT; true if its argument is false
The first six entries in Table 4-1 are comparison operators. The equality opera_ tor is used to compare two numbers. For example, the following is true if the value of n is 0, and is false otherwise:
n == 0;
Looks can be deceiving. Don’t confuse the equality operator (==) with the assignment operator (=). Not only is this a common mistake, but it’s a mis_ take that the C++ compiler generally cannot catch — that makes it more than twice as bad.
n = 0; // programmer meant to say n == 0
The greater-than (>) and less-than (<) operators are similarly common in everyday life. The following expression logical comparison is true:
Chapter 4: Performing Logical Operations 49
int n1 = 1;
int n2 = 2;
n1 < n2;
It’s easy to forget which operator is “greater than” and which is “less than.” Just remember that the operator is true if the arrow points to the smaller of the two.
You may think that n1 is greater than or less than n2; however, this ignores the possibility that n1 and n2 are equal. The greater-than-or-equal-to opera_ tor (<=) and the less-than-or-equal-to operator (>=) include that bit of mathe_ matical nuance. They are similar to the less-than and greater-than operators, with one major exception: They include equality; the other operators donÂ’t.
The && (AND) and || (OR) can combine with the other logic operators, like this:
// true if n2 is greater than n1 but n2 smaller than n3
// (this is the most common way determining that n2 is in
// the range of n1 to n3, exclusive)
(n1 < n2) && (n2 < n3);
Storing logical values
The result of a logical operation can be assigned to a variable of type bool:
int n1 = 1; int n2 = 2; bool b;
b = (n1 == n2);
This expression highlights the difference between the assignment operator = and the comparison operator ==. The expression says, “Compare the vari_ ables n1 and n2. Store the results of this comparison in the variable b.”
The assignment operators are about as low down on the precedence totem pole as you can get. The equality operator is executed before the assignment. The parentheses are not required — so the following is an equally valid form
of logical confusion:
b = n1 == n2; // compare n1 with n2; generate a true if n1
// if n1 has the same value as n2, false if not
// store the result, true or false, in b
Whoa. Better look at that again. Note the difference between the operators.
50 Part I: Introduction to C++ Programming
The following program demonstrates the use of a bool variable:
// BoolTest - compare variables input from the
// keyboard and store the results off
// into a logical variable
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// set output format for bool variables
// to true and false instead
// of 1 and 0
cout.setf(cout.boolalpha);
// initialize two arguments int nArg1;
cout << “Input value 1: “;
cin >> nArg1;
int nArg2;
cout << “Input value 2: “;
cin >> nArg2;
bool b;
b = nArg1 == nArg2;
cout << “The statement, “ << nArg1
<< “ equals “ << nArg2
<< “ is “ << b
<< endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
The first line cout.setf() makes sure that our bool variable b is output as
“true” or “false”. The next section explains why this is necessary.
The program inputs two values from the keyboard and displays the result of the equality comparison:
Input value 1: 5
Input value 2: 5
The statement, 5 equals 5 is true
Press any key to continue . . .
Chapter 4: Performing Logical Operations 51
The special value endl inserts a newline. The difference between the value endl and the character ‘\n’ as described in Chapter 2 is subtle and explained in Chapter 24.
Using logical int variables
C++ hasn’t always had a bool type variable. Back in the old days (before that guy on TV kept walking around saying “Can you hear me now?”), C++ used int variables to store logical values. A value of 0 was considered false and all other values true. By the same token, a logical operator generated a 0 for false and a 1 for true. (Thus, 0 was false while 10 > 5 returned a 1.)
C++ retains a high degree of compatibility between bool and int in order to support the older programs that still have that quirk. You get completely different output from the BitTest program if you remove the line cout. setf(cout.boolalpha):
Input value 1: 5
Input value 2: 5
The statement, 5 equals 5 is 1
Press any key to continue . . .
Variables of type int and bool can be mixed in expressions as well. For exam_
ple, Dev-C++ allows the following bizarre statement without batting an eyelid:
int n;
n = nArg1 == nArg2;
Continue to use type bool to hold logical values despite this wart that modern
C++ inherits from its forefathers. Other compilers may not be as forgiving. Be careful performing logical operations on floating-point variables
Real numbers are those numbers that can have a fractional part. Because of this, real numbers cannot be counting numbers. That is, you can say the first
(1st), second (2nd), third, fourth, and so on because the relationship of 1, 2, and 3 are known exactly. It does not make sense to speak of the 4.5th number in a sequence. (This brings to mind the number between the fourth and fifth, but it has no real meaning.)
Similarly the C++ type float, which is the C++ representation, is not a count_ ing number. Even worse (unlike a real number), a floating-point number canÂ’t have an infinite number of digits beyond the decimal point if a computer is
52 Part I: Introduction to C++ Programming going to make any use of it. Because of this limitation, be careful when you use comparison operators on floating-point numbers. Consider the following example:
float f1 = 10.0;
float f2 = f1 / 3;
f1 == (f2 * 3.0); // are these two equal?
The comparison in the preceding example is not necessarily true. A floating- point variable cannot hold an unlimited number of significant digits. Thus, f2 is not equal to the number we’d call “three-and-a-third,” but rather to 3.3333..., stopping after some number of decimal places.
A float variable supports about 6 digits of accuracy while a double sup_ ports 13 digits. I say “about” because the computer is likely to generate a number like 3.3333347 due to vagaries in floating point calculations.
Now, in pure math, the number of threes after the decimal point is infinite — but no computer built can handle infinity. So, after multiplying 3.3333 by 3, you get 9.9999 instead of the 10 you’d get if you multiplied “three-and-a-third” —
in effect, a round-off error. Such small differences may be unnoticeable to a person, but not to the computer. Equality means exactly that — exact equality.
Modern processors are very sophisticated in performing such calculations. The processor may, in fact, accommodate the round-off error, but from inside C++, you canÂ’t predict exactly what any given processor will do.
Problems can arise even in a straightforward calculation, such as the following:
float f1 = 10.0;
float f2 = 100 % 30;
f1 == f2; // are these two equal?
Theoretically, f1 and f2 should be equal (after you apply that percentlike operator that Chapter 3 identifies as modulus). There doesn’t appear to be any problem with round off. So far. But you can’t be sure — you have no idea how the computer that eventually runs your program is going to represent floating-point numbers internally. To flatly claim that there’s no round-off error lurking here makes unwarranted assumptions about CPU internals.
The safer comparison is as follows:
float f1 = 10.0; float f2 = f1 / 3; float f3 = f2 * 3.0;
(f1 - f3) < 0.0001 && (f3 - f1) < 0.0001;
Chapter 4: Performing Logical Operations 53
This comparison is true if f1 and f3 are within some small delta from each other, which should still be true even if you take some small round-off error into account.
Short circuits and C++
The& & and || perform what is called short-circuit evaluation. Consider the following:
condition1 && condition2
If condition1 is not true, the result is not true, no matter what the value of condition2. (For example, condition2 could be true or false without changing the result.) The same situation occurs in the following:
condition1 || condition2
If condition1 is true, the result is true, no matter what the value of
condition2.
To save time, C++ (wisely) cuts to the chase and evaluates condition1 first. C++ does not evaluate condition2 if condition1 is false (in the case of &
&) or condition1 is true (in the case of ||). This is known as short circuit evaluation.
Expressing Binary Numbers
C++ variables are stored internally as so-called binary numbers. Binary num_ bers are stored as a sequence of 1 and 0 values known as bits. Most of the time, you don’t really need to deal with which particular bits you use to rep_ resent numbers. Sometimes, however, it’s actually practical and convenient to tinker with numbers at the bit level — so C++ provides a set of operators for that purpose.
Fortunately, you wonÂ’t have to deal too often with C++ variables at the bit level, so itÂ’s pretty safe to consider the remainder of this chapter a Deep Techie excursion.
The so-called bitwise logical operators operate on their arguments at the bit level. To understand how they work, letÂ’s first examine how computers store variables.
54 Part I: Introduction to C++ Programming
The decimal number system
The numbers we’ve been familiar with from the time we could first count on our fingers are known as decimal numbers because they’re based on the number 10. (Coincidence? I don’t think so . . .) In general, the programmer expresses C++ variables as decimal numbers. Thus you could specify the value of var as (say) 123 — but consider the implications.
A number such as 123 refers to 1 * 100 + 2 * 10 + 3 * 1. Each of these base numbers — 100, 10, and 1 — are powers of 10.
123 = 1 * 100 + 2 * 10 + 3 * 1
Expressed in a slightly different (but equivalent) way, 123 looks like this:
123 = 1 * 102 + 2 * 101 + 3 * 100
Remember that any number to the zero power is 1.
Other number systems
Well, okay, using 10 as the basis (or base) of our counting system probably stems from those 10 human fingers, the original counting tools. An alterna_ tive base for a counting system could just as easily have been 20 (maybe the inventor of base 10 had shoes on at the time).
If our numbering scheme had been invented by dogs, it might well be based on
8 (one digit of each paw is out of sight on the back part of the leg). Mathemat_
ically, such an octal system would have worked just as well:
12310 = 1 * 8
2 + 7 * 81 + 3 * 80 = 173
8
The small 10 and 8 here refer to the numbering system, 10 for decimal
(base 10) and 8 for octal (base 8). A counting system may use any positive base.
The binary number system
Computers have essentially two fingers. (Maybe thatÂ’s why computers are so stupid: without an opposing thumb, they canÂ’t grasp anything. And then again, maybe not.) Computers prefer counting using base 2. The number 12310 would be expressed this way:
Chapter 4: Performing Logical Operations 55
12310 = 0*128 + 1*64 + 1*32 + 1*16 + 1*8 + 0*4 +1*2 + 1*1
= 011110112
Computer convention expresses binary numbers by using 4, 8, 16, 32 or even
64 binary digits even if the leading digits are zero. This is also because of the way computers are built internally.
Because the term digit refers to a multiple of ten, a binary digit is called a bit
(an abbreviation of binary digit). Eight bits make up a byte. (Calling a binary digit a byte-it didnÂ’t seem like a good idea.) A short word is two bytes; a long word is four bytes.
With such a small base, you have to use a large number of bits to express numbers. Human beings donÂ’t want the hassle of using an expression such as
011110112 to express such a mundane value as 12310. Programmers prefer to express numbers by using an even number of bits. The octal system — which is based on 3 bits — has been almost completely replaced by the hexadeci_ mal system, which is based on 4-bit digits.
Hexadecimal uses the same digits for the numbers 0 through 9. For the digits between 9 and 16, hexadecimal uses the first six letters of the alphabet: A for
10, B for 11, etc. Thus, 12310 becomes 7B16, like this:
123 = 7 * 161 + B (i.e. 11) * 160 = 7B
16
Programmers prefer to express hexadecimal numbers in 2, 4, or 8 hexadeci_
mal digits even when the leading digit in each case is 0.
Finally, who wants to express a hexadecimal number such as 7B16 by using a subscript? Terminals donÂ’t even support subscripts. Even on a word proces_ sor such as the one IÂ’m using now, itÂ’s a drag to change fonts to and from sub_ script mode just to type two lousy digits. Therefore, programmers (no fools they) use the convention of beginning a hexadecimal number with a 0x. (Why? Well, the reason for such a strange convention goes back to the early days of C, in a galaxy far, far, away . . . never mind.) Thus, 7B becomes 0x7B. Using this convention, the hexadecimal number 0x7B is equal to 123 decimal while
0x123 hexadecimal is equal to 291 decimal.
You can use all the mathematical operators on hexadecimal numbers, in the same way youÂ’d apply them to decimal numbers. (Well, okay, most of us canÂ’t perform a multiplication such as 0xC * 0xE in our heads, but that has more to do with the multiplication tables we learned in school than it has to do with any limitation in the number system.)
56 Part I: Introduction to C++ Programming
Roman numeral expressions
On a historical note, I should mention that some numbering systems actually hinder computa_ tions. The Roman numeral system is a (so to speak) classic example that greatly hindered the development of math.
Adding two Roman numerals isnÂ’t too difficult: XIX + XXVI = XLV
Think this one out:
a) IX + VI: The I after the V cancels out the I
before the X so the result is V carry the X.
b) X + XX: Plus the carry X is XXXX, which is expressed as XL.
Subtraction is only slightly more difficult.
Ah, but multiplying two Roman numerals all but
requires a bachelorÂ’s degree in mathematics.
(You end up with rules like X promotes the digits on the right by 1 letter so that X –* IV becomes XL.) Division practically required a Ph.D., and higher operations such as integration would have been completely impossible.
Love those Arabic numerals . . .
Performing Bitwise Logical Operations
All C++ numbers can be expressed in binary form. Binary numbers use only the digits 1 and 0 to represent a value. The following Table 4-2 defines the set of operations that work on numbers one bit at a time, hence the term bitwise operators.
Table 4-2 Bitwise Operators
Operator Function
~ NOT: Toggle each bit from 1 to 0 and from 0 to 1
& AND each bit of the left-hand argument with that on the right
| OR each bit of the left-hand argument with that on the right
^ XOR (exclusive OR) each bit of the left-hand argument with that on the right
Bitwise operations can potentially store a lot of information in a small amount of memory. There are a lot of traits in the world that have only two (or, at most, four) possibilities — that are either this way or that way. You are either married or you’re not (you might be divorced but you are still not currently
Chapter 4: Performing Logical Operations 57
married). You are either male or female (at least that’s what my driver’s license says). In C++, you can store each of these traits in a single bit — in this way, you can pack 32 separate properties into a single 32-bit int.
In addition, bit operations can be extremely fast. There is no performance penalty paid for that 32-to-1 saving.
Even though memory is cheap these days, itÂ’s not unlimited. Sometimes, when youÂ’re storing large amounts of data, this ability to pack a whole lot of properties into a single word is a big advantage.
The single bit operators
The bitwise operators — AND (&), OR (|) and NOT (~) — perform logic oper_ ations on single bits. If you consider 0 to be false and 1 to be true (it doesn’t have to be this way, but it’s a common convention), you can say things like the following for the NOT operator:
NOT 1 (true) is 0 (false)
NOT 0 (false) is 1 (true)
The AND operator is defined as following:
1 (true) AND 1 (true) is 1 (true)
1 (true) AND 0 (false) is 0 (false)
ItÂ’s a similar situation for the OR operator:
1 (true) OR 0 (false) is 1 (true)
0 (false) OR 0 (false) is 0 (false)
The definition of the AND operator appears in Table 4-3.
Table 4-3 Truth Table for the AND Operator
AND 1 0
1 1 0
0 0 0
You read this table as the column corresponding to the value of one of the arguments while the row corresponds to the other. Thus, 1 & 0 is 0. (Column 1
58 Part I: Introduction to C++ Programming
and row 0.) The only combination that returns anything other than 0 is 1 & 1.
(This is known as a truth table.)
Similarly, the truth table for the OR operator is shown in Table 4-4. Table 4-4 Truth Table for the OR Operator XOR 1 0
1 1 1
0 1 0
One other logical operation that is not so commonly used in day-to-day living is the OR ELSE operator commonly contracted to XOR. XOR is true if either argument is true but not if both are true. The truth table for XOR is shown in Table 4-5.
Table 4-5 Truth Table for the XOR Operator
XOR 1 0
1 0 1
0 1 0
Armed with these single bit operators, we can take on the C++ bitwise logical operations.
Using the bitwise operators
The bitwise operators operate on each bit separately.
The bitwise operators are used much like any other binary arithmetic operator. The NOT operator is the easiest to understand. To NOT a number is to NOT each bit that makes up that number (and to a programmer, that sentence makes perfect sense — honest). Consider this example:
~01102 (0x6)
10012 (0x9)
Thus we say that ~0x6 equals 0x9.
The following calculation demonstrates the & operator:
Chapter 4: Performing Logical Operations 59
01102
&
00112
00102
Beginning with the most significant bit, 0 AND 0 is 0. In the next bit, 1 AND 0
is 0. In bit 3, 1 AND 1 is 1. In the least significant bit, 0 AND 1 is 0.
The same calculation can be performed in hexadecimal by first converting the number in binary, performing the operation and then converting the result back.
0x6 01102
& &
0x3 00112
0x2 00102
In shorthand, we say that 0x6 & 0x3 equals 0x2.
(Try this test: What is the value of 0x6 | 0x3? Get this in 7 seconds, and you can give yourself 7 pats on the back.)
A simple test
The following program illustrates the bitwise operators in action. The pro_ gram initializes two variables and outputs the result of ANDing, ORing, and XORing them.
// BitTest - initialize two variables and output the
// results of applying the ~,& , | and ^
// operations
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// set output format to hexadecimal cout.setf(cout.hex);
// initialize two arguments int nArg1;
nArg1 = 0x1234;
int nArg2;
nArg2 = 0x00ff;
// now perform each operation in turn
// first the unary NOT operator
60 Part I: Introduction to C++ Programming
cout << “Arg1 = 0x” << nArg1 << “\n”; cout << “Arg2 = 0x” << nArg2 << “\n”; cout << “~nArg1 = 0x” << ~nArg1 << “\n”; cout << “~nArg2 = 0x” << ~nArg2 << “\n”;
// now the binary operators cout << “nArg1 & nArg2 = 0x”
<< (nArg1 & nArg2)
<< “\n”;
cout << “nArg1 | nArg2 = 0x”
<< (nArg1 | nArg2)
<< “\n”;
cout << “nArg1 ^ nArg2 = 0x”
<< (nArg1 ^ nArg2)
<< “\n”;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
The first expression in our program, cout.setf(ios::hex), sets the output format from the default decimal to hexadecimal (youÂ’ll have to trust me for now that it works).
The remainder of the program is straightforward. The program assigns nArg1 the test value 0x1234 and nArg2 the value 0x00ff. The program then out_ puts all combinations of bitwise calculations. The process looks like this:
Arg1 = 0x1234
Arg2 = 0xff
~nArg1 = 0xffffedcb
~nArg2 = 0xffffff00
nArg1 & nArg2 = 0x34 nArg1 | nArg2 = 0x12ff nArg1 ^ nArg2 = 0x12cb
Press any key to continue . . .
Do something logical with logical calculations
Running through simple and bitwise logical calculations in your head at par_ ties is fun (well, okay, for some of us), but a program has to make actual, prac_ tical use of these values to make them worth the trouble. Coming right up: Chapter 5 demonstrates how logical calculations are used to control program flow.
Chapter 5
Controlling Program Flow
In This Chapter
Controlling the flow through the program Executing a group of statements repetitively Avoiding infinite loops
The simple programs that appear in Chapters 1 through 4 process a fixed number of inputs, output the result of that calculation, and quit. However,
these programs lack any form of flow control. They cannot make tests of any sort. Computer programs are all about making decisions. If the user presses a key, the computer responds to the command.
For example, if the user presses Ctrl+C, the computer copies the currently selected area to the Clipboard. If the user moves the mouse, the pointer moves on the screen. If the user clicks the right mouse button with the Windows key depressed, the computer crashes. The list goes on and on. Programs that donÂ’t make decisions are necessarily pretty boring.
Flow-control commands allow the program to decide what action to take, based on the results of the C++ logical operations performed (see Chapter 4). There are basically three types of flow-control statements: the branch, the loop, and the switch.
Controlling Program Flow with the
Branch Commands
The simplest form of flow control is the branch statement. This instruction allows the program to decide which of two paths to take through C++ instruc_ tions, based on the results of a logical expression (see Chapter 4 for a descrip_ tion of logical expressions).
62 Part I: Introduction to C++ Programming
In C++, the branch statement is implemented using the if statement:
if (m > n)
{
}
else
{
}
// Path 1
// ...instructions to be executed if
// m is greater than n
// Path 2
// ...instructions to be executed if not
First, the logical expression m > n is evaluated. If the result of the expression is true, control passes down the path marked Path 1 in the previous snip_ pet. If the expression is false, control passes to Path 2. The else clause is optional. If it is not present, C++ acts as if it is present but empty.
Actually, the braces are optional (sort of) if there’s only one statement to exe_ cute as part of the if. If you lose the braces, however, it’s embarrassingly easy to make a mistake that the C++ compiler can’t catch. The braces serve as a guide marker; it’s much safer to include ’em. (If your friends try to entice you into not using braces, “Just say No.”)
The following program demonstrates the if statement (note all the lovely braces):
// BranchDemo - input two numbers. Go down one path of the
// program if the first argument is greater than
// the first or the other path if not
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// input the first argument... int arg1;
cout << “Enter arg1: “;
cin >> arg1;
// ...and the second int arg2;
cout << “Enter arg2: “;
cin >> arg2;
// now decide what to do:
if (arg1 > arg2)
Chapter 5: Controlling Program Flow 63
{
}
else
{
}
cout << “Argument 1 is greater than argument 2”
<< endl;
cout << “Argument 1 is not greater than argument 2”
<< endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
Here the program reads two integers from the keyboard and compares them. If the expression “arg1 is greater than arg2” is true, control flows to the output statement cout << “Argument 1 is greater than argument 2”. If arg1 is not greater than arg2, control flows to the else clause where the statement cout << “Argument 1 is not greater than argument 2\n” is executed. Here’s what that operation looks like:
Enter arg1: 5
Enter arg2: 6
Argument 1 is not greater than argument 2
Press any key to continue . . .
Executing Loops in a Program
Branch statements allow you to control the flow of a programÂ’s execution from one path of a program or another. This is a big improvement, but still not enough to write full-strength programs.
Consider the problem of updating the computer display. On the typical PC display, 1 million pixels are drawn to update the entire display. A program that canÂ’t execute the same code repetitively would need to include the same set of instructions over and over 1,000 times.
What we really need is a way for the computer to execute the same sequence of instructions thousands and millions of times. Executing the same command multiple times requires some type of looping statements.
64 Part I: Introduction to C++ Programming
Looping while a condition is true
The simplest form of looping statement is the while loop. HereÂ’s what the
while loop looks like:
while(condition)
{
// ... repeatedly executed as long as condition is true
}
The condition is tested. This condition could be if var > 10 or if var1 == var2 or anything else you might think of. If it is true, the state_
ments within the braces are executed. Upon encountering the closed brace, C++ returns control to the beginning, and the process starts over. The effect is that the C++ code within the braces is executed repeatedly as long as the condition is true. (Kind of reminds me of how I get to walk around the yard with my dog until she . . . well, until weÂ’re done.)
If the condition were true the first time, what would make it be false in the future? Consider the following example program:
// WhileDemo - input a loop count. Loop while
// outputting astring arg number of times.
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// input the loop count int loopCount;
cout << “Enter loopCount: “;
cin >> loopCount;
// now loop that many times while (loopCount > 0)
{
loopCount = loopCount - 1;
cout << “Only “ << loopCount << “ loops to go\n”;
}
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
WhileDemo begins by retrieving a loop count from the user, which it stores in the variable loopCount. The program then executes a while loop. The while first tests loopCount. If loopCount is greater than zero, the program enters
Chapter 5: Controlling Program Flow 65
the body of the loop (the body is the code between the braces) where it decre_ ments loopCount by 1 and outputs the result to the display. The program then returns to the top of the loop to test whether loopCount is still positive.
When executed, the program WhileDemo outputs the results shown in this next snippet. Here I entered a loop count of 5. The result is that the program loops 5 times, each time outputting a countdown.
Enter loopCount: 5
Only 4 loops to go Only 3 loops to go Only 2 loops to go Only 1 loops to go Only 0 loops to go
Press any key to continue . . .
If the user enters a negative loop count, the program skips the loop entirely. ThatÂ’s because the specified condition is never true, so control never enters the loop. In addition, if the user enters a very large number, the program loops for a long time before completing.
A separate, less frequently used version of the while loop known as the do . . . while appears identical except the condition isnÂ’t tested until the bottom of the loop:
do
{
// ...the inside of the loop
} while (condition);
Because the condition isnÂ’t tested until the end, the body of the do . . . while is always executed at least once.
The condition is only checked at the beginning of the while loop or at the end of the do . . . while loop. Even if the condition ceases to be true at some time during the execution of the loop, control does not exit the loop until the condition is retested.
Using the autoincrement/
autodecrement feature
Programmers very often use the autoincrement ++ or the autodecrement -- operators with loops that count something. Notice, from the following snip_ pet extracted from the WhileDemo example, that the program decrements the loop count by using assignment and subtraction statements, like this:
66 Part I: Introduction to C++ Programming
// now loop that many times while (loopCount > 0)
{
loopCount = loopCount - 1;
cout << “Only “ << loopCount << “ loops to go\n”;
}
A more compact version uses the autodecrement feature, which does what you may well imagine:
while (loopCount > 0)
{
loopCount--;
cout << “Only “ << loopCount << “ loops to go\n”;
}
The logic in this version is the same as in the original. The only difference is the way that loopCount is decremented.
Because the autodecrement both decrements its argument and returns its value, the decrement operation can actually be combined with the while loop. In particular, the following version is the smallest loop yet.
while (loopCount-- > 0)
{
cout << “Only “ << loopCount << “ loops to go\n”;
}
Believe it or not, the loopcount— > 0 is the version that most C++ program_ mers would use. It’s not that C++ programmers like being cute (although they do). In fact, the more compact version (which embeds the autoincrement or autodecrement feature in the logical comparison) is easier to read, especially as you gain experience.
Both loopCount— and —loopCount expressions decrement loopCount. The former expression, however, returns the value of loopCount before being decremented; the latter expression does so after being decremented.
How often should the autodecrement version of WhileDemo execute when the user enters a loop count of 1? If you use the pre-decrement version, the value of —loopCount is 0, and the body of the loop is never entered. With the post- decrement version, the value of loopCount— is 1, and control enters the loop.
Beware thinking that the version of the program with the autodecrement command executes faster (since it contains fewer statements). It probably executes exactly the same. Modern compilers are pretty good at getting the number of machine-language instructions down to a minimum, no matter which of the decrement instructions shown here you actually use
Chapter 5: Controlling Program Flow 67
Using the for loop
The most common form of loop is the for loop. The for loop is preferred over the more basic while loop because itÂ’s generally easier to read (thereÂ’s really no other advantage).
The for loop has the following format:
for (initialization; conditional; increment)
{
// ...body of the loop
}
Execution of the for loop begins with the initialization clause, which got its name because itÂ’s normally where counting variables are initialized. The ini_ tialization clause is only executed once when the for loop is first encountered.
Execution continues with the conditional clause. This clause works a lot like the while loop: as long as the conditional clause is true, the for loop con_ tinues to execute.
After the code in the body of the loop finishes executing, control passes to the increment clause before returning to check the conditional clause — thereby repeating the process. The increment clause normally houses the autoincrement or autodecrement statements used to update the counting variables.
The following while loop is equivalent to the for loop:
{
initialization;
while(conditional)
{
{
// ...body of the loop
}
increment;
}
}
All three clauses are optional. If the initialization or increment clauses are missing, C++ ignores them. If the conditional clause is missing, C++ performs the for loop forever (or until something else passes control outside the loop).
The for loop is best understood by example. The following ForDemo program is nothing more than the WhileDemo converted to use the for loop construct:
68 Part I: Introduction to C++ Programming
// ForDemo1 - input a loop count. Loop while
// outputting astring arg number of times.
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// input the loop count int loopCount;
cout << “Enter loopCount: “;
cin >> loopCount;
// count up to the loop count limit for (; loopCount > 0
{
loopCount = loopCount - 1;
cout << “Only “ << loopCount << “ loops to go\n”;
}
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
The program reads a value from the keyboard into the variable loopCount. The for starts out comparing loopCount to zero. Control passes into the
for loop if loopCount is greater than zero. Once inside the for loop, the pro_ gram decrements loopCount and displays the result. That done, the program returns to the for loop control. Control skips to the next line after the for loop as soon as loopCount has been decremented to zero.
All three sections of a for loop may be empty. An empty initialization or increment section does nothing. An empty comparison section is treated like a comparison that returns true.
This for loop has two small problems. First, it’s destructive — not in the sense of what my puppy does to a slipper, but in the sense that it changes the value of loopCount, “destroying” the original value. Second, this for loop counts “backward” from large values down to smaller values. These two problems are addressed if you add a dedicated counting variable to the for loop. Here’s what it looks like:
// ForDemo2 - input a loop count. Loop while
// outputting astring arg number of times.
#include <cstdio>
#include <cstdlib>
Chapter 5: Controlling Program Flow 69
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// input the loop count int loopCount;
cout << “Enter loopCount: “;
cin >> loopCount;
// count up to the loop count limit for (int i = 1; i <= loopCount; i++)
{
cout << “We’ve finished “ << i << “ loops\n”;
}
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
This modified version of WhileDemo loops the same as it did before. Instead of modifying the value of loopCount, however, this ForDemo2 version uses a counter variable.
Control begins by declaring a variable and initializing it to the value contained in loopCount. It then checks the variable i to make sure that it is positive. If so, the program executes the output statement, decrements i and starts over.
When declared within the initialization portion of the for loop, the index variable is only known within the for loop itself. Nerdy C++ programmers say that the scope of the variable is the for loop. In the example just given, the variable i is not accessible from the return statement because that state_ ment is not within the loop. Note, however, that not all compilers are strict about sticking to this rule. The Dev-C++ compiler (for example) generates a warning if you use i outside the for loop — but it uses the variable anyway.
Avoiding the dreaded infinite loop
An infinite loop is an execution path that continues forever. An infinite loop occurs any time the condition that would otherwise terminate the loop can’t occur — usually the result of a coding error.
Consider the following minor variation of the earlier loop:
70 Part I: Introduction to C++ Programming
while (loopCount > 0)
{
cout << “Only “ << loopCount << “ loops to go\n”;
}
The programmer forgot to decrement the variable loopCount as in the loop example below. The result is a loop counter that never changes. The test con_ dition is either always false or always true. The program executes in a never- ending (infinite) loop.
I realize that nothingÂ’s infinite. Eventually the power will fail, the computer will break, Microsoft will go bankrupt, and dogs will sleep with cats. . . . Either the loop will stop executing, or you wonÂ’t care anymore.
You can create an infinite loop in many more ways than shown here, most of which are much more difficult to spot than this one.
Applying special loop controls
C++ defines two special flow-control commands known as break and continue. Sometimes the condition for terminating the loop occurs at neither the beginning nor the end of the loop, but in the middle. Consider a program that accumulates numbers of values entered by the user. The loop terminates when the user enters a negative number.
The challenge with this problem is that the program canÂ’t exit the loop until the user has entered a value, but must exit before the value is added to the sum.
For these cases, C++ defines the break command. When encountered, the break causes control to exit the current loop immediately. Control passes from the break statement to the statement immediately following the closed brace at the end of the loop.
The format of the break commands is as follows:
while(condition) // break works equally well in for loop
{
if (some other condition)
{
break; // exit the loop
}
} // control passes here when the
// program encounters the break
Armed with this new break command, my solution to the accumulator prob_
lem appears as the program BreakDemo.
Chapter 5: Controlling Program Flow 71
// BreakDemo - input a series of numbers.
// Continue to accumulate the sum
// of these numbers until the user
// enters a negative number.
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// input the loop count int accumulator = 0;
cout << “This program sums values entered”
<< “by the user\n”;
cout << “Terminate the loop by entering “
<< “a negative number\n”;
// loop “forever”
for(;
{
// fetch another number int value = 0;
cout << “Enter next number: “;
cin >> value;
// if itÂ’s negative... if (value < 0)
{
// ...then exit break;
}
// ...otherwise add the number to the
// accumulator
accumulator = accumulator + value;
}
// now that weÂ’ve exited the loop
// output the accumulated result cout << “\nThe total is “
<< accumulator
<< “\n”;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
After explaining the rules to the user (entering a negative number to termi_
nate, and so on), the program enters what looks like an infinite for loop.
72 Part I: Introduction to C++ Programming
Once within the loop, BreakDemo retrieves a number from the keyboard. Only after the program has read a number can it test to see whether the number it just read matches the exit criteria. If the input number is negative, control passes to the break, causing the program to exit the loop. If the input number is not negative, control skips over the break command to the expression that sums the new value into the accumulator. After the program exits the loop, it outputs the accumulated value and then exits.
When performing an operation on a variable repeatedly in a loop, make sure that the variable is initialized properly before entering the loop. In this case, the program zeros accumulator before entering the loop where value is added to it.
The result of an example run appears as follows:
This program sums values entered by the user Terminate the loop by entering a negative number Enter next number: 1
Enter next number: 2
Enter next number: 3
Enter next number: -1
The total is 6
Press any key to continue . . .
The continue command is used less frequently. When the program encoun_ ters the continue command, it immediately moves back to the top of the loop. The rest of the statements in the loop are ignored for the current iteration.
The following example snippet ignores negative numbers that the user might input. Only a zero terminates this version (the complete program appears on
the CD-ROM as ContinueDemo):
while(true) // this while() has the same effect as for(;
{
// input a value
cout << “Input a value:”;
cin >> value;
// if the value is negative... if (value < 0)
{
// ...output an error message...
cout << “Negative numbers are not allowed\n”;
// ...and go back to the top of the loop continue;
}
// ...continue to process input like normal
}
Chapter 5: Controlling Program Flow 73
Nesting Control Commands
Return to our PC-screen-repaint problem. Surely it must need a loop struc_ ture of some type to write each pixel from left to right on a single line. (Do Middle Eastern terminals scan from right to left? I have no idea.) What about repeatedly repainting each scan line from top to bottom? (Do PC screens in Australia scan from bottom to top? Beats me.) For this particular task, you need to include the left-to-right scan loop within the top-to-bottom scan loop.
A loop command within another loop is known as a nested loop. As an exam_ ple, you can modify the BreakDemo program into a program that accumulates any number of sequences. In this NestedDemo program, the inner loop sums numbers entered from the keyboard until the user enters a negative number. The outer loop continues accumulating sequences until the sum is 0. HereÂ’s what it looks like:
// NestedDemo - input a series of numbers.
// Continue to accumulate the sum
// of these numbers until the user
// enters a 0. Repeat the process
// until the sum is 0.
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// the outer loop
cout << “This program sums multiple series\n”
<< “of numbers. Terminate each sequence\n”
<< “by entering a negative number.\n”
<< “Terminate the series by entering two\n”
<< “negative numbers in a row\n”;
// continue to accumulate sequences int accumulator;
do
{
// start entering the next sequence
// of numbers accumulator = 0;
cout << “Start the next sequence\n”;
// loop forever for(;
{
// fetch another number
int value = 0;
74 Part I: Introduction to C++ Programming
cout << “Enter next number: “;
cin >> value;
// if itÂ’s negative... if (value < 0)
{
// ...then exit break;
}
// ...otherwise add the number to the
// accumulator
accumulator = accumulator + value;
}
// output the accumulated result...
cout << “The total for this sequence is “
<< accumulator
<< endl << endl;
// ...and start over with a new sequence
// if the accumulated sequence was not zero
} while (accumulator != 0);
// weÂ’re about to quit
cout << “Thank you” << endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
Switching to a Different Subject?
One last control statement is useful in a limited number of cases. The switch statement resembles a compound if statement by including a number of dif_ ferent possibilities rather than a single test:
switch(expression)
{
case c1:
// go here if the expression == c1
break;
case c2:
// go here if expression == c2
break;
default:
// go here if there is no match
}
Chapter 5: Controlling Program Flow 75
The value of expression must be an integer (int, long, or char). The case values c1, c2, and c3 must be constants. When the switch statement is encountered, the expression is evaluated and compared to the various case constants. Control branches to the case that matches. If none of the cases match, control passes to the default clause.
Consider the following example code snippet:
int choice;
cout << “Enter a 1, 2 or 3:”;
cin >> choice;
switch(choice)
{
case 1:
// do “1” processing break;
case 2:
// do “2” processing break;
case 3:
// do “3” processing break;
default:
cout << “You didn’t enter a 1, 2 or 3\n”;
}
Once again, the switch statement has an equivalent, in this case multiple if statements; however, when there are more than two or three cases, the switch structure is easier to understand.
The break statements are necessary to exit the switch command. Without the break statements, control falls through from one case to the next. (Look out below!)
76 Part I: Introduction to C++ Programming
Part II
Becoming a Functional C++ Programmer
In this part . . .
I
t’s one thing to perform operations such as addition and multiplication — even when we’re logical (AND and OR or other operations). It’s another thing to write
real programs. This section introduces the features neces_
sary to make the leap into programmerdom.
YouÂ’ll ï¬nd the program BUDGET1 on the enclosed
CD-ROM. This largish program demonstrates the concepts of functional programming. You may want to visit this pro_ gram and its documentation once youÂ’ve mastered func_ tional programming concepts.
Chapter 6
Creating Functions
In This Chapter
Writing functions
Passing data to functions
Naming functions with different arguments
Creating function prototypes
Working with include files
The programs developed in prior chapters have been small enough that they can be easily read as a single unit. Larger, real-world programs can be many thousands (or millions!) of lines long. Developers need to break up
these monster programs into smaller chunks that are easier to conceive, develop, and maintain.
C++ allows programmers to divide their code into exactly such chunks
(known as functions). As long as a function has a simple description and a well-defined interface to the outside world, it can be written and debugged without worrying about the code that surrounds it.
This divide-and-conquer approach reduces the difficulty of creating a work_ ing program of significant size. This is a simple form of encapsulation — see Chapter 15 for more details on encapsulation.
Writing and Using a Function
Functions are best understood by example. This section starts with the example program FunctionDemo, which simplifies the NestedDemo program I discussed in Chapter 5 by defining a function to contain part of the logic. Then this section explains how the function is defined and how it is invoked, using FunctionDemo as a pattern for understanding both the problem and the solution.
80 Part II: Becoming a Functional C++ Programmer The NestedDemo program in Chapter 5 contains an inner loop (which accu_ mulates a sequence of numbers) surrounded by an outer loop (which repeats the process until the user quits). Separating the two loops would simplify the program by allowing the reader to concentrate on each loop independently.
The following FunctionDemo program shows how NestedDemo can be simpli_
fied by creating the function sumSequence().
Function names are normally written with a set of parentheses immediately following the term, like this:
// FunctionDemo - demonstrate the use of functions
// by breaking the inner loop of the
// NestedDemo program off into its own
// function
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
// sumSequence - add a sequence of numbers entered from
// the keyboard until the user enters a
// negative number.
// return - the summation of numbers entered int sumSequence(void)
{
// loop forever
int accumulator = 0;
for(;
{
// fetch another number int value = 0;
cout << “Enter next number: “;
cin >> value;
// if itÂ’s negative... if (value < 0)
{
// ...then exit from the loop break;
}
// ...otherwise add the number to the
// accumulator
accumulator= accumulator + value;
}
// return the accumulated value return accumulator;
}
int main(int nNumberofArgs, char* pszArgs[])
{
Chapter 6: Creating Functions 81
cout << “This program sums multiple series\n”
<< “of numbers. Terminate each sequence\n”
<< “by entering a negative number.\n”
<< “Terminate the series by entering two\n”
<< “negative numbers in a row\n”
<< endl;
// accumulate sequences of numbers... int accumulatedValue;
for(;
{
// sum a sequence of numbers entered from
// the keyboard
cout << “Enter next sequence” << endl;
accumulatedValue = sumSequence();
// terminate the loop if sumSequence() returns
// a zero
if (accumulatedValue == 0)
{
break;
}
// now output the accumulated result cout << “The total is “
<< accumulatedValue
<< “\n”
<< endl;
}
cout << “Thank you” << endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
Defining the sumSequence() function The statement int sumSequence(void) begins the definition of the sumSequence() function. The block of code contained in the braces is the function body. The function sumSequence() accumulates a sequence of values entered from the keyboard. This code section is identical to that found in the inner loop of NestedDemo.
82 Part II: Becoming a Functional C++ Programmer
Calling the function sumSequence()
LetÂ’s concentrate on the main program contained in the braces following
main(). This section of code looks similar to the outer loop in NestedDemo. The main difference is the expression accumulatedValue = sumSequence(); that appears where the inner loop would have been. The sumSequence() statement invokes the function of that name. A value returned by the function is stored in the variable accumulatedValue. Then this value is displayed.
The main program continues to loop until sumSequence() returns a sum of zero, which indicates that the user has finished calculating sums.
Divide and conquer
The FunctionDemo program has split the outer loop in main() from the inner loop into a function sumSequence(). This division wasn’t arbitrary: sumSequence() performs a separate role — worth considering by itself — apart from the control features within FunctionDemo.
A good function is easy to describe. You shouldnÂ’t have to use more than a single sentence, with a minimum of such words as and, or, unless, or but. For example, hereÂ’s a simple, straightforward definition:
“The function sumSequence accumulates a sequence of integer values entered by the user.”
This definition is concise and clear. ItÂ’s a world away from the ContinueDemo
program description:
“sums a sequence of positive values AND generates an error if the user enters a negative number AND displays the sum AND starts over again until the user enters two zero-length sums.”
The output of a sample run of this program appears much like that generated by the NestedDemo program, as follows:
This program sums multiple series of numbers. Terminate each sequence by entering a negative number.
Terminate the series by entering two negative numbers in a row
Enter next sequence
Enter next number: 1
Enter next number: 2
Chapter 6: Creating Functions 83
Enter next number: 3
Enter next number: -1
The total is 6
Enter next sequence
Enter next number: 1
Enter next number: 2
Enter next number: -1
The total is 3
Enter next sequence
Enter next number: -1
Thank you
Press any key to continue . . .
Understanding the Details of Functions Functions are so fundamental to creating C++ programs that getting a handle on the details of defining, creating, and testing them is critical. Armed with the example FunctionDemo program, consider the following definition of function:
A function is a logically separated block of C++ code. The function construct has the following form:
<return type> name(<arguments to the function>)
{
// ...
return <expression>;
}
The arguments to a function are values that can be passed to the function to be used as input information. The return value is a value that the function returns. For example, in the call to the function square(10), the value 10 is an argument to the function square(). The returned value is 100.
Both the arguments and the return value are optional. If either is absent, the keyword void is used instead. That is, if a function has a void argument list, the function does not take any arguments when called (this was the case with the FunctionDemo program). If the return type is void, the function does not return a value to the caller.
In the example FunctionDemo program, the name of the function is
sumSequence(), the return type is int, and no arguments exist.
84 Part II: Becoming a Functional C++ Programmer
The default argument type to a function is void, meaning that it takes no arguments. A function int fn(void) may be declared as int fn().
The function construct made it possible for me to write two distinct parts of the FunctionDemo program separately. I concentrated on creating the sum of a sequence of numbers when writing the sumSequence() function. I didnÂ’t think about other code that may call the function.
Similarly, when writing main(), I concentrated on handling the summation returned by sumSequence() while thinking only of what the function did — not how it worked.
Understanding simple functions
The simple function sumSequence() returns an integer value that it calcu_ lates. Functions may return any of the regular types of variables. For exam_ ple, a function might return a double or a char (int, double, and char are a few of the variable types discussed in Chapter 2).
If a function returns no value, the return type of the function is labeled void. A function may be labeled by its return type — for example, a function that returns an int is often known as an integer function. A function that returns no value is known as a void function.
For example, the following void function performs an operation, but returns no value:
void echoSquare()
{
int value;
cout << “Enter a value:”;
cin >> value;
cout << “\n The square is:” << (value * value) << “\n”;
return;
}
Control begins at the open brace and continues through to the return state_
ment. The return statement in a void function is not followed by a value. The return statement in a void function is optional. If it isnÂ’t present, execu_ tion returns to the calling function when control encounters the close brace.
Chapter 6: Creating Functions 85
Understanding functions with arguments Simple functions are of limited use because the communication from such functions is one-way — through the return value. Two-way communication is through function arguments.
Functions with arguments
A function argument is a variable whose value is passed to the calling function during the call operation. The following SquareDemo example program defines and uses a function square() that returns the square of a double precision float passed to it:
// SquareDemo - demonstrate the use of a function
// which processes arguments
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
// square - returns the square of its argument
// doubleVar - the value to be squared
// returns - square of doubleVar double square(double doubleVar)
{
return doubleVar * doubleVar;
}
// sumSequence - accumulate the square of the number
// entered at the keyboard into a sequence
// until the user enters a negative number double sumSequence(void)
{
// loop forever
double accumulator= 0.0;
for(;
{
// fetch another number double dValue = 0;
cout << “Enter next number: “;
cin >> dValue;
// if itÂ’s negative...
if (dValue < 0)
{
// ...then exit from the loop
86 Part II: Becoming a Functional C++ Programmer
break;
}
// ...otherwise calculate the square double value = square(dValue);
// now add the square to the
// accumulator
accumulator= accumulator + value;
}
// return the accumulated value return accumulator;
}
int main(int nNumberofArgs, char* pszArgs[])
{
cout << “This program sums multiple series\n”
<< “of numbers squared. Terminate each sequence\n”
<< “by entering a negative number.\n”
<< “Terminate the series by entering two\n”
<< “negative numbers in a row\n”
<< endl;
// Continue to accumulate numbers... double accumulatedValue;
for(;
{
// sum a sequence of numbers entered from
// the keyboard
cout << “Enter next sequence” << endl;
accumulatedValue = sumSequence();
// terminate if the sequence is zero or negative if (accumulatedValue <= 0.0)
{
break;
}
// now output the accumulated result
cout << “\nThe total of the values squared is “
<< accumulatedValue
<< endl;
}
cout << “Thank you” << endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
Chapter 6: Creating Functions 87
This is the same FunctionDemo() program, except that SquareDemo() accumulates the square of the values entered. The function square() returns the value of its one argument multiplied by itself. The change to
the sumSequence() function is simple — rather than accumulate the value entered, the function now accumulates the result returned from square().
Functions with multiple arguments
Functions may have multiple arguments that are separated by commas. Thus, the following function returns the product of its two arguments:
int product(int arg1, int arg2)
{
return arg1 * arg2;
}
main() exposed
The “keyword” main() from our standard program template is nothing more than a function — albeit a function with strange arguments — but a function nonetheless.
When a program is built, C++ adds some boilerplate code that executes before your program ever starts (you canÂ’t see this code without digging into the bowels of the C++ library functions). This code sets up the environment in which your program operates. For example, this boilerplate code opens the default input and output channels cin and cout.
After the environment has been established, the C++ boilerplate code calls the function main(), thereby beginning execution of your code. When your program finishes, it exits from main(). This enables the C++ boilerplate to clean up a few things before turning control over to the operating system that kills the program.
Overloading Function Names
C++ allows the programmer to assign the same name to two or more functions. This multiple use of names is known as overloading functions.
In general, two functions in a single program cannot share the same name. If they did, C++ would have no way to distinguish them. Note, however, that the name of the function includes the number and type of its arguments — but does not include its return argument. Thus the following are not the same functions:
88 Part II: Becoming a Functional C++ Programmer
void someFunction(void)
{
// ....perform some function
}
void someFunction(int n)
{
// ...perform some different function
}
void someFunction(double d)
{
// ...perform some very different function
}
void someFunction(int n1, int n2)
{
// ....do something different yet
}
C++ still knows that the functions someFunction(void), someFunction(int), someFunction(double), and someFunction(int, int) are not the same. Like so many things that deal with computers, this has an analogy in the human world.
void as an argument type is optional. sumFunction(void) and sumFunction() are the same function. A function has a shorthand name, such as someFunction(), in same way that I have the shorthand name Stephen (actually, my nickname is Randy, but work with me on this one). If there arenÂ’t any other Stephens around, people can talk about Stephen
behind his back. If, however, there are other Stephens, no matter how hand_ some they might be, people have to use their full names — in my case, Stephen Davis. As long as we use the entire name, no one gets confused — however many Stephens might be milling around. Similarly, the full name for one of the someFunctions() is someFunction(int). As long as this full name is unique, no confusion occurs.
The analogies between the computer world (wherever that is) and the human world are hardly surprising because humans build computers. (I wonder . . .
if dogs had built computers, would the standard unit of memory be a gnaw instead of a byte? Would requests group in packs instead of queues?)
HereÂ’s a typical application that uses overloaded functions with unique full names:
int intVariable1, intVariable2; // equivalent to
// int Variable1;
// int Variable2;
double doubleVariable;
// functions are distinguished by the type of
// the argument passed
Chapter 6: Creating Functions 89
someFunction(); // calls someFunction(void) someFunction(intVariable1); // calls someFunction(int) someFunction(doubleVariable); // calls someFunction(double) someFunction(intVariable1, intVariable2); // calls
// someFunction(int, int)
// this works for constants as well
someFunction(1); // calls someFunction(int)
someFunction(1.0); // calls someFunction(double)
someFunction(1, 2); // calls someFunction(int, int) In each case, the type of the arguments matches the full name of the three functions.
The return type is not part of the extended name (which is also known as the
function signature) of the function. The following two functions have the same
name — so they can’t be part of the same program:
int someFunction(int n); // full name of the function
// is someFunction(int)
double someFunction(int n); // same name
YouÂ’re allowed to mix variable types as long as the source variable type is
more restrictive than the target type. Thus an int can be promoted to a
double. The following is acceptable:
int someFunction(int n);
double d = someFunction(10); // promote returned value
The int returned by someFunction() is promoted into a double. Thus the following would be confusing:
int someFunction(int n);
double someFunction(int n);
double d = someFunction(10);// promote returned int?
// or use returned double as is Here C++ does not know whether to use the value returned from the double version of someFunction() or promote the value returned from int version.
Defining Function Prototypes
The programmer may provide the remainder of a C++ source file, or module, the extended name (the name and functions) during the definition of the function.
90 Part II: Becoming a Functional C++ Programmer The target functions sumSequence() and square() — appearing earlier in this chapter — are both defined in the code that appears before the actual call. This doesn’t have to be the case: A function may be defined anywhere
in the module. (A module is another name for a C++ source file.)
However, something has to tell the calling function the full name of the func_
tion to be called. Consider the following code snippet:
int main(int nNumberofArgs, char* pszArgs[])
{
someFunc(1, 2);
}
int someFunc(double arg1, int arg2)
{
// ...do something
}
main() doesn’t know the full name of the function someFunc() at the time of the call. It may surmise from the arguments that the name is someFunc(int, int) and that its return type is void — but as you can see, this is incorrect.
I know, I know — C++ could be less lazy and look ahead to determine the full name of someFunc()s on its own, but it doesn’t. It’s like my crummy car; it gets me there, and I’ve learned to live with it.
What is needed is some way to inform main() of the full name of someFunc()
before it is used. This is handled by what we call a function prototype.
A prototype declaration appears the same as a function with no body. In use, a prototype declaration looks like this:
int someFunc(double, int);
int main(int nNumberofArgs, char* pszArgs[])
{
someFunc(1, 2);
}
int someFunc(double arg1, int arg2)
{
// ...do something
}
The prototype declaration tells the world (at least that part of the world after the declaration) that the extended name for someFunc() is someFunction(double, int). The call in main() now knows to cast
the 1 to a double before making the call. In addition, main() knows that the value returned by someFunc() is an int.
Chapter 6: Creating Functions 91
Variable Storage Types
Function variables are stored in three different places. Variables declared within a function are said to be local. In the following example, the variable localVariable is local to the function fn():
int globalVariable;
void fn()
{
int localVariable;
static int staticVariable;
}
The variable localVariable doesnÂ’t exist until execution passes through its declaration within the function fn(). localVariable ceases to exist when the function returns. Upon return, whatever value that is stored in
localVariable is lost. In addition, only fn() has access to localVariable —
other functions cannot reach into the function to access it.
By comparison, the variable globalVariable is created when the program begins execution — and exists as long as the program is running. All func_ tions have access to globalVariable all the time.
The static variable staticVariable is a sort of mix between a local and a global variable. The variable staticVariable is created when execution first reaches the declaration — at roughly when the function fn() is called.
The variable is not destroyed when program execution returns from the func_ tion. If fn() assigns a value to staticVariable once, itÂ’ll still be there the next time fn() is called. The declaration is ignored every subsequent time execution passes through.
In case anyone asks, there is a fourth type, auto, but today it has the same meaning as local, so you rarely (if ever) see that declaration type anymore. So whoever asked you about it is probably just being a show off (or showing his age).
Including Include Files
It’s common to place function prototypes in a separate file (called an include file) that the programmer can then include in her C++ source files. Doing so sets the stage for a C++ preprocessor program (which runs before the actual compiler takes over) to insert the contents of a file such as filename, at the point of a statement such as #include “filename”.
92 Part II: Becoming a Functional C++ Programmer
A definition for a typical math include file looks like this:
// math include file:
// provide prototypes for functions that might be useful
// in more than one program
// abs - return the absolute value of the argument double abs(double d);
// square - return the square of the argument
double square(double d);
A program uses the math include file like this:
// MyProgram -
#include “math”
using namespace std;
// my code continues here
The #include directive says, in effect, Replace this directive with the contents of the math file.
The #include directive doesnÂ’t have the format of a C++ statement because itÂ’s interpreted by a separate interpreter that executes before the C++ com_ piler starts doing its thing. In particular, the # must be in column one and an end-of-line terminates the include statement. The actual file name must be enclosed in either quotes or brackets. Brackets are used for C++ library func_ tions. Use the quotes for any includes that you create.
The C++ environment provides include files such as cstdio and iostream. In fact, itÂ’s iostream that contains the prototype for the setf() function used in Chapter 4 to set output to hex mode.
For years, programmers followed the custom of using the extension .h to designate include files. In more recent years, the C++ ISO standard removed the .h extension from standard include files. (For example, the include file cstdio was previously known as stdio.h.) Many programmers still stub_ bornly cling to the “.h gold standard” for their own programs. (What’s in a name? Evidence that even high-tech folks have traditions.)
Chapter 7
Storing Sequences in Arrays
In This Chapter
Considering the need for something like an array
Introducing the array data type
Using an array
Using the most common type of array — the character string
An array is a sequence of variables that shares the same name and that is referenced using an index. Arrays are useful little critters that allow
you to store a large number of values of the same type and that are related in some way — for example, the batting averages of all the players on the same team might be a good candidate for storage within an array. Arrays can be multidimensional, too, allowing you, for example, to store an array of batting averages within an array of months, which allows you to work with the bat_ ting averages of the team as they occur by month. If you think about it long enough, you get a headache.
In this chapter, you find out how to initialize and use arrays for fun and profit. You also find out about an especially useful form of array, a string, which in C++ is really just an array of type char.
Considering the Need for Arrays
Consider the following problem. You need a program that can read a sequence of numbers from the keyboard. You guessed it — the program stops reading in numbers as soon as you enter a negative number. Unlike similar programs in Chapters 5 and 6, this program is to output all the numbers entered before displaying the average.
94 Part II: Becoming a Functional C++ Programmer
You could try to store numbers in a set of independent variables, as in
cin >> value1;
if (value1 >= 0)
{
cin >> value2;
if (value2 >= 0)
{
...
You can see that this approach canÂ’t handle sequences involving more than just a few numbers. Besides, itÂ’s ugly. What we need is some type of structure that has a name like a variable but that can store more than one value. May I present to you, Ms. A. Ray.
An array solves the problem of sequences nicely. For example, the following snippet declares an array valueArray that has storage for up to 128 int values. It then populates the array with numbers entered from the keyboard.
int value;
// declare an array capable of holding up to 128 ints int valueArray[128];
// define an index used to access subsequent members of
// of the array; donÂ’t exceed the 128 int limit for (int i = 0; i < 128; i++)
{
cin >> value;
// exit the loop when the user enters a negative
// number
if (value < 0)
{
break;
}
valueArray = value;
}
The second line of this snippet declares an array valueArray. Array declara_ tions begin with the type of the array members: in this case, int. This is fol_ lowed by the name of the array. The last element of an array declaration is an open and closed bracket containing the maximum number of elements that the array can hold. In this code snippet, valueArray can store up to 128 integers.
This snippet reads a number from the keyboard and stores it into each sub_ sequent member of the array valueArray. You access an individual element of an array by providing the name of the array followed by brackets contain_ ing the index. The first integer in the array is valueArray[0], the second is valueArray[1], and so on.
Chapter 7: Storing Sequences in Arrays 95
In use, valueArray represents the ith element in the array. The index variable i must be a counting variable — that is, i must be a char, an int, or a long. If valueArray is an array of ints, valueArray is an int.
Using an array
The following program inputs a sequence of integer values from the keyboard until the user enters a negative number. The program then displays the num_ bers input and reports their sum.
// ArrayDemo - demonstrate the use of arrays
// by reading a sequence of integers
// and then displaying them in order
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
// prototype declarations
int sumArray(int integerArray[], int sizeOfloatArray);
void displayArray(int integerArray[], int sizeOfloatArray);
int main(int nNumberofArgs, char* pszArgs[])
{
// input the loop count int nAccumulator = 0;
cout << “This program sums values entered “
<< “by the user\n”;
cout << “Terminate the loop by entering “
<< “a negative number\n”;
cout << endl;
// store numbers into an array int inputValues[128];
int numberOfValues;
for(numberOfValues = 0; numberOfValues < 128; numberOfValues++)
{
// fetch another number int integerValue;
cout << “Enter next number: “;
cin >> integerValue;
// if itÂ’s negative...
if (integerValue < 0)
96 Part II: Becoming a Functional C++ Programmer
{
// ...then exit break;
}
// ... otherwise store the number
// into the storage array inputValues[numberOfValues] = integerValue;
}
// now output the values and the sum of the values displayArray(inputValues, numberOfValues);
cout << “The sum is “
<< sumArray(inputValues, numberOfValues)
<< endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
// displayArray - display the members of an
// array of length sizeOfloatArray
void displayArray(int integerArray[], int sizeOfArray)
{
cout << “The value of the array is:” << endl;
for (int i = 0; i < sizeOfArray; i++)
{
cout.width(3);
cout << i << “: “ << integerArray << endl;
}
cout << endl;
}
// sumArray - return the sum of the members of an
// integer array
int sumArray(int integerArray[], int sizeOfArray)
{
int accumulator = 0;
for (int i = 0; i < sizeOfArray; i++)
{
accumulator += integerArray;
}
return accumulator;
}
The program ArrayDemo begins with a prototype declaration of the functions sumArray() and displayArray() that it will need later. The main body of the program contains an input loop (boring). This time, however, the input values are stored off in the array inputValues.
Chapter 7: Storing Sequences in Arrays 97
If the input value is negative, control exits the loop by executing the break. If not, integerValue is copied into the array. The int variable numberOfValues is used as an index into the array. numberOfValues was initialized to 0 up
at the beginning of the for loop. The index is incremented on each iteration of the loop. The test in the for loop keeps the program from storing more than the 128 integers allocated by the program. (The program goes immedi_ ately to the output portion after 128 entries whether or not the user enters a negative number.)
The array inputValues is declared as 128 integers long. If youÂ’re thinking that this is enough, donÂ’t count on it. Writing more data than an array causes your program to perform erratically and often to crash. No matter how large you make the array, always put a check to make sure that you do not exceed the limits of the array.
The main function ends by calling displayArray() to print the contents of the array and the sum.
The Dev-C++ environment can help keep you and your functions straight. Figure 7-1 shows the contents of the Classes tab. The name and prototype of each function appear there. Double-clicking a function name takes you straight to the function in the .CPP source file.
Figure 7-1: The Classes tab displays information about the functions that make up the program.
The displayArray() function contains the typical for loop used to traverse an array. Each entry in the array is added to the variable accumulator. The sizeOfArray passed to the function indicates the number of values contained in integerArray.
Notice, yet again, that the index is initialized to 0 and not to 1. In addition, notice how the for loop terminates before i is equal to sizeOfArray. None of the elements after the sizeOfArray element contains valid data. The output appears as follows:
98 Part II: Becoming a Functional C++ Programmer
This program sums values entered by the user
Terminate the loop by entering a negative number
Enter next number: 1
Enter next number: 2
Enter next number: 3
Enter next number: -1
The value of the array is:
0: 1
1: 2
2: 3
The sum is 6
Press any key to continue . . .
Just to keep nonprogrammers guessing, the term iterate means to tra_
verse through a set of objects such as an array. Programmers say that
the sumArray() function iterates through the array. In a similar fashion, I “get irate” when my dog iterates from one piece of furniture to another.
Initializing an array
A local variable does not start life with a valid value, not even the value 0. Said another way, a local variable contains garbage until you actually store something in it. Locally declared arrays are the same — each element con_ tains garbage until you actually assign something to it. You should initialize local variables when you declare them. This rule is even truer for arrays. It is far too easy to access uninitialized array elements thinking that they are valid values.
Fortunately, a small array may be initialized at the time it is declared. The fol_
lowing code snippet demonstrates how this is done:
float floatArray[5] = {0.0, 1.0, 2.0, 3.0, 4.0};
This initializes floatArray[0] to 0, floatArray[1] to 1, floatArray[2]
to 2, and so on.
The number of initialization constants can determine the size of the array. For example, you could have determined that floatArray has five elements just by counting the values within the braces. C++ can count as well (hereÂ’s at least one thing C++ can do for itself).
The following declaration is identical to the preceding one.
float floatArray[] = {0.0, 1.0, 2.0, 3.0, 4.0};
Chapter 7: Storing Sequences in Arrays 99
You may initialize all the elements in an array to a common value by listing only that value. For example, the following initializes all 25 locations in floatArray to 1.0.
float floatArray[25] = {1.0};
Accessing too far into an array
Mathematicians start counting arrays with 1. Most program languages start with an offset of 1 as well. C++ arrays begin counting at 0. The first member of a C++ array is valueArray[0]. That makes the last element of a 128-integer array integerArray[127] and not integerArray[128].
Unfortunately for the programmer, C++ does not check to see whether the index you are using is within the range of the array. C++ is perfectly happy giving you access to integerArray[200]. Our yard is only 128 integers long — that’s 72 integers into someone else’s yard. No telling who lives there and what he’s storing at that location. Reading from integerArray[200] will return some unknown and unpredictable value. Writing to that location gen_ erates unpredictable results. It may do nothing — the house may be aban_ doned and the yard unused. On the other hand, it might overwrite some data, thereby confusing the neighbor and making the program act in a seemingly random fashion. Or it might crash the program.
The most common wrong way to access an array is to read or write loca_ tion integerArray[128]. This is one integer beyond the end of the array. Although itÂ’s only one element beyond the end of the array, reading or writ_ ing this location is just as dangerous as using any other incorrect address.
Using arrays
On the surface, the ArrayDemo program doesnÂ’t do anything more than our earlier, non-array-based programs did. True, this version can replay its input by displaying the set of input numbers before calculating their sum, but this feature hardly seems earth shattering.
Yet, the ability to redisplay the input values hints at a significant advantage to using arrays. Arrays allow the program to process a series of numbers multiple times. The main program was able to pass the array of input values to displayArray() for display and then repass the same numbers to sumArray() for addition.
100 Part II: Becoming a Functional C++ Programmer
Defining and using arrays of arrays
Arrays are adept at storing sequences of numbers. Some applications require sequences of sequences. A classic example of this matrix configuration is the spreadsheet. Laid out like a chessboard, each element in the spreadsheet has both an x and a y offset.
C++ implements the matrix as follows:
int intMatrix[10][5];
This matrix is 10 elements in 1 dimension, and 5 in another, for a total of 50 elements. In other words, intMatrix is a 10-element array, each element of which is a 5-int array. As you might expect, one corner of the matrix is in intMatrix[0][0], while the other corner is intMatrix[9][4].
Whether you consider intMatrix to be 10 elements long in the x dimension and in the y dimension is a matter of taste. A matrix can be initialized in the same way that an array is
int intMatrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
This line initializes the 3-element array intMatrix[0] to 1, 2, and 3 and the
3-element array intMatrix[1] to 4, 5, and 6, respectively.
Using Arrays of Characters
The elements of an array are of any type. Arrays of floats, doubles, and longs are all possible; however, arrays of characters have particular significance.
Creating an array of characters
Human words and sentences can be expressed as an array of characters. An array of characters containing my first name would appear as
char sMyName[] = {‘S’, ‘t’, ‘e’, ‘p’, ‘h’, ‘e’, ‘n’};
The following small program displays my name:
// CharDisplay - output a character array to
// standard output, the MS-DOS window
Chapter 7: Storing Sequences in Arrays 101
#include <stdio.h>
#include <iostream.h>
// prototype declarations
void displayCharArray(char stringArray[], int sizeOfloatArray);
int main(int nArg, char* pszArgs[])
{
char charMyName[] = {‘S’, ‘t’, ‘e’, ‘p’, ‘h’, ‘e’, ‘n’};
displayCharArray(charMyName, 7);
cout << “\n”;
return 0;
}
// displayCharArray - display an array of characters
// by outputing one character at
// a time
void displayCharArray(char stringArray[], int sizeOfloatArray)
{
for(int i = 0; i< sizeOfloatArray; i++)
{
cout << stringArray;
}
}
The program declares a fixed array of characters charMyName containing — you guessed it — my name (what better name?). This array is passed to the function displayCharArray() along with its length. The displayCharArray() function is identical to the displayArray() function in the earlier example program except that this version displays chars rather than ints.
This program works fine; however, it is inconvenient to pass the length of the array around with the array itself. If we could come up with a rule for deter_ mining the end of the array, we wouldn’t need to pass its length — you would know that the array was complete when you encountered the special rule that told you so.
Creating a string of characters
In many cases, all values for each element are possible. However, C++ reserves the special “character” 0 as the non-character. We can use ‘\0’ to mark the end of a character array. (The numeric value of ‘\0’ is zero; how_ ever, the type of ‘\0’ is char.)
102 Part II: Becoming a Functional C++ Programmer
The character \y is the character whose numeric value is y. The character \0 is the character with a value of 0, otherwise known as the null character. Using that rule, the previous small program becomes
// DisplayString - output a character array to
// standard output, the MS-DOS window
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
using namespace std;
// prototype declarations
void displayString(char stringArray[]);
int main(int nNumberofArgs, char* pszArgs[])
{
char charMyName[] =
{‘S’, ‘t’, ‘e’, ‘p’, ‘h’, ‘e’, ‘n’, 0};
displayString(charMyName);
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
// displayString - display a character string
// one character at a time void displayString(char stringArray[])
{
for(int i = 0; stringArray != ‘\0’; i++)
{
cout << stringArray;
}
}
The declaration of charMyName declares the character array with the extra null character \0 on the end. The displayString program iterates through the character array until a null character is encountered.
The function displayString() is simpler to use than its displayCharArray() predecessor because it is no longer necessary to pass along the length of the character array. This secret handshake of terminating a character array with
a null is so convenient that it is used throughout C++ language. C++ even gives such an array a special name.
A string of characters is a null terminated character array. Confusingly enough, this is often shortened to simply string, even though C++ defines the separate type string.
Chapter 7: Storing Sequences in Arrays 103
The choice of ‘\0’ as the terminating character was not random. Remember that zero is the only numeric value that converts to false; all other values translate to true. This means that the for loop could (and usually is) writ_ ten as:
for(int i = 0; stringArray; i++)
This whole business of null terminated character strings is so ingrained into the C++ language psyche that C++ uses a string of characters surrounded by double quotes to be an array of characters automatically terminated with a
‘\0’ character. The following are identical declarations:
char szMyName[] = “Stephen”;
char szAlsoMyName[] =
{‘S’, ‘t’, ‘e’, ‘p’, ‘h’, ‘e’, ‘n’, ‘\0’};
The naming convention used here is exactly that, a convention. C++ does not care. The prefix sz stands for zero-terminated string.
The string Stephen is eight characters long and not seven — the null charac_ ter after the n is assumed. The string “” is one character long consisting of just the null character.
Manipulating Strings with Character
The C++ programmer is often required to manipulate strings. C++ provides a number of standard string-manipulation functions to make the job easier. A few of these functions are listed in Table 7-1.
Table 7-1 String-Handling Functions
Name Operation
int strlen(string) Returns the number of characters in a string.
void strcpy(target, source, n) Copies the source string into a target array.
void strcat(target, source, n) Concatenates the source string onto the end of the target string.
void strncpy(target, source, n) Copies a string up to n characters from the source string into a target array.
(continued)
104 Part II: Becoming a Functional C++ Programmer
Table 7-1 (continued)
Name Operation
void strncat(target, source, n) Concatenates the source string onto the end of the target string or
‘n’ characters, whichever comes first.
int strnstr(string, pattern, n) Finds the first occurrence of one pattern string in another.
int strncmp(source1, source2, n) Compares the first n characters in two strings. Returns a zero if the two strings match exactly.
int strnicmp(source1, source2) Compares up to n characters in two strings without regard to case.
You need to add the statement #include <strings.h> to the beginning of any program that uses a str... function.
The current ANSI C++ standard suggests that you avoid the str...() func_ tions. ANSI C++ says that these functions are deprecated, meaning that ANSI will leave them alone for now, but don’t be surprised if they go away some day. That’s why strings.h uses the older standard of ending all include files with a “.h”. The ANSI standard suggests that you use the string type as defined in the next section. However, you will see a large number of programs that continue to use these functions.
The following Concatenate program inputs two strings from the keyboard and concatenates them into a single string.
// Concatenate - concatenate two strings
// with a “ - “ in the middle
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
// the following include file is deprecated;
// however, it is required for the str functions
#include <strings.h>
int main(int nNumberofArgs, char* pszArgs[])
{
// read first string... char szString1[256];
cout << “Enter string #1:”;
cin >> szString1;
Chapter 7: Storing Sequences in Arrays 105
// safer alternative
// cin.getline(szString1, 128);
// ...now the second string... char szString2[128];
cout << “Enter string #2:”;
cin >> szString2;
// safer alternative
// cin.getline(szString1, 128);
// accumulate both strings into a single buffer char szString[260];
// copy the first string into the buffer... strncpy(szString, szString1, 128);
// ...concatenate a “ - “ onto the first... strncat(szString, “ - “, 4);
// ...now add the second string... strncat(szString, szString2, 128);
// ...and display the result
cout << “\n” << szString << endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
The Concatenate program reads two character strings and appends them together with a “ - “ in the middle.
The arguments to the str...() functions appear backward to any reason_ able individual (you might consider this an acid test for “reasonable”). For example, the function strncat(target, source, count) tacks the second string source onto the end of the first argument target.
An example output from the program appears as follows:
Enter string #1:Chester
Enter string #2og
Chester - Dog
Press any key to continue . . .
The program begins by reading a string from the keyboard. cin >> szString1 stops when any type of whitespace is encountered. Characters up to the first whitespace are read, the whitespace character is tossed, and
106 Part II: Becoming a Functional C++ Programmer the remaining characters are left in the input hopper for the next cin>> statement. Thus, if I were to enter “the Dog”, szString2 would be filled
with “the”, and the word “Dog” would be left in the input buffer.
The cin >> extractor knows nothing about the length of the string. cin is perfectly willing to read thousands of characters and stuff them into szString1, even though it is declared 256 characters long. This causes a dangerous overflow condition that hackers can (and will) exploit to put a virus in your program.
C++ provides work-arounds for many of the string overflow problems. For example, the function getline() inputs a line of text; however, this function accepts the length of the string as one of its arguments:
cin.getline(string, lengthOfTheString);
(Ignore the strange looking cin. format for now.)
The strncpy() and strncat() functions accept the length of the target buffer as one of their arguments. The call strncpy(szString, szString1,
128) says “copy the characters in szString1 into szString until you copy a null character or until you’ve copied 128 characters, whichever comes first.” The call specifically does not mean copy 128 characters every time.
There are both “counting” and “noncounting” versions of most of the str...() functions. The noncounting versions don’t require the maximum number of characters to process as an argument. You can use these when you don’t know the buffer size, but be aware that they are perfectly happy to write beyond the end of the target string.
String-ing Along Variables
ANSI C++ includes a type string designed to make it easier to manipulate strings of text.
I use the term string to refer to an array of characters terminated by a null and string type to refer to the type string. The string type includes opera_ tions for copying, concatenating, capitalizing, knotting, and simple magic tricks. string avoids the overrun problems inherent with null terminated strings. The functions that manipulate string objects are defined in the include file <string>.
The string type based StringConcatenate program appears as follows:
Chapter 7: Storing Sequences in Arrays 107
// StringConcatenate - concatenate two string type
// variables with a “ - “ in the middle
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
// read first string... string string1;
cout << “Enter string #1:”;
cin >> string1;
// ...now the second string... string string2;
cout << “Enter string #2:”;
cin >> string2;
// accumulate both strings into a single buffer string buffer;
string divider = “ - “;
buffer = string1 + divider + string2;
// ...and display the result cout << “\n” << buffer << endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
This concatenate function defines two variables, string1 and string2
(clever, no?). A string type variable is not defined of any specified length — it can grow and shrink to fit the number of characters it contains (up to avail_ able memory, of course, or the cows come home, whichever is first). Not only do you not have to guess how big to make a target character array, but some nefarious user can’t crash your program by inputting too many characters. The StringConcatenate program manipulates the string type variables as it would any other.
Notice that some operations have to be understood in a slightly different way from their arithmetic equivalent. For example, to add two string type vari_ ables together means to concatenate them. In addition, notice how C++ can convert a null terminated character string into a string type variable with_ out being told to.
108 Part II: Becoming a Functional C++ Programmer
The string type is not intrinsic to C++ like int or float, meaning that its operations are not built into the syntax of the language. Operations on string type variables are defined in the string include file. The string class is discussed further in Chapter 27. I mention string here to demon_ strate that it is often easier to use than manipulating null terminated charac_ ter arrays yourself.
Chapter 8
Taking a First Look at C++ Pointers
In This Chapter
Addressing variables in memory
Declaring and using pointer variables Recognizing the inherent dangers of pointers Passing pointers to functions
Allocating objects off the heap (whatever that is)
So far, the C++ language has been fairly conventional compared with other programming languages. Sure, some computer languages lack (il-)logical operators (see Chapter 4 for more) and C++ has its own unique symbols, but thereÂ’s nothing new in the way of concepts. C++ really separates itself from the
crowd in definition and use of pointer variables. A pointer is a variable that
“points at” other variables. I realize that’s a circular argument, but let me put it this way: A pointer variable contains the address of a variable in memory.
This chapter introduces the pointer variable type. It begins with some con_ cept definitions, flows through pointer syntax, and then introduces some of the reasons for the pointer mania that grips the C++ programming world.
Variable Size
My weight goes up and down all the time, but here IÂ’m really referring to the size of a variable, not my own variable size. Memory is measured in bytes or bits. The following program gives you the size of the different variable types:
// VariableSize - output the size of each type of
// variable
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
110 Part II: Becoming a Functional C++ Programmer
{
bool b; char c; int n; long l; float f; double d;
cout << “sizeof a bool = “ << sizeof b << endl; cout << “sizeof a char = “ << sizeof c << endl; cout << “sizeof an int = “ << sizeof n << endl; cout << “sizeof a long = “ << sizeof l << endl; cout << “sizeof a float = “ << sizeof f << endl; cout << “sizeof a double= “ << sizeof d << endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
sizeof is actually a C++ construct that returns the size of its argument in bytes. The variable size program generates the following output:
sizeof a bool = 1 sizeof a char = 1 sizeof an int = 4 sizeof a long = 4 sizeof a float = 4 sizeof a double= 8
Press any key to continue . . .
DonÂ’t be concerned if the compiler that youÂ’re using generates different results. For example, you may find that an int is smaller than a long. C++ doesnÂ’t say exactly how big a variable type must be; it just says that a long is the same
size as or larger than an int and that a double is the same size as or larger than a float. The sizes output by the VariableSize program are typical for a
32-bit processor such as the Pentium class processors.
WhatÂ’s in an Address?
Like the saying goes: “Everyone has to be somewhere.” Every C++ variable is stored somewhere in the computer’s memory. Memory is broken into individ_ ual bytes with each byte carrying its own address numbered 0, 1, 2, and so on.
A variable intReader might be at address 0x100, whereas floatReader might be over at location 0x180. (By convention, memory addresses are expressed in hexadecimal.) Of course, intReader and floatReader might
Chapter 8: Taking a First Look at C++ Pointers 111
be somewhere else in memory entirely — only the computer knows for sure and only at the time that the program is executed.
This is somewhat analogous to a hotel. When you make your reservation, you may be assigned room 0x100. (I know that suite numbers are normally not expressed in hexadecimal, but bear with me.) Your buddy may be assigned
80 doors down in room 0x180. Each variable is assigned an address when it is created (more on that later in this chapter when we talk about scope).
Address Operators
The two pointer-related operators are shown in Table 8-1. The & operator says “tell me your hotel address,” and * says “his address is.”
Table 8-1 Pointer Operators
Operator Meaning
& (unary) The address of
* (unary) (In an expression) the thing pointed at by
* (unary) (In a declaration) pointer to
The following Layout program demonstrates how the & operator displays the layout of memory variables in memory:
// Layout - this program tries to give the
// reader an idea of the layout of
// local memory in her compiler
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
int end; int n; long l; float f; double d;
// set output to hex mode cout.setf(ios::hex); cout.unsetf(ios::dec);
112 Part II: Becoming a Functional C++ Programmer
// output the address of each variable
// in order to get an idea of how variables are
// laid out in memory
cout << “--- = 0x” << &end << “\n”; cout << “&n = 0x” << &n << “\n”; cout << “&l = 0x” << &l << “\n”; cout << “&f = 0x” << &f << “\n”; cout << “&d = 0x” << &d << “\n”;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
The program declares a set of variables. It then applies the & operator to each one to find out where it lies in memory. The results of one execution of this program with Dev-C++ appear as follows:
--- = 0x0x22ff6c
&n = 0x0x22ff68
&l = 0x0x22ff64
&f = 0x0x22ff60
&d = 0x0x22ff58
Press any key to continue . . .
Your results may vary. The absolute address of program variables depends on a lot of factors. In general, it may even vary from one execution of the pro_ gram to the next.
Notice how the variable n is exactly 4 bytes from the first variable declared
(m2). The variable l appears 4 bytes down from that. The double variable d is a full 8 bytes from its neighboring variable f. Each variable has been allo_ cated just the space needed for its type.
There is no requirement that the C++ compiler pack variables in memory with no spaces between them. Dev-C++ could have laid out the variables in memory in any other reasonable fashion.
Using Pointer Variables
A pointer variable is a variable that contains an address, usually the address of another variable. Returning to my hotel analogy for a moment, I might tell my son that I will be in room 0x100 on my trip. My son is a pointer variable of sorts. Anyone can ask him at any time, “Where’s your father staying?” and he’ll spill his guts without hesitation.
Chapter 8: Taking a First Look at C++ Pointers 113
The following pseudo-C++ demonstrates how the two address operators shown in Table 8-1 are used:
mySon = &DadsRoom; // tell mySon the address of DadÂ’s Room
room = *mySon; // “Dad’s room number is”
The following C++ code snippet shows these operators used correctly:
void fn()
{
int intVar;
int* pintVar;
pintVar = &intVar; // pintVar now points to intVar
*pintVar = 10; // stores 10 into int location
// pointed at by pintVar
}
The function fn() begins with the declaration of intVar. The next statement declares the variable pintVar to be a variable of type pointer to an int. (By the way, pintVar is pronounced pee-int-Var, not pint-Var.)
Pointer variables are declared like normal variables except for the addition of the unary * character. This * character can appear anywhere between the base type name — in this case int — and the variable name; however, it is becoming increasingly common to add the * to the end of the type.
The * character is called the asterisk character (thatÂ’s logical enough), but because asterisk is hard to say, many programmers have come to call it the splat character. Thus, they would say splat pintVar.
Many programmers adopt a naming convention in which the first character of the variable name indicates the type of the variable, such as n for int, d for double, and so on. A further aspect of this naming convention is to place a p at the beginning of a pointer variable name.
In an expression, the unary operator & means the address of. Thus, we would read the first assignment as store the address of intVar in pintVar.
To make this more concrete, assume that the memory for function fn() starts at location 0x100. In addition, assume that intVar is at address 0x102 and that pintVar is at 0x106. The layout here is simpler than the actual results from the Layout program; however, the concepts are identical.
The first assignment stores the value of & intVar (0x102) in the pointer vari_
able pintVar. The second assignment in the small program snippet says
“store 10 in the location pointed at by pintVar.” The value 10 is stored in the address contained in pintVar, which is 0x102 (the address of intVar).
114 Part II: Becoming a Functional C++ Programmer
Comparing pointers and houses
A pointer is much like a house address. Your house has a unique address. Each byte in memory has an address that is unique. A house address is made up of both numbers and letters. For example, my address is 123 Main Street.
You can store a couch in the house at 123 Main Street — you can store a number in the byte located at 0x123456. Alternatively, you can take a piece of paper and write an address — I don’t know, say, 123 Main Street. You can now store a couch at the house with the address written on the piece of paper. In fact, this is the way delivery people work — their job is to deliver a couch to the address written down on the shipping orders whether it’s 123 Main Street or not. (I’m not maligning delivery people — they have brains — it’s just that this is more or less the way things work.)
In C++, the following code snippet finds the address of myHouse and stores a couch at that houseAddress (loosely speaking):
House myHouse; House* houseAddress;
houseAddress = &myHouse;
*houseAddress = couch;
In humanspeak, you would say myHouse is a House. houseAddress is the address of a House. Assign the address of myHouse to the House pointer, houseAddress. Now store a couch at the house located at the address stored in houseAddress.
Having said all that, take a look at the int and int* version of the earlier example code snippet:
int myInt;
int* intAddress;
intAddress = &myInt;
*intAddress = 10;
That is, myInt is an int. intAddress is a pointer to an int. Assign the address of myInt to the pointer intAddress. Finally, assign 10 to the int that intAddress points to.
Using different types of pointers
Every expression has a type as well as a value. The type of the expression intVar is pointer to an integer, written as int*. Comparing this with the dec_ laration of pintVar, you see that the types match exactly:
int* pintVar = &intVar; // both sides of the assignment are
// of type int*
Chapter 8: Taking a First Look at C++ Pointers 115
Similarly, because pintVar is of type int*, the type of *pintVar is int:
*pintVar = 10; // both sides of the assignment are
// of type int
The type of the thing pointed to by pintVar is int. This is equivalent to saying that, if houseAddress is the address of a house, the thing pointed at by houseAddress must be a house. Amazing, but true.
Pointers to other types of variables are expressed the same way:
double doubleVar;
double* pdoubleVar = &doubleVar;
*pdoubleVar = 10.0;
A pointer on a Pentium class machine takes 4 bytes no matter what it points to. That is, an address on a Pentium is 4 bytes long, period.
Matching pointer types is extremely important. Consider what might happen if the following were allowed:
int n1;
int* pintVar;
pintVar = &n1;
*pintVar = 100.0;
The second assignment attempts to store the 8-byte double value 100.0 into the 4-byte space allocated for n1. Actually, this isn’t as bad as it looks — C++ is smart enough to demote the constant 100.0 to an int before making the assignment.
It is possible to cast one type of variable into another:
int iVar;
double dVar = 10.0;
iVar = (int)dVar;
Similarly, it is possible to cast one pointer type into another.
int* piVar;
double dVar = 10.0;
double* pdVar;
piVar = (int*)pdVar;
Consider, however, what catastrophes can arise if this type of casting about of pointers were to get loose. Save a variable into an area of the wrong size, and nearby variables can be wiped out. This is demonstrated graphically in the following LayoutError program.
116 Part II: Becoming a Functional C++ Programmer
// LayoutError - demonstrate the results of
// a messing up a pointer usage
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int nNumberofArgs, char* pszArgs[])
{
int upper = 0; int n = 0; int lower = 0;
// output the values of the three variables before... cout << “the initial values are” << endl;
cout << “upper = “ << upper << endl; cout << “n = “ << n << endl; cout << “lower = “ << lower << endl;
// now store a double into the space
// allocated for an int
cout << “\nStoring 13.0 into the location &n” << endl;
double* pD = (double*)&n;
*pD = 13.0;
// display the results
cout << “\nThe final results are:” << endl;
cout << “upper = “ << upper << endl; cout << “n = “ << n << endl; cout << “lower = “ << lower << endl;
// wait until user is ready before terminating program
// to allow the user to see the program results system(“PAUSE”);
return 0;
}
The first three lines in main() declare three integers in the normal fashion. The assumption made here is that these three variables are laid out next to each other.
The next three executable lines output the value of the three variables. Not surprisingly, all three variables display as 0. The assignment *pD = 13.0; stores the double value 13.0 in the integer variable n. The three output state_ ments display the values of all three variables after the assignment.
After assigning the double value 13.0 in the integer variable n, n is not modi_ fied at all; however, the nearby variable upper is filled with a garbage value. This is not good, as the output from the program shows:
the initial values are
upper = 0
Chapter 8: Taking a First Look at C++ Pointers 117
n = 0
lower = 0
Storing 13.0 into the location &n
The final results are:
upper = 1076494336
n = 0
lower = 0
Press any key to continue . . .
The house equivalent goes something like this:
House* houseAddress = &”123 Main Street”; Hotel* hotelAddress;
hotelAddress = (Hotel*)houseAddress;
*hotelAddress = TheRitz;
houseAddress is initialized to point to my house. The variable hotelAddress is a pointer to a hotel. Now, the house address is cast into the address of a hotel and saved off into the variable hotelAddress. Finally, TheRitz is plopped down on top of my house. Because TheRitz is slightly bigger than my house (okay, a lot bigger than my house), it isnÂ’t surprising that TheRitz wipes out my neighborsÂ’ houses as well.
The type of pointer saves the programmer from stuffing an object into a space that is too big or too small. The assignment *pintVar = 100.0; actu_ ally causes no problem — because C++ knows that pintVar points to an int, C++ knows to demote the 100.0 to an int before making the assignment.
Passing Pointers to Functions
One of the uses of pointer variables is in passing arguments to functions. To understand why this is important, you need to understand how arguments are passed to a function.
Passing by value
You may have noticed that it is not normally possible to change the value of a variable passed to a function from within the function. Consider the following example code segment:
118 Part II: Becoming a Functional C++ Programmer
void fn(int intArg)
{
intArg = 10;
// value of intArg at this point is 10
}
void parent(void)
{
int n1 = 0;
fn(n1);
// value of n1 at this point is 0
}
Here the parent() function initializes the integer variable n1 to zero. The value of n1 is then passed to fn(). Upon entering the function, intArg is equal to 10, the value passed. fn() changes the value of intArg before returning to parent(). Perhaps surprisingly, upon returning to parent(), the value of n1 is still 0.
The reason is that C++ doesnÂ’t pass a variable to a function. Instead, C++ passes the value contained in the variable at the time of the call. That is, the expression is evaluated, even if it is just a variable name, and the result is passed.
It is easy for a speaker to get lazy and say something like, “Pass the variable x
to the function fn().” This really means to pass the value of the expression x.
Passing pointer values
Like any other intrinsic type, a pointer may be passed as an argument to a function:
void fn(int* pintArg)
{
*pintArg = 10;
}
void parent(void)
{
int n = 0;
fn(&n); // this passes the address of i
// now the value of n is 10
}
In this case, the address of n is passed to the function fn() rather than the value of n. The significance of this difference is apparent when you consider the assignment within fn().