Previous Page TOC Next Page



- 9 -
Special Operations


typecasting


What You'll Learn About


Although you now understand a large number of Visual C++ operators, there are a few left to cover. You won't learn about all the remaining operators in this unit because some of them work only when you write advanced programs (which you will do in this book, but not until Lesson 8 and beyond).

This unit explores several operators that help you change data from one data type to another. Visual C++ prefers to work with similar data types, but you can't always ensure that your data will have the same type. Hence, you must use the type-changing operators discussed here to convert between types.

The remainder of this unit shows you how to use some shortcut operators. In Lesson 4, you saw how the compound assignment operators ease your programming burden. Visual C++ is the only major programming language with compound assignment operators. Visual C++ also provides you with additional operators that—although you could write programs without having them—do save you a lot of time. You'll want to use them as much as possible.

Combining Data Types




If you must mix data types, you can first convert all the data to the same type so that your expressions operate on similar values.

There's no way to write a program that does much work without mixing data types in some way. Data values come in all shapes, sizes, and forms, and you must be able to work with combinations of data. Visual C++ converts from one data type to another in the following two ways:

The following sections explain how each method works.

Automatic Promotion


definition

The promotion of one data type to another occurs automatically when you mix types in an expression.

Visual C++ does its best to help you when you mix one data type with another, such as when you add a floating-point to an integer. Much of the time, Visual C++ assumes that you want the smaller data type converted to the larger one before it evaluates any expression. This automatic promotion of data means that you don't always have to be on guard, watching each and every constant and variable in each and every expression that you write.

Visual C++ often converts one data type to another. Given the variable definitions


int i=7;

float f=12.3;

you can add, subtract, or combine the data types in virtually any order without worrying about the results. Visual C++ automatically promotes the integer to a floating-point value before carrying out the evaluation. Therefore, the expression


ans = f + i;   // Visual C++ converts both to float

adds 12.3 to 7.0 (notice the decimal), resulting in 19.3, which is stored in ans. For now, assume that ans is a floating-point variable that you've defined.



i is changed to a floating-point only for that single expression. In reality, the compiler doesn't change i. Instead, it grabs the integer value out of i and then converts that value to a float before doing the math. i never changes from an integer variable that holds a 7.

The promotion of the smaller data type to the larger one provides for as much accuracy as possible. Although you're about to learn a way to promote down—for example, from a floating-point to an integer—you almost always want as much precision retained as possible.

Look at Table 9.1 to see how Visual C++ promotes from each of the smaller data types to the larger ones.

Table 9.1. The automatic data-type promotions.

Source Data Type What It Promotes To
char int or the largest data type if int is not the largest data type in the expression
short int or the largest data type if int is not the largest data type in the expression
unsigned short unsigned int or the largest data type if unsigned int is not the largest data type in the expression
float The larger of double or long double, depending on which appears in the expression
double long double if a long double appears in the expression

In Lesson 3, you saw how to use the data-type suffix characters such as L and U to specify numeric literals that you want Visual C++ to treat as specific data types. Visual C++ treats all numeric literals such as 2, -0.00002, and 123.45 as floating-point literals, except in these two cases:

definition

To truncate means to cut off or eliminate part of a number, such as the decimal portion.

The only problem that can arise is when you attempt to store a higher precision value in a smaller precision variable. For example, if you want to put a double floating-point value that contains fractional digits in an integer variable, you already know that the integer can't hold the fractional portion. Therefore, Visual C++ truncates the decimal portion, converting the number to an integer.

In the following code, three variables are defined—two integers and a floating-point value. When Visual C++ multiplies the integer by the floating-point value, it converts the integer to a floating-point by changing the value of i to 8.000000. Visual C++ then multiplies the 8.000000 by f to get 55.2. However, the variable that is to hold the resulting 55.2 is an integer variable, and an integer variable can't hold the .2. Therefore, Visual C++ truncates the fractional portion and stores 55 in result. 55 is approximately equal to the correct answer, but .2 is a large enough truncation to be aware of.


int i = 8;

int result;

float f = 6.9;

result = i * f;   // Oops! Puts only 55 in result

The order of promotion does affect calculated results!

Would result have held a different result if Visual C++ had first converted f to an integer before multiplying? Think this through. You will see that result would have held an entirely different value. Visual C++ would have multiplied 8 by 6 and stored only a 48 in result. As you can see, it's vital that you understand Visual C++'s promotion so that you can mix and match expressions and be able to predict how Visual C++ will handle the math.

Mixing data types is fairly common. You might need to multiply the number of products sold by a price to get a total price. As a matter of fact, several programs so far in this book have mixed integers and floating-point values, such as multiplying the hours worked times a pay rate. The next section explains an important operator in Visual C++ that helps with type changes—the typecast operator.

The Typecast Operator


definition

A typecast changes one data type to another.

The typecast operator is one of the strangest-looking operators in Visual C++. Unlike most of the other operators, the typecast operator doesn't use a traditional symbol such as * or %. Here are some of the typecast operators:

(double)

As you can see, there is a different typecast operator for every kind of data type in Visual C++. The data type must appear in parentheses. By using typecasting, you can specify exactly when and where you want one data type converted to another.



In Visual C++, you can define your own data types. You'll see how in Lesson 9. You can typecast using your own data types as well as the built-in data types.

To typecast one value's data type to another data type, place the typecast operator right before the value (either a variable, literal, or expression). In the previous expression, you saw how Visual C++ combined an integer and a floating-point value. If you would like to convert the floating-point value to an integer, you can place an integer typecast operator right before the floating-point variable:


int i = 8;

int result;

float f = 6.9;

result = i * (int)f;   // Convert f before multiplying

f becomes the integer 6. Without the typecast, Visual C++ multiplies 8.0 by 6.9 to get a result of 55.2 before storing 55 in the integer variable. With the typecast, Visual C++ multiplies 8 by 6 to get 48 and then stores 48 in the variable named result.

Notice that Visual C++ doesn't round floating-points to the closest integer. It merely truncates the fractional portion when converting from a floating-point value to an integer. If you want to round a floating-point to an integer, you can use a built-in operator, which you'll learn about in Lesson 12.

Recall the problem of integer division giving a whole number answer even if a fractional answer was required. You might now see that the solution to force a real division is to use a typecast:


int people,events;

float avgEv;

avgEv = (float)people / events;

Now C++ will provide a floating-point division, and as long as the result can hold the precision, a more accurate result will be given.

Can you see that the following typecast is redundant and adds nothing to what Visual C++ would do without the typecast?


result = (float)i * f;   // Visual C++ first converts i to float anyway


A typecast doesn't change a value's data type permanently. The value is changed for the location of the typecast only.

If you need to, you also can typecast the result of an entire expression:


ans = a + b * (long int)(e / y * 2);

In this assignment statement, Visual C++ computes the result of e / y * 2 and then converts that result to a long integer. Most of the time, you can let Visual C++ handle the automatic typecasting for you through its promotion rules, which you read about earlier. However, typecasting gives you exact control over data-type changes when you need those changes. There are specific times in Visual C++ when you will have to typecast. One of the most important times is when you dynamically allocate data in Lesson 10.

The designers of C++ wanted to base C++ on the C language. In C++, you can write your typecasts using function-call notation. Instead of putting the data type in parentheses, you can put the converted value in parentheses. Therefore, you can write the statements


result = i * (int)f;     // Convert f before multiplying

result = (float)i * f;   // Visual C++ first converts i to float anyway

ans = a + b * (long int)(e / y * 2);

like this:


result = i * int;     // Convert f before multiplying

result = float(i * f);   // Visual C++ first converts i to float anyway

ans = a + b * long int(e / y * 2);

The new C++-only notation makes the typecasts look a little less strange. Also, when you move to advanced Visual C++ using object-oriented programming, the new notation is required to change the behavior of objects to related types.

The program in Listing 9.1 contains one expression that is typecast several times in different ways. As you will see from the output, the location of the typecast, Visual C++'s automatic promotion of data types, and the resulting variable's data type all play parts in how Visual C++ evaluates expressions.



Use typecasting operators when you want to control exactly where one type changes to another type.

Input Listing 9.1. Using typecast operators.

  1:// Filename: TYPECAST.CPP

  2:// Applies the typecast operator to a single expression

  3:// in several different ways to show the different results

  4:#include <iostream.h>

  5:void main()

  6: {

  7:   double answer;   // Make variable large to hold any precision

  8:   int i = 9;

  9:   float f = 7.8;

 10:   double d = 16.4;

 11:

 12:   // Apply the typecast in several ways

 13:   answer = (i * f * d);

 14:   cout << "The answer after (i * f * d) is "

 15:        << answer << endl;

 16:

 17:   answer = float(i * f * d);   // The typecast

 18:   cout << "The answer after float(i * f * d) is "

 19:        << answer << endl;

 20:

 21:   answer = (i * int * int);

 22:   cout << "The answer after (i * int * int) is "

 23:        << answer << endl;

 24:

 25:   answer = int(i * f * d);

 26:   cout << "The answer after int(i * f * d) is "

 27:        << answer << endl;

 28:

 29:   answer = float(i * f * d);

 30:   cout << "The answer after float(i * f * d) is "

 31:        << answer << endl;

 32:

 33:   return;

 34: }

Output


The answer after (i * f * d) is 1151.280028

The answer after ((float)i * f * d) is 1151.280028

The answer after (i * (int)f * (int)d) is 1008

The answer after (int)(i * f * d) is 1151

The answer after float(i * f * d) is 1151.280028

Analysis

The movement of the typecast sometimes changes the answer a little (as shown in the loss of precision in the fourth line of the output, which results from the calculation in line 25) or a lot (as shown in the third line of the output, which results from the calculation in line 21).

When Visual C++ calculates the expression without any typecasting on your part (line 13), that answer actually is the most accurate. However, the answer Visual C++ produces might not always be the most accurate, especially if you store the answer in an integer variable. When storing a double floating-point calculation in an integer variable (which you might need to do in some mathematical computations or financial interest rate period calculations), you'll want to control exactly where the precision gets lost.

Be sure that you understand the difference between the third and fourth lines of output. At first, it appears that both calculations are the same because all the multiplied values get converted to integers before being stored in the double floating-point variable. The difference lies in when they are converted to integer values. In the fourth line of the output, the parentheses contain a double floating-point calculation because Visual C++ promotes the integer and the floating-point value inside the parentheses to double floating-point. This is done because double floating-point is the highest precision inside the parentheses. Only after the double floating-point calculation finishes does the typecast change the result to integer, which means truncating only the fractional portion from the result.

In the third line of the output, the floating-point and the double floating-point variables are first typecast to integers. Only after all three values are integers does the multiplication occur. The integer multiplication produces a much smaller result than the same multiplication when three double floating-point values let the fractional portions of f and d stay in the calculation.

Having fun? Typecasting and data-type conversions aren't always the most exciting components of Visual C++, but your current understanding is worth the time that you spent. As you progress in Visual C++, you'll see why typecasting can become very important as you write longer programs.



The Fractional Portion

By using typecasts, you can store only the fractional portion of a floating-point value if you need to. It's easy to find the whole-number portion of a floating-point number, because you only have to store the value in an integer or long integer variable or typecast the value to an integer.

A simple subtraction does the trick of storing only the fractional portion. Suppose you want to store the fractional part of fract in a variable named rightSide. Here is how to do just that:



rightSide = fract - int(fract);

Suppose fract holds 33.456. Subtracting 33 (the integer portion on the right of the minus sign) from 33.456 (the value on the left of the minus sign) results in the fraction 0.456, which goes into rightSide.

Remember that despite its powerful operator capabilities, Visual C++ isn't great at storing large precision values with extreme accuracy (very few programming languages are, except for those dedicated to such purposes). Therefore, if you think that 0.456 is going into a variable, don't be surprised if Visual C++ stores 0.045601 in the variable. You don't have to print the full precision, and besides, 0.000001 is not very big at all. (However, a little 0.000001 here and a little 0.000001 there, and you'll wind up with 0.000002 in no time!)

The sizeof Operator




The sizeof operator determines how much memory is needed to hold a value.

Visual C++ has a second operator that looks a lot like the typecast operator. Instead of using a symbol, as most of the other operators do, the sizeof operator uses parentheses. Here is the general format of sizeof:


sizeof(dataValue)

sizeof() looks a lot like a function call because of the parentheses. However, sizeof is an operator because it works on built-in data-type names as well as data that you define. Here are some sample sizeof expressions:


numBytes = sizeof(int);   // Store the number of bytes an integer takes


storage1 = sizeof(myName); // The amount of memory taken by a string
storage2 = "A string"; // Put 9 in storage2

sizeof() always returns the total amount of memory it takes to hold whatever value you put in the sizeof parentheses.



Actually, the parentheses are optional if you pass a variable and not a built-in data type to sizeof, but sizeof is easier to read when you include the parentheses. All examples in this book that use sizeof() include the parentheses.

Don't use sizeof to find the length of a string. Use the strlen() function if you need to find the length of a string. (You will learn more about how functions work in Lesson 7.) By definition, sizeof returns the number of bytes it takes to hold whatever value you pass it. You'll remember from Lesson 3 that Visual C++ always needs a terminator byte to hold any string.

sizeof does not work with arrays in all circumstances. The main exception will be highlighted in Lesson 8 when we look at functions.

Different Visual C++ compilers store data in different ways. Visual C++'s sizeof operator might very well return values that differ from those of other Visual C++ compilers, depending on the data type and the type of machine running the program at the time.

Listing 9.2 contains a program that prints the amount of storage it takes to store several built-in data types, variables, and literals in memory using Visual C++.



The sizeof operator lets you find the storage requirements of your program's target machine. sizeof returns the number of bytes of storage needed to hold the value that you pass to it.

Input Listing 9.2. Use sizeof to return several values.

  1: // Filename: SIZEOF.CPP

  2: // The sizeof operator always returns the amount of memory

  3: // (the number of bytes) that it takes to store a value

  4: #include <iostream.h>

  5: void main()

  6: {

  7:   char c = 'x';

  8:   char name[] = "Italy";

  9:   int i = 29;

 10:   float f = 6.7643;

 11:   double d = 9493945.6656543;

 12:

 13:   // Print the sizes of the data

 14:   // Typecast sizeof to an integer because sizeof returns its

 15:   // value as a long integer and this program prints with %d only

 16:   cout << "The sizes of variables:" << endl;

 17:   cout << "The size of c is " << int(sizeof) << endl;

 18:   cout << "The size of name is " << int(sizeof(name)) << endl;

 19:   cout << "(See, that was not the length of the string!)" << endl;

 20:   cout << "The size of i is " << int(sizeof(i)) << endl;

 21:   cout << "The size of f is " << int(sizeof) << endl;

 22:   cout << "The size of d is " << int(sizeof) << endl;

 23:   cout << endl << "The sizes of literals:" << endl;

 24:   cout << "The size of 4.3445 is " << int(sizeof(4.3445))

 25:        << endl;

 26:   cout << "The size of Hello is " << int(sizeof("Hello"))

 27:        << endl;

 28:   cout << "The sizes of data types:" << endl;

 29:   cout << "The size of a long double is "

 30:        << int(sizeof(long double)) << endl;

 31:   cout << "The size of a float is " << int(sizeof(float));

 32:   return;

 33: }

Output


The sizes of variables:

The size of c is 1

The size of name is 6

 (See, that was not the length of the string!)

The size of i is 2

The size of f is 4

The size of d is 8

The sizes of literals:

The size of 4.3445 is 8

The size of Hello is 6

The sizes of data types:

The size of a long double is 10

The size of a float is 4

Analysis

This program is divided into three sections that print these values:

If sizeof were a function instead of a built-in operator, it couldn't return the sizes of data types. Notice that each cout typecasts the sizeof operator to an integer because sizeof normally returns a special internally defined data type named size_t unless you typecast its return value.

sizeof returns the number of bytes that it takes to hold data. name's string has a size of five but sizeof returns six in line 18 because name requires six bytes with the terminator.



When you learn how to create your own data types in Lesson 9, you'll see that sizeof works with those data types as well. (sizeof is really smart!)


Using the Conditional Operator


definition

A ternary operator works on three values (called operands).



You can exchange simple multiline if-else code for a single operator—the conditional operator.

The conditional operator does a lot of work. In fact, it's the only ternary operator in the Visual C++ language. The other operators are either unary or binary and work on either single values or two values at a time, as you've seen.

When discussing the conditional operator in general, this book shows it like this: ?:. However, in programs, there is always a value between the ? and the :. Before looking at an example of the conditional operator, you should see its format:


relationalTest ? trueCode : falseCode;


Often, Visual C++ programmers put parentheses around each of the conditional operator's values, as you'll see in a moment.

The relational test is any Visual C++ expression that uses a relational operator—for example, any relational expression that might appear in an if statement. The relational test can include one or more logical operators as well if you want to combine several relational tests into a single test. If and only if the relational test evaluates to a true (nonzero) condition, the trueCode executes. If the relational test evaluates to a false (zero) condition, the falseCode executes.

Use the conditional operator in place of simple if-else statements such as the following:


if (a > b)

  { c = 17; }

else

  { c = 23; }

In this if, the relational test is a > b, the trueCode is c = 17; and the falseCode is c = 23;. Here is the very same logic expressed as a single conditional statement:


 (a > b) ? (c = 17) : (c = 23);

Notice how the parentheses help distinguish the three parts of the conditional operator. The question mark helps the statement read as follows:

"Is a greater than b? If so, put 17 in c. Otherwise, put 23 in c."

Figure 9.1 shows when the conditional operator's true and false conditions execute. The individual parts (operands) of the conditional don't require semicolons at the end of their statements. Notice, however, that the conditional operator includes a colon, not a semicolon, before the third argument.

Figure 9.1. Either the trueCode or the falseCode executes, but never both.

If both the true and false expressions assign values to the same variable, as done here, you can improve the efficiency by assigning the variable one time to the right of the conditional operator. In other words, the line


 (a > b) ? (c = 17) : (c = 23);

becomes this:


c = (a > b) ? 17 : 23;

You can put parentheses around the 17 and 23 if doing so improves the code's readability for you. You don't have to put assignment statements in the conditional. Any statement, even a cout or a cin, will work in the trueCode and falseCode parts of the conditional operator.



Readability and the Conditional

At first, most Visual C++ programmers feel that multiline if-else logic is easier to read than an equivalent conditional operator. Although they're correct, after you accustom yourself to the conditional operator, it becomes just as easy to follow as a multiline if-else because you can read the entire statement on one line without having to scan four or more lines. In addition, the conditional is slightly more efficient than if.

You can do some very tricky things with the conditional, such as writing a single statement that performs nested if-else logic. Such tricks are best left alone, however. A complicated conditional is too difficult to maintain later.


Listing 9.3 is a program that prints the minimum and maximum values entered by the user. First the program uses if-else logic, and then it uses conditional operators.



The conditional operator replaces if-else logic and is more efficient as well.

Input Listing 9.3. Using the conditional operator.

  1: // Filename: CONDIT.CPP

  2: // Finds the minimum and maximum values

  3: // from the user's entered values

  4: #include <iostream.h>

  5: void main()

  6: {

  7:   int val1, val2, min, max;

  8:

  9:   cout << "Enter a value: ";

 10:   cin >> val1;

 11:

 12:   cout << "Enter another value: ";

 13:   cin >> val2;

 14:

 15:   if (val1 < val2)

 16:      { min = val1;

 17:        max = val2; }

 18:   else

 19:      { max = val1;

 20:        min = val2; }

 21:

 22:   cout << endl << "Using if-else, the minimum is "

 23:        << min << " and the maximum is " << max << endl;

 24:

 25:   min = (val1 < val2) ? val1 : val2;

 26:   max = (val2 > val1) ? val2 : val1;

 27:

 28:   cout << endl << "Using ?:, the minimum is "

 29:        << min << " and the maximum is " << max << endl;

 30:   return;

 31: }

Output


Enter a value: 45

Enter another value: 67

Using if-else, the minimum is 45 and the maximum is 67

Using ?:, the minimum is 45 and the maximum is 67

Analysis

Lines 25 and 26 are the focus of this program. The if-else statement group from lines 15 to 20 performs the very same logic as the two lines starting at 25. The user's smaller value is either val1 or val2. The conditional operator in line 25 stores the smaller of the two values in min, and the conditional operator in line 26 stores the larger of the two values in max.

You also can squeeze the same logic into a single line using the comma operator, also known as the sequence point. It ensures that the statements on either side of the comma execute from left to right. Here is the statement that will assign both values to min and max using a single conditional:


 (val1 < val2) ? (min = val1, max = val2) : (min = val2, max = val1);

Obviously, such code is cumbersome and the individual conditional statements are much easier to write. Semicolons wouldn't work between the inner assignments because the first semicolon would signal the end of the conditional operator, and the remaining part of the statement would be in error as soon as Visual C++ spotted the colon.



Don't use the comma operator for day to day coding in C++. It leads to code that is difficult to follow. You might come across it in other people's code, so be aware of it.


Adding and Subtracting One




The increment and decrement operators add and subtract one from variables.

definition

To increment means to add one and to decrement means to subtract one.

The increment and decrement operators, ++ and -- respectively, are two of the most distinguishing operators in Visual C++. In fact, the ++ in C++ came from C's original important increment operator. These operators aren't necessary for Visual C++ programs; you can use an assignment statement to add one to or subtract one from any value whenever you need to. However, the increment and decrement operators are simple. You can use them in places where an assignment statement could be awkward.



Use the increment and decrement operators for readability and efficiency. Efficiency becomes especially important when you need to increment or decrement a variable several hundred or thousand times in a program, as you will do in Lesson 6.

Table 9.2 describes the actions of the ++ and -- operators. It shows equivalent assignment statements. There are actually two sets of increment and decrement operators. One pair is called the prefix operators, and the other pair is called the postfix operators. The difference lies in where you put the ++ and --, either before or after the variable being incremented or decremented.

Table 9.2. Visual C++'s increment and decrement operators.

Operator Example Order Equivalent Assignments
++ ++i; Prefix i = i + 1; or i += 1;
-- --i; Prefix i = i - 1; or i -= 1;
++ i++; Postfix i = i + 1; or i += 1;
-- i--; Postfix i = i - 1; or i -= 1;

There are times when you should use prefix and times when you should use postfix. However, as you can see from Table 9.2's Equivalent Assignments column, their actions appear to be exactly the same. The difference between prefix and postfix appears when you combine the ++ and -- with other variables inside larger expressions. When you use the ++ and -- on single variables, as done in Table 9.2's Example column, the postfix and prefix notations are identical.

Use the increment and decrement operators only on integer variables, not on floating-point variables. Also, never use these operators on literal values. The ++ and -- increment only integer variables.

After the variable definitions


int i = 10;

int j = 20;

the following statements add one to and subtract one from i and j respectively:


i++;   // i now holds 11

j--;   // j now holds 19

If the ++ and -- were expressed as prefix, the results would be the same. Now, after the statements


--i;

++j;

i and j hold their original values.

Being able to increment gives you the advantage of changing a variable without dedicating an entire statement to the change. The statements


cout << "The age is now " << age << endl;

age += 1;

can become this statement:


cout << "The age is now " << age++ << endl;

In this cout, the use of postfix is required. This leads us to the reason that there is a difference between the two categories of increment and decrement operators. In effect, if you specify prefix increment and decrement, Visual C++ performs the increment and decrement before any other operator in the expression. If, however, you specify postfix, Visual C++ computes the entire expression, including any assignment, and then performs the increment or decrement. In the previous cout, if you had incremented age using prefix (++age), Visual C++ would have incremented age before printing the age:


age += 1;   // Increment the age before the cout

cout << "The age is now " << age << endl;

The operator precedence table in Appendix C lists the prefix operators early and the postfix operators last to illustrate their precedence relative to the other operators. Technically speaking, the table is incorrect because the prefix operators actually should appear on the topmost level, with the postfix operators immediately following on the next level, to translate such confusing expressions as this:


a = +i+++j++;

By now, you know that such confusing statements aren't worth the trouble of maintaining later. Practically speaking, the prefix operators execute first in an expression and the postfix operators execute last.

Prefix and postfix differences become extremely important when you combine the operators with other operators in an expression such as this:


int a;

int i = 12;

int j = 6;

a = i++ - j;

When the last line finishes executing, you know that i will be 13. There's no doubt about the ++. It adds one to i. However, the timing of that addition is in question. Does the increment occur before or after the assignment to a?

i increments after a is assigned its value. Therefore, a gets assigned the value of 6 (12–6), and then Visual C++ increments i to 13. If prefix is used, like so


int a;

int i = 12;

int j = 6;

a = ++i - j;

i is incremented before a gets its value. Therefore, a is assigned a 7 (13–6). Again, i is 13 whether or not prefix or postfix is used, but the surrounding expression differs depending on the prefix or postfix notation.

The execution of prefix and postfix is under Visual C++'s control. Nothing you can do will change the execution of prefix before postfix in your expressions. The following statement wastes a lot of parentheses. They do nothing to force an early execution of the postfix decrement:


a = (((i--))) * value;   // Still decrements i LAST

Another common trap is using the ++ or -- operators twice in one expression on the same variable. What is the value of the following code?


int j = 0;

int k = ++j + ++j + ++j;

Visual C++ will give you an answer, but different compiler versions will give different answers. The official answer is that the result is undefined. The compiler will not warn you, but you should avoid such statements. (Visual C++ gives the answer 6, which is what you might expect.)

Be extremely careful when combining increment and decrement operators inside expressions with logical operators. To be safe, don't use increment and decrement at the same time that you use logical operators. For example, don't do this:


if ( (i == 10) || (j != ++k) )

A problem occurs if i is equal to 10. You might recall from Lesson 4 that Visual C++ short-circuits the || logical operator if the left side evaluates to true. Visual C++ never looks at the right side if i is indeed 10. Therefore, k isn't incremented if the short-circuit happens. Even though k is incremented using prefix, Visual C++ still doesn't increment k because of the short-circuiting feature. However, k does increment if i is not equal to 10, because Visual C++ has to evaluate the right side of the || to see whether the right side is true or false. This "maybe/maybe not" execution provides fertile soil for program bugs. If you want k incremented, pull it out of the if ahead of time like this:


++k;   // This can be either prefix or postfix

if ( (i == 10) || (j != k))

Listing 9.4 contains a program that prints the values of integer variables being incremented and decremented. You will see the increment and decrement used much more frequently when you learn in Lesson 6 how Visual C++ performs loops.



++ adds one to integer variables and -- subtracts one. The prefix and postfix differences determine how the increment and decrement operators affect surrounding operators.

Input Listing 9.4. Using increment and decrement operators to change variables.

  1:  // Filename: INCDEC.CPP

  2:  // Uses increment and decrement

  3:  #include <iostream.h>

  4:  void main()

  5:  {

  6:    int age;

  7:    int lastYear, nextYear;

  8:

  9:    cout << "How old are you? ";

 10:    cin >> age;

 11:

 12:    lastYear = age;

 13:    nextYear = age;

 14:

 15:    cout << "Wow, you're a year older than last year ("

 16:         << --lastYear << ")" << endl;

 17:    cout << "and you'll be " << ++nextYear

 18:         << " next year."  << endl;

 19:

 20:    return;

 21:  }

Output


How old are you? 32

Wow, you're a year older than last year (31)

and you'll be 33 next year.

Analysis

The increment and decrement occur inside each of the couts in lines 15 through 18. The program wouldn't work correctly if postfix were used because Visual C++ would print the statements before decrementing and incrementing the age as needed. The age is assigned to lastYear and nextYear in lines 12 and 13 without using the increment there. Consider what would happen if you attempted to replace the middle of the program with this:


lastYear = --age;

nextYear = ++age;

cout << "Wow, you're a year older than last year ("

     << lastYear << ")" << endl;

cout << "and you'll be " << nextYear

     << " next year."  << endl;

The printed results wouldn't be correct! The first statement assigns a correct age - 1 value, but then the age variable is off by one. Therefore, nextYear would be assigned the current age of the user.

Homework



General Knowledge


  1. If you were to multiply an integer by a floating point and store the result in a floating-point variable, would you get a correct value? If so, why? If not, why not?

  2. What is meant by promotion of data types?

  3. Given the following variable definitions

    
    int a;
    
    long b;
    
    float c;
    
    double d;

    what is the resulting data type for the following expressions?

    A. a + b

    B. a + c

    C. d + c

    D. a * d

    E. a + b + c + d

  4. Which of the following is most efficient?
    
    i++;
    
    i+=1;
    
    i = i + 1;

  5. Which operator replaces simple if-else statements?

  6. Why is ?: called a ternary operator?

  7. True or false: You don't actually need typecasts because of Visual C++'s automatic data-type promotion.

  8. True or false: Visual C++ will automatically convert the smallest data type in an expression to the data type of the largest value in the expression unless you override the smallest data type with a typecast.

  9. True or false: A typecast changes a variable's data type for the rest of the program.

  10. True or false: Postfix and prefix mean the same thing when incrementing or decrementing a single variable and when no other computations are being made in the same expression.

  11. True or false: You should nest conditional operators to eliminate nested if-else logic.


    What's the Output?


  12. What value resides in a after the following?

    
    int a = 6;
    
    b = ++a - 1;

  13. What value resides in b after the code in question 12 finishes?

  14. What value resides in a after the following?

    
    int a = 6;
    
    b = a++ - 1;

  15. What value resides in b after the code in question 14 finishes?

    Find the Bug



  16. What's wrong with the following conditional statement?


    
    r = (u < 100) ? (r = 12) : (r = 13);

  17. What's wrong with the following conditional statement?

    
    (9 > i || u <= 8) ? (p = 12) ; (p = 13);

    Write Code That. . .


  18. Given the variable definitions
    
    char c;
    
    int i;
    
    float f;
    
    double d;

    rewrite each of the following expressions using typecasts that match the automatic promotion that Visual C++ would perform without the typecasts. The first one is done for you. (Hint: One or more expressions might require two typecasts to match the automatic promotion that Visual C++ will perform.)

    
    answer = i * 4.5; becomes answer = float(i) * 4.5;

    A. answer = i * d;

    B. answer = c + i + f;

    C. answer = d + f;

    D. answer = c + i + 2;

  19. Rewrite the following conditional statement using parentheses to help make the three parts of the conditional statement clearer:


    
    age <= 18 ? adultCode = 0: adultCode = 1;

  20. Eliminate the following if-else to use only a conditional operator:

    
    if (price > 21.00)
    
      { salePercent = .12; }
    
    else
    
      { salePercent = .08; }

  21. Write a program that asks the user for the number of years, from 1 to 40, that he or she has worked. If the user enters a value that doesn't fall in that range, print an error and terminate the program early. Otherwise, print a message that says the following (assuming that the user enters 13):

    
    You have worked 13 years

    If the user enters 1, print the singular of the message, like this:


    
    You have worked 1 year

  22. Use a conditional to print the singular or plural of year. (Hint: Store an s or a null character in a character variable.)


    Extra Credit


  23. Write a program that prints the final price of purchases at a store where everything costs exactly one dollar. Ask for the number of items purchased. Compute a sales tax of 8 percent if the user's purchase is less than $100 and 7.5 percent if the purchase is greater than or equal to $100. Also, if the purchase is over $500, give the customer an additional 10 percent after-tax discount. Print the purchase price, the amount of the tax, the amount of the discount ($ 0.00 if no discount applies) and the total price. Don't use an if statement in the program. Use only the conditional operator when your program must make a choice. Typecast all operations so that Visual C++ doesn't have to promote anything to floating-point.

Previous Page Page Top TOC Next Page