This is where we actually start doing something with the data that we enter. We are beginning the 'processing' part of a C++ program. Almost all of the computational aspects of C++ are fairly intuitive, so we should slice through this like a hot knife through butter.
The Assignment Statement
We have already seen examples of the assignment statement. A typical assignment statement would look like this:whole = part1 + part2 + part3;
The assignment statement enables you to calculate the value of an expression which appears on the right hand side of the equals sign (in this case the sum of part1, part2 and part3) and store the result in the variable specified on the left hand side (whole). In this statement, the whole is exactly the sum of its parts, and no more.
Note how the statement, as always, ends with a semicolon.
You can also write repeated assignments such as,A = B = 1;
where this is equivalent to assigning the value 1 to B, then assigning the value of B to A.
Understanding Lvalues
The simplest definition of an lvalue is something that has an address in memory. Usually this is something that can appear on the left hand side of an equals sign in an assignment, although this is not necessarily the case. Variables are lvalues, since they specify a place in memory. However, there are lvalues that can't appear on the left of an assignment, because their values have been defined as constant. We'll be looking at such variables a bit later in this chapter. The variables A and B appearing in the preceding paragraph are lvalues, whereas the expression A+B would not be, since its result doesn't determine an address in memory where a value might be stored.
Arithmetic Operations
The basic arithmetic operators we have at our disposal are addition, subtraction, multiplication and division, represented by the symbols +, -, * and / respectively. These operate generally as you would expect, with the exception of division, which has a slight aberration when working with integer variables or constants, as we'll see. You can write statements like this:netPay = hours * rate - deductions;
Here, the product of hours and rate will be calculated, then deductions subtracted from the value produced. The multiply and divide operators are executed before addition and subtraction. We will discuss the order of execution more fully later in this chapter. The overall result of the expression will be stored in the variable netPay.
The minus sign used in the last statement applies to two operands - it subtracts one from another. This is called a binary operation because two values are involved. The minus sign can also be used with one operand to change the sign of its value, in which case it is called a unary minus. You could write this:int A = 0;
int B = -5;
A = -B; // Changes the sign of the operand
Here, A will be assigned the value +5, because the unary minus changes the sign of the value of the operand B.
Note that an assignment is not the equivalent of the equations you saw in high school algebra. It specifies an action to be carried out rather than a statement of fact. The statement,A = A + 1;
means, 'add 1 to the current value stored in A and then store the result back in A'. As a normal algebraic statement it wouldn't make sense.
Try It Out - Exercising Basic Arithmetic
We can exercise basic arithmetic in C++ by calculating how many standard rolls of wallpaper are needed to paper a room. This is done with the following example:// EX1_04.CPP
// Calculating how many rolls of wallpaper are required for a room
#include
using namespace std;
int main()
{
double height = 0.0, width = 0.0, length = 0.0; // Room dimensions
double perimeter = 0.0; // Room perimeter
const double rollwidth = 21.0; // Standard roll width
const double rolllength = 12.*33.; // Standard roll length(33ft.)
int strips_per_roll = 0; // Number of strips in a roll
int strips_reqd = 0; // Number of strips needed
int nrolls = 0; // Total number of rolls
cout << endl // Start a new line
<< "Enter the height of the room in inches: ";
cin >> height;
cout << endl // Start a new line
<< "Now enter the length and width in inches: ";
cin >> length >> width;
strips_per_roll = rolllength/height; // Get number of strips in a roll
perimeter = 2.0*(length + width); // Calculate room perimeter
strips_reqd = perimeter/rollwidth; // Get total strips required
nrolls = strips_reqd/strips_per_roll; // Calculate number of rolls
cout << endl
<< "For your room you need " << nrolls << " rolls of wallpaper."
<< endl;
return 0;
}
How It Works
One thing needs to be clear at the outset. No responsibility is assumed for you running out of wallpaper as a result of using this program! As we shall see, all errors in the estimate of the number of rolls required are due to the way C++ works and to the wastage that inevitably occurs when you hang your own wallpaper - usually 50%+!
We can work through the statements in this example in sequence, picking out the interesting, novel, or even exciting features. The statements down to the start of the body of main() are familiar territory by now, so we will take those for granted.
A couple of general points worth noting are about the layout of the program. First, the statements in the body of main() are indented to make the extent of the body easier to see and, second, various groups of statements are separated by a blank line to indicate that they are functional groups. The compiler just ignores such whitespace, but indenting statements and leaving some lines blank is a fundamental technique in laying out program code in C++. You will see that this is applied universally to provide visual cues for various logical blocks in a program.
The const Modifier
We have a block of declarations for the variables used in the program right at the beginning of the body of main(). These statements are also fairly familiar, but there are two which contain some new features:const double rollwidth =21.0; // Standard roll width
const double rolllength = 12.*33.; // Standard roll length(33ft.)
They both start out with a new keyword: const. This is a type modifier which indicates that the variables are not just of type double, but are also constants. Because we effectively tell the compiler that these are constants, the compiler will check for any statements which attempt to change their values and, if it finds any, it will generate an error message. This is relatively easy since a variable declared as const cannot legally be placed on the left of an assignment operation.
You could check this out by adding, anywhere after the declaration of rollwidth, a statement such as:rollwidth = 0;
You will find the program no longer compiles.
It can be very useful defining constants by means of const variable types, particularly when you use the same constant several times in a program. For one thing, if you need to change it, you will only need to change its definition at the beginning to ensure that the change automatically appears throughout. We'll see this technique used quite often.
Constant Expressions
The const variable rolllength is also initialized with an arithmetic expression (12.*33.). Being able to use constant expressions to initialize variables saves having to work out the value yourself, and can also be more meaningful, as 33 feet times 12 inches is much clearer than simply writing 396. The compiler will generally evaluate constant expressions accurately, whereas if you do it yourself, depending on the complexity of the expression and your ability to number-crunch, there is a finite probability that it may be wrong.
You can use any expression that can be calculated as a constant at compile time, including const objects that you have already defined. So, for instance, if it was useful in the program to do so, we could declare the area of a standard roll of wallpaper as:const double rollarea = rollwidth*rolllength;
Obviously, this statement would need to be placed after the declarations for the two const variables used in the initialization of rollarea.
Program Input
The next four statements in the program handle input:cout << endl // Start a new line
<< "Enter the height of the room in inches: ";
cin >> height;
cout << endl // Start a new line
<< "Now enter the length and width in inches: ";
cin >> length >> width;
Here we have used cout to prompt for the input required and then read the input from the keyboard using cin. We first obtain the room height and then read the length and width successively. In a practical program, we would need to check for errors, and possibly make sure that the values that are read are sensible, but we don't have enough knowledge to do that yet!
Calculating the Result
We have four statements involved in calculating the number of standard rolls of wallpaper required for the size of room given:strips_per_roll = rolllength/height; // Get number of strips in a roll
perimeter = 2.0*(length + width); // Calculate room perimeter
strips_reqd = perimeter/rollwidth; // Get total strips required
nrolls = strips_reqd/strips_per_roll; // Calculate number of rolls
The first statement calculates the number of strips of paper with a length corresponding to the height of the room that we can get from a standard roll, by dividing one into the other. So, if the room is 8 feet high, we divide 96 into 396, which would produce the floating point result 4.125. There is a subtlety here, however. The variable where we store the result, strips_per_roll, was declared as int, so it can only store integer values. Consequently, any floating point value to be stored as an integer is rounded down to the nearest integer, 4 in our case, and this value is stored. This is actually the result that you want here since, although they may fit under a window or over a door, fractions of a strip are best ignored when estimating.
The conversion of a value from one type to another is called casting. This particular example is called an implicit cast, because the code doesn't explicitly state that a cast is needed, and the compiler has to work it out for itself. You should beware when using implicit casts. The compiler does not supply a warning that an implicit cast is being made, and if you are assigning a value of one type to a variable of a type with a lesser range of values, then there is a danger that you will lose information. If there are implicit casts in your program which you don't know about, then they will almost certainly form bugs which are difficult to locate.
Where such an assignment is unavoidable, you can specify the conversion explicitly to demonstrate that it is no accident and that you really meant to do it. We do this by making an explicit cast of the value on the right of the assignment to int, so the statement would become:strips_per_roll = static_cast
The addition of static_cast
Note how we calculate the perimeter of the room in the next statement. In order to multiply the sum of the length and the width by two, we enclose the sum of the two variables between parentheses. This ensures that the addition is performed first and the result is then multiplied by 2.0 to give us the correct value for the perimeter. We can use parentheses to make sure that a calculation is carried out in the order we require, since expressions in parentheses are always evaluated first. Where there are nested parentheses, the expressions within the parentheses are evaluated in sequence, from the innermost to the outermost.
The third statement, calculating how many strips of paper are required to cover the room, uses the same effect that we observed in the first statement: the result is rounded down to the nearest integer, because it is to be stored in the integer variable, strips_reqd. This is not what we need in practice. It would be best to round up for estimating, but we don't have enough knowledge of C++ to do this yet. Once you have read the next chapter, you can come back and fix it!
The last arithmetic statement calculates the number of rolls required by dividing the number of strips required (integer) by the number of strips in a roll (also integer). Because we are dividing one integer by another, the result has to be integer, so any remainder is ignored. This would still be the case if the variable nrolls were floating point. The resulting integer value would be converted to floating point form before it was stored in nrolls. The result that we obtain is essentially the same as if we had produced a floating point result and rounded down to the nearest integer. Again, this is not what we want, so if you want to use this, you will need to fix it.
Displaying the Result
The result of the calculation is displayed by the following statement:cout << endl
<< "For your room you need " << nrolls << " rolls of wallpaper."
<< endl;
This is a single output statement spread over three lines. It first outputs a newline character, then the text string "For your room you need ". This is followed by the value of the variable, nrolls, and finally the text string, " rolls of wallpaper.". As you can see, output statements are very easy in C++.
Finally, the program ends when this statement is executed: return 0;
The value zero here is a return value which, in this case, will be returned to the operating system.
Calculating a Remainder
We have seen in the last example that dividing one integer value by another produces an integer result that ignores any remainder, so if you take 11 divided by 4, then you get the result 2. Since the remainder after division can be of great interest, particularly when you are dividing cookies amongst children, for example, C++ provides a special operator, %, for this. So we can write the statements,int residue = 0, cookies = 19, children = 5;
residue = cookies % children;
and the variable residue will end up with the value 4 - the number left after dividing 19 by 5. To calculate how many each of them received, you just need to use division, as in the statement:each = cookies / children;
This statements says that the result of the division cookies/children, 3, will be stored in the variable each.
Modifying a Variable
It's often necessary to modify the existing value of a variable, such as incrementing it or doubling it. We could increment a variable called count using the statement:count = count + 5;
This simply adds 5 to the current value stored in count, and stores the result back in count, so if count started out at 10, it would end up as 15. You have an alternative, shorthand way of writing the same thing in C++:count += 5;
This says, 'Take the value in count, add 5 to it and store the result back in count'. We can also use other operators with this notation. For example, the statement,count *= 5;
has the effect of multiplying the current value of count by 5 and storing the result back in count. In general, we can write statements of the form,lhs op= rhs;
where op is any of the following operators: + - * / %
<< >> & ^
The first five of these we have already met, and the remainder, which are shift and logical operators, we will see later in this chapter. lhs stands for any legal expression for the left-hand side of the statement, and is usually (but not necessarily) a variable name. rhs stands for any legal expression on the right-hand side of the statement.
The general form of the statement is equivalent to this:lhs = lhs op (rhs);
This means that we can write statements such as,A /= B + C;
which will be identical in effect to,A = A/(B + C);
The Increment and Decrement Operators
We will now take a brief look at some unusual arithmetic operators called the increment and decrement operators, as we will find them to be quite an asset once we get further into applying C++ in earnest. These are unary operators, which are used to increment or decrement a variable. For example, assuming the variable count is of type int, the following three statements all have exactly the same effect:count = count + 1;
count += 1;
++count;
They each increment the variable count by 1. The last form, using the increment operator, is clearly the most concise. If this action is contained within another expression then the action of the operator is to first increment the value of the variable, and then use the incremented value in the expression. For example, if count has the value 5, and the variable total is of type int, then the statement,total = ++count + 6;
will result in count being incremented to 6, while total is then assigned the value 12.
So far, we have written the increment operator, ++, in front of the variable to which it applies. This is called the prefix form. The increment operator also has a postfix form, where the operator is written after the variable to which it applies; the effect of this is slightly different. The variable to which the operator applies is only incremented after its value has been used in context. For example, let's reset count to the value 5, and rewrite the previous example as:total = count++ + 6;
Here, total is assigned the value 11, since the initial value of count is used to evaluate the expression before it is incremented by 1. The statement above is equivalent to the two statements:total = count + 6;
++count;
The clustering of '+' signs, in the example of the postfix form above, is likely to lead to confusion. Generally, it isn't a good idea to write the increment operator in the way that we have here. It would be clearer to write:total = 6 + count++;
Where we have an expression such as a++ + b, or even a+++b, it becomes less obvious what is meant or what the compiler will do. They are actually the same, but in the second case you might really have meant a + ++b, which is different - it evaluates to one more than the other two expressions.
Exactly the same rules that we have discussed in relation to the increment operator also apply to the decrement operator, --. For example, if count has the initial value 5, then the statement,total = --count + 6;
results in total having the value 10 assigned, whereas,total = 6 + count--;
sets the value of total to 11. Both operators are usually applied to integers, particularly in the context of loops, as we shall see in the next chapter. We shall also see in later chapters that they can be applied to other data types in C++.
Try It Out - The Comma Operator
The comma operator allows you to specify several expressions where normally only one might occur. This is best understood by looking at an example of how it is used:// EX1_05.CPP
// Exercising the comma operator
#include
using namespace std;
int main()
{
long num1 = 0, num2 = 0, num3 = 0, num4 = 0;
num4 = (num1 = 10, num2 = 20, num3 = 30);
cout << endl
<< "The value of a series of expressions "
<< "is the value of the right most: "
<< num4;
cout << endl;
return 0;
}
How It Works
If you compile and run this program you will get this output:
This is fairly self-explanatory. The variable num4 receives the value of the last of the series of three assignments, the value of an assignment being the value assigned to the left-hand side. The parentheses in the assignment for num4 are essential. You could try executing this without them to see the effect. Without the parentheses, the first expression separated by commas in the series will become:num4 = num1 = 10
So, num4 will have the value 10.
Of course, the expressions separated by the comma operator don't have to be assignments. We could equally well write the following:long num1 = 1, num2 = 10, num3 = 100, num4 = 0;
num4 = (++num1, ++num2, ++num3);
The effect of this assignment would be to increment the variables num1, num2 and num3 by 1, and to set num4 to the value of the last expression which will be 101.
This example is aimed at illustrating the effect of the comma operator, and is not an example of how to write good code. As you can see, writing an assignment statement in this fashion can be very misleading.
The Sequence of Calculation
So far, we haven't talked about how we arrive at the sequence of calculations involved in evaluating an expression. It generally corresponds to what you will have learnt at school when dealing with basic arithmetic operators, but there are many other operators in C++. To understand what happens with these, we need to look at the mechanism used in C++ to determine this sequence. It's referred to as operator precedence.
Operator Precedence
Operator precedence orders the operators in a priority sequence. In any expression, operators with the highest precedence are always executed first, followed by operators with the next highest precedence, and so on, down to those with the lowest precedence of all. A list of all the C++ operators in order of their precedence can be found in the online help in Visual C++ Documentation \ Reference \ C/C++ Language and C++ Libraries \ C++ Language Reference \ Lexical Conventions \ C++ Operators.
If there are no parentheses in an expression, operators of equal precedence are executed in a sequence determined by their associativity. Thus, if the associativity is 'left to right', the left-most operator in an expression is executed first, progressing through the expression to the right-most.
Note that where an operator has a unary (working with one operand) and a binary (working with two operands) form, the unary form is always of a higher precedence and is, therefore, executed first.
You can always override the precedence of operators by using parentheses. Since there are so many operators in C++, it's sometimes hard to be sure what takes precedence over what. It is a good idea to insert parentheses to make sure. A further plus is that parentheses often make the code much easier to read.
No comments:
Post a Comment