Some Additional Operators

I l @ ve RuBoard

Some Additional Operators

C has about 40 operators, but some are used much more than others. The ones just covered are the most common, but let's add four more useful operators to the list.

The sizeof Operator

You used the sizeof operator in Chapter 3, "Data and C." To review, the sizeof operator returns the size, in bytes, of its operand. (Recall that a C byte is defined as the size used by the char type. In the past, this has most often been 8 bits, but some character sets may use larger bytes.) The operand can be a specific data object, such as the name of a variable, or it can be a type. If it is a type, such as float , the operand must be enclosed in parentheses. For instance, the example in Listing 5.8 shows both forms. (Some users may need to replace %d with %ld .)

Listing 5.8 The sizeof.c program.
 /* sizeof.c -- uses sizeof operator */ #include <stdio.h> int main(void) {   int n = 0;   printf("n = %d, n has %d bytes; all ints have %d bytes.\n",        n, sizeof n, sizeof (int) );   return 0; } 

Modulus Operator: %

The modulus operator is used in integer arithmetic. It gives the remainder that results when the integer to its left is divided by the integer to its right. For example, 13 % 5 (read as "13 modulo 5") has the value 3, because 5 goes into 13 twice, with a remainder of 3. Don't bother trying to use this operator with floating-point numbers . It just won't work.

At first glance, this operator might strike you as an esoteric tool for mathematicians, but actually it is rather practical and helpful. One common use is to help you control the flow of a program. Suppose, for example, you are working on a bill-preparing program designed to add in an extra charge every third month. Just have the program evaluate the month number modulo 3 (that is, month % 3 ) and check to see whether the result is 0. If it is, the program adds in the extra charge. After you learn about "if statements" in Chapter 7, "C Control Statements: Branching and Jumps," you'll understand this better.

Listing 5.9 shows another use for the % operator.

Listing 5.9 The min_sec.c program.
 /* min_sec.c -- converts seconds to minutes and seconds */ #include <stdio.h> #define SEC_PER_MIN 60       /* seconds in a minute          */ int main(void) {    int sec, min, left;    printf("Convert seconds to minutes and seconds!\n");    printf("Enter the number of seconds you wish to convert.\n");    scanf("%d", &sec);        /* number of seconds is read in */    min = sec / SEC_PER_MIN;  /* truncated number of minutes  */    left = sec % SEC_PER_MIN; /* number of seconds left over  */    printf("%d seconds is %d minutes, %d seconds.\n", sec, min,           left);    return 0; } 

Here is a sample output:

 Convert seconds to minutes and seconds! Enter the number of seconds you wish to convert.  234  234 seconds is 3 minutes, 54 seconds. 

One problem with this interactive program is that it processes just one input value. Can you figure out a way to have the program prompt you repeatedly for new input values? We will return to this problem later in the "Review Questions," but if you can work out a solution first, congratulations.

One matter the language leaves open is the result of using integer division and the modulus operator when one of the operands is negative. One way of interpreting truncating a result is tossing away the fractional part, so 11/5 is 2 and -11/5 is -2. In this case, 11%5 is 1 and -11%5 is -1. This is the most common implementation. However, you could interpret truncation as resulting in the largest integer less or equal to the result. This definition gives the same behavior for positive numbers, but results in -11/5 being -3 because -3 is the largest integer less than -2.2. In this case, -11%5 would be +4 because you would add 4/5 to -3 to get to -2.2.

To put the two options another way, suppose you entered a value of -234 when running Listing 5.9. The first way of handling negative numbers would report the result as being -3 minutes, -54 seconds, but the second would report the result as being -4 minutes, 6 seconds. The ANSI committee felt it was important to provide a portable solution that would work on all systems, but it did not want to disrupt existing code that already assumed one or the other method. So they added the div() function, which uses the first method. This method involves using structures, so we'll defer futher mention until Appendix F, "The ANSI Standard C Library."

Increment and Decrement Operators: ++ and --

The increment operator performs a simple task; it increments ( increases ) the value of its operand by 1. This operator comes in two varieties. The first variety has the ++ come before the affected variable; this is the prefix mode. The second variety has the ++ after the affected variable; this is the postfix mode. The two modes differ with regard to the precise time that the incrementing takes place. We'll explain the similarities first and then return to that difference. The short example in Listing 5.10 shows how the increment operators work.

Listing 5.10 The add_one.c program.
 /* add_one.c -- incrementing: prefix and postfix */ #include <stdio.h> int main(void) {    int ultra = 0, super = 0;    while (super < 5)    {       super++;       ++ultra;       printf("super = %d, ultra = %d \n", super, ultra);    }    return 0; } 

Running add_one.c produces this output:

 super = 1, ultra = 1 super = 2, ultra = 2 super = 3, ultra = 3 super = 4, ultra = 4 super = 5, ultra = 5 

The program counted to 5 twice and simultaneously . I confess that the same results could have been achieved by replacing the two increment statements with this:

 super = super + 1; ultra = ultra + 1; 

These are simple enough statements. Why bother creating one, let alone two, abbreviations? One reason is that the compact form makes your programs neater and easier to follow. These operators give your programs an elegant gloss that cannot fail to please the eye. For instance, you can rewrite part of shoe2.c (refer to Listing 5.2) this way:

 shoe = 3.0; while (shoe < 18.5) {     foot = SCALE*size + ADJUST;     printf("%10.1f %20.2f inches\n", shoe, foot);     ++shoe; } 

However, you still haven't taken full advantage of the increment operator. You can shorten the fragment this way:

 shoe = 2.0; while (++shoe < 18.5) {    foot = SCALE*shoe + ADJUST;    printf("%10.1f %20.2f inches\n", shoe, foot); } 

Here you have combined the incrementing process and the while comparison into one expression. This type of construction is so common in C that it merits a closer look.

First, how does this construction work? Simply. The value of shoe is increased by 1 and then compared to 18.5 . If it is less than 18.5 , the statements between the braces are executed once. Then shoe is increased by 1 again, and the cycle is repeated until shoe gets too big. You changed the initial value of shoe from 3.0 to 2.0 to compensate for shoe being incremented before the first evaluation of foot (see Figure 5.4).

Figure 5.4. Through the loop once.
graphics/05fig04.jpg

Second, what's so good about this approach? It is more compact. More important, it gathers in one place the two processes that control the loop. The primary process is the test: Do you continue or not? In this case, the test is checking to see whether the shoe size is less than 18.5. The secondary process changes an element of the test; in this case, the shoe size is increased.

Suppose you forgot to change the shoe size. Then shoe would always be less than 18.5 , and the loop would never end. The computer would churn out line after identical line, caught in a dreaded "infinite loop." Eventually, you would lose interest in the output and have to kill the program somehow. Having the loop test and the loop change at one place, instead of at separate locations, helps you to remember to update the loop.

A disadvantage is that combining two operations in a single expression can make the code harder to follow and can make it easier to make counting errors.

Another advantage of the increment operator is that it usually produces slightly more efficient machine language code because it is similar to actual machine language instructions. However, as implementors produce better C compilers, this advantage may disappear. A smart compiler recognizes that x = x + 1 can be treated the same as ++x .

Finally, these operators have an additional feature that can be useful in certain delicate situations. To find out what this feature is, try running the program in Listing 5.11.

Listing 5.11 The post_pre.c program.
 /* post_pre.c -- postfix vs prefix */ #include <stdio.h> int main(void) {    int a = 1, b = 1;    int aplus, plusb;    aplus = a++;       /* postfix */    plusb = ++b;       /* prefix  */    printf("a   aplus   b   plusb \n");    printf("%1d %5d %5d %5d\n", a, aplus, b, plusb);    return 0; } 

If you and your compiler do everything correctly, you should get this result:

 a   aplus   b   plusb 2     1     2     2 

Both a and b were increased by 1, as promised . However, aplus has the value of a before a changed, but plusb has the value of b after b changed. This is the difference between the prefix form and the postfix form (see Figure 5.5).

Figure 5.5. Prefix and postfix.
graphics/05fig05.jpg
 aplus = a++;  /* postfix: a is changed after its value is used */ plusb = ++b;  /* prefix: b is changed before its value is used */ 

When one of these increment operators is used by itself, as in a solitary ego++; statement, it doesn't matter which form you use. The choice does matter, however, when the operator and its operand are part of a larger expression, as in the assignment statements you just saw. In this kind of situation, you must give some thought to the result you want. For instance, recall that we suggested using the following:

 while (++shoe < 18.5) 

This test condition provided a table up to size 18. If you use shoe++ instead of ++shoe , the table will go to size 19 because shoe will be increased after the comparison instead of before.

Of course, you could fall back on the less subtle form,

 shoe = shoe + 1; 

but then no one will believe you are a true C programmer.

We suggest you pay special attention to the examples of increment operators as you read through this book. Ask yourself if you could have used the prefix and the suffix forms interchangeably or if circumstances dictated a particular choice.

Decrementing: --

For each form of increment operator, there is a corresponding form of decrement operator. Instead of ++ , use -- :

 -- count;   /* prefix form of decrement operator  */ count --;   /* postfix form of decrement operator */ 

Listing 5.12 illustrates that computers can be accomplished lyricists.

Listing 5.12 The bottles.c program.
 /* bottles.c -- counting down */ #include <stdio.h> #define MAX 100 int main(void) {   int count = MAX + 1;   while (--count > 0) {      printf("%d bottles of beer on the wall, "             "%d bottles of beer!\n", count, count);      printf("Take one down and pass it around,\n");      printf("%d bottles of beer!\n\n", count - 1);   }   return 0; } 

The output starts like this:

 100 bottles of beer on the wall, 100 bottles of beer! Take one down and pass it around, 99 bottles of beer! 99 bottles of beer on the wall, 99 bottles of beer! Take one down and pass it around, 98 bottles of beer! 

It goes on a bit and ends this way:

 1 bottles of beer on the wall, 1 bottles of beer! Take one down and pass it around, 0 bottles of beer! 

Apparently the accomplished lyricist has a problem with plurals, but that could be fixed by using the conditional operators of Chapter 8, "Character Input/Output and Redirection."

Incidentally, the > operator stands for "is greater than." Like < ("is less than"), it is a relational operator . You will take a longer look at relational operators in Chapter 6, "C Control Statements: Looping."

Precedence

The increment and decrement operators have a very high precedence of association; only parentheses are higher. Therefore, x*y++ means (x)*(y++) , not (x*y)++ , which is fortunate because the latter is invalid. The increment and decrement operators affect a variable (or, more generally , a modifiable lvalue), and the combination x*y is not itself a variable, although its parts are.

Don't confuse precedence of these two operators with the order of evaluation. Suppose you have the following:

 y = 2; n = 3; nextnum = (y + n++)*6; 

What value does nextnum get? Substituting in values yields this:

 nextnum = (2 + 3)*6 = 5*6 = 30 

Only after n is used is it increased to 4 . Precedence tells us that the ++ is attached only to the n , not to 2 + n . It also tells us when the value of n is used for evaluating the expression, but the nature of the increment operator determines when the value of n is changed.

When n++ is part of an expression, you can think of it as meaning "use n ; then increment it." On the other hand, ++n means "increment n ; then use it."

Don't Be Too Clever

You can get fooled if you try to do too much at once with the increment operators. For example, you might think that you could improve on the squares.c program (refer to Listing 5.4) to print integers and their squares by replacing the while loop with this one:

 while (num < 21)    {    printf("%10d %10d\n", num, num*num++);    } 

This looks reasonable. You print the number num , multiply it by itself to get the square, and then increase num by 1. In fact, this program may even work on some systems, but not all. The problem is that when printf() goes to get the values for printing, it might evaluate the last argument first and increment num before getting to the other argument. Therefore, instead of printing, say,

 5          25 

it may print

 6          25 

In C, the compiler can choose which arguments in a function to evaluate first. This freedom increases compiler efficiency, but can cause trouble if you use an increment operator on a function argument.

Another possible source of trouble is a statement like this one:

 ans = num/2 + 5*(1 + num++); 

Again, the problem is that the compiler may not do things in the same order you have in mind. You would think that it would find num/2 first and then move on, but it might do the last term first, increase num , and use the new value in num/2 . There is no guarantee.

Yet another troublesome case is this:

 n = 3; y = n++ + n++; 

Certainly, n winds up larger by 2 after the statement is executed, but the value for y is ambiguous. A compiler can use the old value of n twice in evaluating y and then increment n twice. This gives y the value 6 , and n the value 5 , or it can use the old value once, increment n once, use that value for the second n in the expression, and then increment n a second time. This gives y the value 7 , and n the value 5 . Either choice is allowable . More exactly, the result is undefined, which means the C standard fails to define what the result should be.

You can easily avoid these problems:

  • Don't use increment or decrement operators on a variable that is part of more than one argument of a function.

  • Don't use increment or decrement operators on a variable that appears more than once in an expression.

On the other hand, C does have some guarantees about when incrementation takes place. We'll return to this subject when we discuss sequence points.

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