Fundamental Operators

I l @ ve RuBoard

Fundamental Operators

C uses operators to represent arithmetic operations. For example, the + operator causes the two values flanking it to be added together. If the term "operator" seems odd to you, please keep in mind that those things had to be called something. "Operator" does seem to be a better choice than, say, "those things" or "arithmetical transactors." Now take a look at the operators used for basic arithmetic: =, +, -, *, and /. (C does not have an exponentiating operator. The standard C math library, however, provides a pow() function for that purpose. For example, pow(3.5, 2.2) returns 3.5 raised to the 2.2 power.)

Assignment Operator: =

In C, the equal sign does not mean "equals." Rather, it is a value-assigning operator. The statement

 bmw = 2002; 

assigns the value 2002 to the variable named bmw . That is, the item to the left of the = sign is the name of a variable, and the item on the right is the value assigned to the variable. The = symbol is called the assignment operator . Again, don't think of the line as saying, " bmw equals 2002 ." Instead, read it as "assign the value 2002 to the variable bmw ." The action goes from right to left for his operator.

Perhaps this distinction between the name of a variable and the value of a variable seems like hair-splitting, but consider the following common type of computer statement:

 i = i + 1; 

As mathematics, this statement makes no sense. If you add 1 to a finite number, the result isn't "equal to" the number you started with, but as a computer assignment statement, it is perfectly reasonable. It means "find the value of the variable named i ; to that value, add 1 , and then assign this new value to the variable named i " (see Figure 5.1).

Figure 5.1. The statement i = i + 1;
graphics/05fig01.jpg

A statement such as

 2002 = bmw; 

makes no sense in C (and, indeed, is invalid) because 2002 is just a constant. You can't assign a value to a constant; it already is its value. When you sit down at the keyboard, therefore, remember that the item to the left of the = sign must be the name of a variable. Actually, the left side must refer to a storage location. The simplest way is to use the name of a variable, but, as you will see later, a "pointer" can be used to point to a location. More generally , ANSI C uses the term modifiable lvalue to label those entities to which you can assign values. "Modifiable lvalue" is not, perhaps, the most intuitive phrase you've encountered , so let's look at some definitions.

Some Terminology: Data Objects, Lvalues, Rvalues, and Operands

A data object is a general term for a region of data storage that can be used to hold values. The data storage used to hold a variable or an array is a data object, for instance. ANSI C uses the term lvalue to mean a name or expression that identifies a particular object. The name of a variable, for instance, is an lvalue, so object refers to the actual data storage, but lvalue is a label used to identify, or locate, that storage.

Not all objects can have their values changed, so ANSI C uses the term modifiable lvalue to identify objects whose values can be changed. Therefore, the left side of an assignment operator should be a modifiable lvalue. Indeed, the l in lvalue comes from left because modifiable lvalues can be used on the left side of assignment operators.

The term rvalue refers to quantities that can be assigned to modifiable lvalues. For instance, consider this statement:

 bmw = 2002; 

Here, bmw is a modifiable lvalue, and 2002 is an rvalue. As you probably guessed, the r in rvalue comes from right . Rvalues can be constants, variables , or any other expression that yields a value.

As long as you are learning the names of things, the proper term for what we have called an "item" (as in "the item to the left of the =") is operand . Operands are what operators operate on. For example, you can describe eating a hamburger as applying the "eat" operator to the "hamburger" operand, or you can say that the left operand of the = operator shall be a modifiable lvalue.

The basic C assignment operator is a little flashier than most. Try the short program in Listing 5.3.

Listing 5.3 The golf.c program.
 /* golf.c -- golf tournament scorecard */ #include <stdio.h> int main(void) {   int jane, tarzan, cheeta;   cheeta = tarzan = jane = 68;   printf("                  cheeta   tarzan    jane\n");   printf(First round score %4d %8d %8d\n",cheeta,tarzan,jane);   return 0; } 

Many languages would balk at the triple assignment made in this program, but C accepts it routinely. The assignments are made right to left: First jane gets the value 68, then tarzan does, and finally cheeta does. Therefore, the output is as follows :

 cheeta  tarzan  jane First round score  68      68     68 

Addition Operator: +

The addition operator causes the two values on either side of it to be added together. For example, the statement

 printf("%d", 4 + 20); 

causes the number 24 to be printed, not the expression

 4 + 20 

The values (operands) to be added can be variables as well as constants. Therefore, the statement

 income salary + bribes; 

causes the computer to look up the values of the two variables on the right, add them, and assign this total to the variable income.

Subtraction Operator: -

The subtraction operator causes the number after the - sign to be subtracted from the number before the sign. The statement

 takehome = 224.00 - 24.00; 

assigns the value 200.0 to takehome .

The + and - operators are termed binary , or dyadic , operators, meaning that they require two operands.

Sign Operators: - and +

The minus sign can also be used to indicate or to change the algebraic sign of a value. For instance, the sequence

 rocky = -12; smokey = -rocky; 

gives smokey the value 12 .

When the minus sign is used in this way, it is called a unary operator , meaning that it takes just one operand (see Figure 5.2).

Figure 5.2. Unary and binary operators.
graphics/05fig02.jpg

The ANSI standard adds a unary + operator to C. It doesn't alter the value or sign of its operand; it just enables you to use statements like

 dozen = +12; 
without getting a compiler complaint. Formerly, this construction was not allowed.

Multiplication Operator: *

Multiplication is indicated by the * symbol. The statement

 cm = 2.54 * in; 

multiplies the variable in by 2.54 and assigns the answer to cm .

By any chance, do you want a table of squares? C doesn't have a squaring function, but, as shown in Listing 5.4, you can use multiplication to calculate squares.

Listing 5.4 The squares.c program.
 /* squares.c -- produces a table of first 20 squares */ #include <stdio.h> int main(void) {    int num = 1;    while (num < 21)   {       printf("%10d %10d\n", num, num * num);       num = num + 1;   }   return 0; } 

This program prints the first 20 integers and their squares, as you can verify for yourself. Let's look at a more interesting example.

Exponential Growth

You have probably heard the story of the powerful ruler who seeks to reward a scholar who has done him a great service. When the scholar is asked what he would like, he points to a chessboard and says, just one grain of wheat on the first square, two on the second, four on the third, eight on the next , and so on. The ruler, lacking mathematical erudition, is astounded at the modesty of this request, for he had been prepared to offer great riches. The joke, of course, is on the ruler, as the program in Listing 5.5 shows. It calculates how many grains go on each square and keeps a running total. Because you might not be up to date on wheat crops, the program also compares the running total to a rough estimate of the annual wheat crop in the United States.

Listing 5.5 The wheat.c program.
 /* wheat.c -- exponential growth */ #include <stdio.h> #define SQUARES 64    /* squares on a checkerboard   */ #define CROP 9E14     /* US wheat crop in grains     */ int main(void) {    double current, total;    int count = 1;    printf("square   grains added   total grains   ");    printf("fraction of \n");    printf("                                       ");    printf("US total\n");    total = current = 1.0;    /* start with one grain   */    printf("%4d %15.2e %13.2e %13.2e\n", count, current,           total, total/CROP);    while (count < SQUARES)    {       count = count + 1;       current = 2.0 * current;                         /* double grains on next square */       total = total + current;          /* update total */       printf("%4d %15.2e %13.2e %13.2e\n", count, current,              total, total/CROP);    }    return 0; } 

The output begins innocuously enough:

 square  grains added  total grains  fraction of                                        US total    1       1.00e+000     1.00e+000     1.11e-015    2       2.00e+000     3.00e+000     3.33e-015    3       4.00e+000     7.00e+000     7.78e-015    4       8.00e+000     1.50e+001     1.67e-014    5       1.60e+001     3.10e+001     3.44e-014    6       3.20e+001     6.30e+001     7.00e-014    7       6.40e+001     1.27e+002     1.41e-013    8       1.28e+002     2.55e+002     2.83e-013    9       2.56e+002     5.11e+002     5.68e-013   10       5.12e+002     1.02e+003     1.14e-012 

After ten squares, the scholar has acquired just a little over a thousand grains of wheat, but look what has happened by square 50!

 50    5.63e+014   1.13e+015   1.25e+000 

The haul has exceeded the total U.S. annual output! If you want to see what happens by the 64th square, you will have to run the program yourself.

This example illustrates the phenomenon of exponential growth. The world population growth and our use of energy resources have followed the same pattern.

Division Operator: /

C uses the / symbol to represent division. The value to the left of the / is divided by the value to the right. For example, the following gives four the value of 4.0 :

 four = 12.0/3.0; 

Division works differently for integer types than it does for floating types. Floating-type division gives a floating-point answer, but integer division yields an integer answer. An integer has to be a whole number, which makes dividing 5 by 3 awkward , because the answer isn't a whole number. In C, any fraction resulting from integer division is discarded. This process is called truncation .

Try the program in Listing 5.6 to see how truncation works and how integer division differs from floating-point division.

Listing 5.6 The divide.c program.
 /* divide.c -- divisions we have known */ #include <stdio.h> int main(void) {    printf("integer division:  5/4   is %d \n", 5/4);    printf("integer division:  6/3   is %d \n", 6/3);    printf("integer division:  7/4   is %d \n", 7/4);    printf("floating division: 7./4. is %1.2f \n", 7./4.);    printf("mixed division:    7./4  is %1.2f \n", 7./4);    return 0; } 

Listing 5.6 includes a case of "mixed types" by having a floating-point value divided by an integer. C is a more forgiving language than some and will let you get away with this, but normally you should avoid mixing types. Now for the results:

 integer division:  5/4   is 1 integer division:  6/3   is 2 integer division:  7/4   is 1 floating division: 7./4. is 1.75 mixed division:    7./4  is 1.75 

Notice how integer division does not round to the nearest integer, but always truncates, that is, discards the entire fractional part. When you mixed integers with floating point, the answer came out the same as floating point. When a calculation uses both types, the integer is converted to floating point before division.

The properties of integer division turn out to be handy for some problems, and we will give an example fairly soon. First, there is another important matter: What happens when you combine more than one operation into one statement? That is the next topic.

Operator Precedence

Consider the following line of code:

 butter = 25.0 + 60.0 * n / SCALE; 

This statement has an addition, a multiplication, and a division. Which operation takes place first? Is 25.0 added to 60.0 , the result of 85.0 then multiplied by n , and that result then divided by SCALE ? Is 60.0 multiplied by n , the result added to 25.0 , and that answer then divided by SCALE ? Is it some other order? Let's take n to be 6.0 and SCALE to be 2.0. If you work through the statement using these values, you will find that the first approach yields a value of 255. The second approach yields 192.5. A C program must have some other order in mind, for it would give a value of 205.0 for butter .

Clearly, the order of executing the various operations can make a difference, so C needs unambiguous rules for choosing what to do first. C does this by setting up an operator pecking order. Each operator is assigned a precedence level. As in ordinary arithmetic, multiplication and division have a higher precedence than addition and subtraction, so they are performed first. What if two operators have the same precedence? If they share an operand, they are executed according to the order in which they occur in the statement. For most operators, the order is from left to right. (The = operator was an exception to this.) Therefore, in the statement

 butter = 25.0 + 60.0 * n / SCALE; 

the order of operations is as follows:

 60.0 * n            The first * or / in the expression (assuming n is 6 so that 60.0 * n                     is 360.0) 360.0 / SCALE       Then the second * or / in the expression 25.0 + 180          Finally (because SCALE is 2.0), the first + or - in the expression to                     yield 205.0 

Many people like to represent the order of evaluation with a type of diagram called an expression tree . Figure 5.3 is an example of such a diagram. The diagram shows how the original expression is reduced by steps to a single value.

Figure 5.3. Expression trees showing operators, operands, and order of evaluation.
graphics/05fig03.jpg

What if you want, say, an addition to take place before a division? Then you can do as we have done in this line:

 flour = (25.0 + 60.0 * n) / SCALE; 

Whatever is enclosed in parentheses is executed first. Within the parentheses, the usual rules hold. For this example, first the multiplication takes place and then the addition. That completes the expression in the parentheses. Now the result can be divided by SCALE .

Table 5.1 summarizes the rules for the operators used so far. (Appendix B, "C Operators," contains a table covering all operators.)

Table  5.1. Operators in order of decreasing precedence.
Operators Associativity
( ) Left to right
+ - (unary) Right to left
* / Left to right
+ - (binary) Left to right
= Right to left

Notice that the two uses of the minus sign have different precedences, as do the two uses of the plus sign. The associativity column tells you how an operator associates with its operands. For example, the unary minus sign associates with the quantity to its right, and in division the left operand is divided by the right.

Precedence and the Order of Evaluation

Operator precedence provides vital rules for determining the order of evaluation in an expression, but it doesn't necessarily determine the complete order. C leaves some choices up to the implementor. Consider this statement:

 y = 6 * 12 + 5 * 20; 

Precedence dictates the order of evaluation when two operators share an operand. For example, the 12 is an operand for both the * and the + operators, and precedence says that multiplication comes first. Similarly, precedence says that the 5 is to be multiplied, not added. In short, the multiplications 6 * 12 and 5 * 20 take place before any addition. What precedence does not establish is which of these two multiplications occurs first. C leaves that choice to the implementor because one choice might be more efficient for one kind of hardware, but the other choice might work better on another kind of hardware. In either case, the expression reduces to 72 + 100 , so the choice doesn't affect the final value for this particular example. "But," you say, "multiplication associates from left to right. Doesn't that mean the leftmost multiplication is performed first?" (Well, maybe you don't say that, but somewhere someone does.) The association rule applies for operators that share an operand. For instance, in the expression 12 / 3 * 2 , the / and * operators, which have the same precedence, share the operand 3 . Therefore, the left-to-right rule applies in this case, and the expression reduces to 4 * 2 , or 8 . (Going from right to left would give 12 / 6 , or 2 . Here the choice does matter.) In the previous example, the two * operators did not share a common operand, so the left-to-right rule did not apply.

Trying the Rules

Let's try these rules on a more complex example (see Listing 5.7).

Listing 5.7 The rules.c program.
 /* rules.c -- precedence test */ #include <stdio.h> int main(void) {   int top, score;   top = score = -(2 + 5) * 6 + (4 + 3 * (2 + 3));   printf("top = %d \n", top);   return 0; } 

What value will this program print? Figure it out; then run the program or read the following description to check your answer.

First, parentheses have the highest precedence. Whether the parentheses in -(2 + 5) * 6 or in (4 + 3 * (2 + 3)) are evaluated first depends on the implementation, as we just discussed. Either choice will lead to the same result for this example, so let's take the left one first. The high precedence of parentheses means that in the subexpression -(2 + 5) * 6 , you evaluate (2 + 5) first, getting 7. Next, you apply the unary minus operator to 7 to get -7 . Now the expression is this:

 top = score = -7 * 6 + (4 + 3 * (2 + 3)) 

The next step is to evaluate 2 + 3 . The expression becomes

 top = score = -7 * 6 + (4 + 3 * 5) 

Next, because the * in the parentheses has priority over + , the expression becomes

 top = score = -7 * 6 + (4 + 15) 

and then

 top = score = -7 * 6 + 19 

Multiply -7 by 6 and get this expression:

 top = score = -42 + 19 

Then addition makes it

 top = score = -23 

Now score is assigned the value -23 , and, finally, top gets the value -23 . Remember that the = operator associates from right to left.

I l @ ve RuBoard


C++ Primer Plus
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2000
Pages: 314
Authors: Stephen Prata

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