Operators

 < Day Day Up > 



Most of the expressions listed in table 5-3 involve the use of multiplicative, additive, relational, conditional, and assignment operators. You may already have a fundamental understanding of how these operators work, especially the multiplicative, additive, and assignment. I will discuss these and a few others in this section. Any operator I fail to discuss here will be introduced to you later in the book when you are ready to learn its use.

Before I talk about each operator I want to talk briefly about operator precedence.

Operator Precedence

Operators in C++ have a precedence associated with their use. Table 5-4 lists C++ operators in order of their precedence, from highest to lowest, along with their associativity. The use of parentheses is covered in the next section. Use them and you will have fewer bugs in your code and fewer headaches.

Table 5-4: C++ Operators, Precedence, and Associativity

Operator

Description

Associates

++

Post-increment

Left to right

--

Post-decrement

Left to right

( )

Function call

Left to right

[ ]

Array element

Left to right

->

Pointer to structure member

Left to right

.

Structure or union member

Left to right

++

Pre-increment

Right to left

--

Pre-decrement

Right to left

!

Logical NOT

Right to left

~

Bitwise NOT

Right to left

-

Unary minus

Right to left

+

Unary plus

Right to left

&

Address

Right to left

*

Indirection

Right to left

sizeof

Size in bytes

Right to left

new

Allocate program memory

Left to right

delete

Deallocate program memory

Left to right

(type)

Type cast (includes all C++ cast operators)

Left to right

.*

Pointer to member (objects)

Left to right

->*

Pointer to member (pointers)

Left to right

*

Multiply

Left to right

/

Divide

Left to right

%

Modulo or Remainder

Left to right

+

Add

Left to right

-

Subtract

Left to right

<<

Left shift

Left to right

>>

Right shift

Left to right

<

Less than

Left to right

<=

Less than or equal to

Left to right

>

Greater than

Left to right

>=

Greater than or equal to

Left to right

==

Equal to

Left to right

!=

Not equal to

Left to right

&

Bitwise AND

Left to right

^

Bitwise exclusive OR

Left to right

|

Bitwise OR

Left to right

&&

Logical AND

Left to right

||

Logical OR

Left to right

? :

Conditional

Right to left

=

Assignment

Right to left

*=, /=, %=, +=, -=

<<=, >>=, &=, ^=, |=

Compound assignment

Right to Left

,

Comma

Left to right

Use Parentheses

Using table 5-4 as a guide, can you determine what value will be printed to the screen when this statement executes?

cout<<7 * 3 + 1 - 201 % 20<<endl; //version 1

How about the next statement?

cout<<(7 * (3 + 1)) - (201 % 20)<<endl; //version 2

And this one?

cout<<((((7 * 3) + 1) - 201) % 20)<<endl; //version 3

As you might guess, each version of the expression results in a different value. Version 1 results in a value derived from performing the computations using each operator’s native precedence. Versions 2 and 3 result in different values because the parentheses force a different order of computation.

From a human perspective, versions 2 and 3 are easier to understand. Version 1 takes a little effort unless you are already familiar with the precedence of the operators used. The next version produces the same result as version 1:

cout<<(((7 * 3) + 1) - (201 % 20))<<endl; //version 4

The parentheses make the expression easier to read and understand. Now you are faced with a dilemma; try and memorize the precedence of every operator, or, use parentheses and simplify your life! Choose wisely grasshopper!

Multiplicative Operators

Table 5-5 lists the three multiplicative operators.

Table 5-5: Multiplicative Operators

Operator

Description

*

multiplication

/

division

%

modulus

The multiplication and division operators are overloaded to work on all the arithmetic types such as float, double, and integer, and enumerations. The modulus operator works on integral type and enumerations only.

Multiplication Operator

The asterisk is used as the multiplication operator. The following code gives an example of its use:

int i=0, j=10, k=10000; i = j*k; cout<<i<<endl;

Be careful when using the multiplication operator. You can easily calculate a value that is too big to fit into a small integer variable. The following code looks like the previous example with one exception. Can you spot the difference?

int j=10, k=10000; short i = j*k; cout<<i<<endl;

The variable named i is now declared a short, which holds half as much as a regular integer. This causes a truncation of the larger value to a size that will fit into the smaller data type. These types of errors are easy to make and hard to detect because the compiler offers no warning.

The asterisk, like other symbols in C++, is overloaded to perform more than just multiplication in C++. It is also used to declare and dereference pointers. (See chapter 8) As you gain experience reading and writing C++ code you will become comfortable recognizing the context in which operators are used, but, until that happens, you will be a little confused to see what you think is the multiplication operator being used for something other than multiplication.

Division Operator

The division operator works as you would expect although there are a few issues to keep in mind when you use it. The following statement shows the division operator in use:

float f = 3.5f / 1.5f;

In this example, a float variable named f is declared and initialized to the value of 2.33333. In this case, each of the numeric literals are of type float and the size of the result fits into the variable f. You will run into trouble when you attempt to store the results of floating point division into an integer variable. The following statement shows an example:

int result = 3.5f/1.5f;

Integer types are not designed to represent the decimal portion of floating point values so you will lose the .33333 portion of the result. The variable result will be initialized with the value 2 and you will not be warned about the loss of numeric precision.

You can perform division on complex expressions by using parentheses. The following statement gives an example:

float f = (3.5 + 1.5) / 2.5;

You should also be aware that an attempt to divide a number by zero will result in a compiler warning.

Modulus Operator

The modulus or remainder operator works like the division operator but returns the remainder and discards the quotient. The following statement shows the modulus operator in use:

int remainder = 215 % 20;

This statement declares an integer variable named remainder and initializes its value to 15.

The primary thing to remember with the modulus operator is that it is to be used on integral types only. Using it on floating point literals or variables results in a compiler error.

Additive Operators

Table 5.6 lists the additive operators.

Table 5-6: Additive Operators

Operator

Description

+

Addition

-

Subtraction

Addition Operator

The addition operator performs arithmetic addition on arithmetic, enumeration, or pointer types. The following code shows an example of the addition operator being used with an enumeration type:

enum set_one {up, down, left, right}; ... int where = 1 + down;

In this example, an enumerated type named set_one is declared with the four enumerations up, down, left, right. The value of up is 0, the value of down 1, the value of left 2, and the value of right 3. (Enums are covered in more detail in chapter 10) The integer variable where is declared and initialized to the value of the integer literal 1 plus the enumeration value of down.

The following code gives an example of the addition operator being used with a pointer operand:

int int_array[] = {1,2,3,4,5}; int int_val = *(int_array + 3);

In this example an array of integers named int_array is declared and initialized with five integer values. On the next line an integer variable named int_val is declared and initialized to the value that resides at the 4th element of the array. In this case the value stored in the 4th element of the array is 4. Arrays are covered in excruciating detail in chapter 8 but here’s a quick explanation of what’s going on here. The array name int_array points to the start of the integer array. This means the name of the array is a pointer, which means it contains a memory address. Since the addition operator is being applied to a memory address that points to an array of integer elements, the value 3 means 3 integer storage units. The addition will be the memory address of the array + (3 times the size of an integer in bytes) or array address + (3 x 4) or array address + 12.

The value resulting from the addition is a memory address or pointer. The overloaded asterisk symbol, in this case used as the pointer dereferencing operator, must be applied to the result to obtain the actual integer object residing in the 4th element of the array. It is this value that is ultimately assigned to the integer variable named int_val.

Don’t worry if you don’t fully understand all this pointer stuff right now. After you read chapters 7 and 8 you will be an expert!

Subtraction Operator

Besides being used for traditional arithmetic subtraction operations on arithmetic data types, the subtraction operator can be used on pointers as well. The following code demonstrates the use of the subtraction operator on pointer types:

int_val = *((&int_array[4]) - 3);

Using the integer array from the previous example, the subtraction operator is being used to subtract 3 integer storage units from the address of the 5th element of int_array. This will result in the value 12 being subtracted from the address of the 5th array element which will yield the address of the 2nd element. The result of the subtraction is a memory address and must be dereferenced to access the integer object stored at that address. When this statement is executed the integer value 2 will be assigned to the variable named int_val.

Shift Operators

Shift operators let you perform bit shifting operations on integral objects. Table 5-7 lists the shift operators.

Table 5-7: Shift Operators

Operator

Description

<<

Left Shift

>>

Right Shift

Left Shift Operator

The following code shows the left shift operator in use.

unsigned shift_val = 1; shift_val = shift_val << 1;

The first statement declares an unsigned integer variable named shift_val and initializes its value to 1. In a computer with 32 bit registers the value 1 looks like this in binary:

00000000000000000000000000000001

The second statement shifts the bits of shift_val to the left by one bit and assigns the result of the shift operation back to the shift_val variable. As the bits are shifted to the left, the right-hand replacement bits are set to zero. Figure 5-8 shows what happens to shift_val when its bits are shifted to the left four times.

click to expand
Figure 5-8: Left Shifting shift_val

The effect of left shifting a bit value by one bit is the same as multiplying the value by two. But you need to be careful and not left shift it too far. You also have to pay attention to the type of bit pattern you are shifting. For instance, left shifting a signed type will result in values going from positive to negative depending on the value of the bit that moves into the sign bit position.

Right Shift Operator

The right shift operator works similar to the left shift operator. If the value being shifted is an unsigned type or a signed type with a positive value then the effect of shifting the bits to the right by one bit will be the same as dividing the value by 2. If the value being shifted is a negative number then the result of shifting right is implementation dependent, meaning the operator’s behavior in this regard is left to the discretion of the compiler manufacturer. The following code shows the right shift operator in use on a negative number:

int shift_val = 0xFFFFFFFF; shift_val = shift_val >> 1;

The first statement declares the integer variable shift_val and initializes it to the hexadecimal value FFFFFFFF. This is the bit pattern for -1. When statement two is executed the bits are shifted to the right by one bit. On Metrowerks CodeWarrior the value remains -1. This is due to the bits coming in from the left being set to 1, which keeps the sign bit set.

It will be helpful to see another example. The following code initializes shift_val to the maximum negative number an integer can hold then shifts it to the right by one bit.

int shift_val = 0x80000000; shift_val = shift_val >> 1;

In the first statement, shift_val is initialized to the value -2,147,483,648. After the execution of the second statement its value will be -1,073,741,824. Shifting the negative number the right by one bit has the effect of dividing the number by 2. However, this will only work until you have shifted all the way to the right, at which time the value will be -1. Further right shifting will have no effect. Figure 5-9 shows what happens to the variable shift_val when the right shift operator is applied 4 times.

click to expand
Figure 5-9: Right Shifting shift_val

You can see in figure 5-9, with a negative number, the bits shifted into the bit pattern in the most significant bit position are set to 1. This keeps the value negative.

If you start with a positive number in a signed integer type the number will remain positive as you shift bits to the right. This means the bit being shifted into the most significant bit position is set to 0.

Relational Operators

Table 5-8 lists the relational operators.

Table 5-8: Relational Operators

Operator

Description

<

Less Than

>

Greater Than

<=

Less Than or Equal To

>=

Greater Than or Equal To

Relational operators are used to compare the value of arithmetic, enumeration, or pointer objects. A relational operator returns a boolean value which is either true or false.

Less Than Operator

The less than operator takes two operands and returns true if the left operand is less than the right operand. The following statement illustrates the use of the less than operator:

bool result = 3 < 5;

When this statement is executed the boolean variable named result will be initialized to the value true or 1.

Greater Than Operator

The greater than operator takes two operands and returns true if the left operand is greater than the right operand. The following statement illustrates the use of the greater than operator:

bool result = 3 > 5;

In this case, the variable result will be initialized to false or 0.

Less Than or Equal To Operator

The less than or equal to operator takes two operands and returns true if the left operand is of lesser value or equal to the right operand. The following statement illustrates the use of the less than or equal to operator:

bool result = 3 <= 5;

When this statement is executed the boolean variable result will be initialized to true or 1.

Greater Than or Equal To Operator

The greater than or equal to operator takes two operands and returns true if the left operand is of greater value or equal to the right operand. The following statement illustrates the use of the greater than or equal to operator:

bool result = 3 >= 5;

When this statement executes result will be initialized to the value false or 0.

You will use relational operators heavily to make decisions in your source code in order to figure out what to do next. You will see more about relational operators covered in chapter 6 when I discuss how to control the flow of program execution.

Equality Operators

Table 5-9 lists the equality operators. Equality operators can compare arithmetic, enumeration, and pointer values just like the relational operators but have a lower precedence.

Table 5-9: Equality Operators

Operator

Description

==

Equal To

!=

Not Equal To

Equal To Operator

The equal to operator compares two operands for equality and returns a boolean value of true or false based on the result of the comparison. The following statement shows the equality operator in action:

bool result = 3 == 5;

This statement will initialize the boolean variable result to false.

Not Equal To Operator

The not equal to operator compares two operands and returns true if they are not equal. The following code illustrates the use of the not equal to operator:

bool result = 3 != 5

This statement will initialize the variable result to true or 1.

Bitwise AND Operator - &

The bitwise AND operator takes two operands, ANDs them, and returns the result. What the heck is an AND operation you ask?

click to expand
Figure 5-10: AND Truth Table

An AND operation compares two bits. The result of the bit comparison is either 1 or 0 depending on the state of the two bits compared. An AND on two bits will result in a true or 1 output only when both bits being compared are true or 1. Figure 5-10 gives the truth table for an AND operation. The following statement shows the bitwise AND operator in use:

int and_result = 0x00000001 & 0xFFFFFFFF;

In this example, the variable and_result will be initialized to the result of the AND operation which, in this case, is 1. Take a look at this operation again in slow motion:

00000000000000000000000000000001 11111111111111111111111111111111 00000000000000000000000000000001

When the hexadecimal is converted to binary you can easily see the only bit comparison that results in a true is the two least significant bits. The rest of the bits are set to false or 0 because only one bit in each comparison is on or true. I use the terms on, true, and 1 interchangeably here as well as off, false, or 0.

Bitwise Exclusive OR Operator - ^

Bitwise exclusive OR operations take place according to the truth table shown in figure 5-11. The following statement illustrates the use of the exclusive OR operator in action:

click to expand
Figure 5-11: Exclusive OR Truth Table

int ex_or_result = 0x00000001 ^ 0xFFFFFFFF;

When this statement is executed the variable ex_or_result will be initialized to 0xFFFFFFFE. Take a look at the binary version:

00000000000000000000000000000001 11111111111111111111111111111111 11111111111111111111111111111110

click to expand
Figure 5-12: Inclusive OR Truth Table

Bitwise Inclusive OR Operator - |

The bitwise inclusive OR takes two operands and returns the result of an inclusive OR comparison between each bit. Figure 5-12 gives the truth table for an inclusive OR operation. The following statement show the bitwise inclusive OR operator in action:

int in_or_result = 0x00000001 | 0xFFFFFFFF;

The variable in_or_result will be set to the value 0xFFFFFFFF. Examine the binary version:

00000000000000000000000000000001 11111111111111111111111111111111 11111111111111111111111111111111

In this case all bits are set to 1 as the truth table for the inclusive OR operation would suggest.

Logical AND Operator - &&

The logical AND operator takes two boolean expressions as operands and returns true or false based on the truth table given in figure 5-10. This is different from its bitwise counterpart in that it is not comparing bits, rather, it is comparing the result of one expression to that of another, and returning a result base on the result of the comparison. The following code gives an example of the logical AND operator in use:

int a = 0; boolean and_result = (a<5) && (true);

On the first line an integer variable named a is declared and initialized to 0. On the second line the boolean variable and_result is declared and initialized to the result of the logical AND expression. The logical AND operator will compare the results of (a<5), which is true, to the (true), which is always true.

Logical OR Operator - ||

The logical OR operator makes comparisons of two boolean expressions according to the truth table shown in Figure 5-12. The following code gives an example of the logical OR in use:

int a = 0; boolean or_result = (a<5) || (true);

In this example, the boolean variable or_result is initialized to the result of the OR comparison between (3<5) which is true, and (true), which is still true!

Conditional Operator - ? :

This is a cool operator but until you get used to it you will look a little cross-eyed at it when you see it in source code. To discuss the conditional operator I have to get ahead of my story a little bit. The conditional operator is a shorthand way of writing an if statement. If statements are covered in detail in chapter 6.

The conditional operator will evaluate a boolean expression and offer two possible alternatives, depending on the result of the evaluation. The expression to be evaluated comes before the question mark. The first alternative, or statement you want to execute if the expression is true is placed to the right of the question mark and to the left of the colon. The statement you want to execute if the expression evaluates to false is placed to the right of the colon. Use this handy map to the conditional operator if you get lost while trying to impress your friends by using it:

click to expand
Figure 5-13: Conditional Operator Map

The following statement shows the conditional operator in use:

 (3>5) ? cout<<“True statement”<<endl : cout<<“False statement”<<endl;

When this statement is executed the expression (3>5) will be evaluated and result in a boolean value of false. This will cause the false statement to be executed. In this case the text False statement will be printed to the screen. The following if statement will do the same thing:

if(3>5)    cout<<“True statement”<<endl;       else cout<<“False statement”<<end;

Assignment Operators

Table 5-10 lists the assignment operators.

Table 5-10: Assignment Operators

Operator

Description

=

Assignment

*=

Compound Multiplication Assignment

/=

Compound Division Assignment

%=

Compound Modulus Assignment

+=

Compound Addition Assignment

-=

Compound Subtraction Assignment

>>=

Compound Right Shift Assignment

<<=

Compound Left Shift Assignment

&=

Compound Bitwise AND Assignment

^=

Compound Bitwise Exclusive OR Assignment

|=

Compound Inclusive OR Assignment

You have seen the assignment operator, =, in action many times in this chapter. The important thing to remember when using the assignment operator is that it is not the Equal To operator, ==, which is an equality operator vice an assignment operator. To better understand the use of the assignment operator it is helpful to know the difference between an “lvalue” and an “rvalue”.

lvalue vs. rvalue

If you are new to C++ programming you have probably seen the compiler error, “Not an lvalue...”, and wondered what it meant.

When making an assignment using an assignment operator, what you are trying to do is assign the result, or value, of some expression to a memory location. The assignment expression, when viewed in this context, looks like this:

click to expand
Figure 5-14: Assignment Operator Operands

An assignment is an expression and therefore returns a result. The type of the result is the type that was stored in the memory location pointed to by the left operand. The following example might help clarify this concept:

int a = 0, b = 0; a = (b = 5);

In this example, two integer variables named a and b were declared and initialized to 0 using the assignment operator. The second statement assigns to the variable b the value 5. The result of the assignment is 5 and is assigned to the variable a.

Compound Assignment Operators

The rest of the assignment operators are known as compound assignment operators and are used as a shorthand way of getting things done in C++. The following code shows the compound multiplication assignment operator in action:

int a = 3; a *= 3;

The first statement declares the integer variable named a and assigns it the value 3. The next statement multiplies a by 3 and assigns the result back to the variable a. The second statement is equivalent to the following longer version that does the same thing:

a = a * 3;

The rest of the compound operators work the same way.

Comma Operator - ,

The comma operator can be used to separate expressions. The following code shows the comma operator in use:

int a = 0, b = 0, c = 0; a = (c = 10), (b = 8); (3 < 5), (a = 0);

The first statement shows the comma operator being used to separate variable declarations and assignments on the same line. The second statement shows two assignment expressions being separated by the comma operator. The third statement is something not often seen but reiterates the use of the comma operator’s ability to separate any expression, not just assignment expressions.

Increment and Decrement Operators (++, --)

There are many times during the course of programming that you need to increment or decrement a variable by 1. You could increment the old fashioned way...

i = i + 1; or i += 1;

...in this example the variable i is being set to the value it contains plus 1, or use the ++ operator:

i++; or ++i;

There are two versions of the increment and decrement operators: prefix and postfix. Consider the following example:

int a = 0, i = 1; a = i++;

This is an example of the postfix version of the increment operator in use. When the second statement executes a will be assigned the value of 1, which is the value of i before the increment operator expression is evaluated. The following example demonstrates the use of the prefix version of the increment operator and will result in the variable a being assigned the value 2:

int a = 0, i = 1; a = ++i;

The decrement operator works the same way. You will most likely see the increment and decrement operators used in for statements to do array processing. The following code gives an example:

for(int i=0; i<ARRAY_SIZE, i++){    //array processing statements here }

Chapter 6 discusses program control flow statements like the for statement, above, in detail.



 < Day Day Up > 



C++ for Artists. The Art, Philosophy, and Science of Object-Oriented Programming
C++ For Artists: The Art, Philosophy, And Science Of Object-Oriented Programming
ISBN: 1932504028
EAN: 2147483647
Year: 2003
Pages: 340
Authors: Rick Miller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net