Wednesday, 16 November 2011

Does operator precedence always work? in C programming

Does operator precedence always work?

The rules for operator precedence are a little complicated. In most cases, they’re set up to do what you need. Arguably, a few of the rules could have been done better.

Quick review: “Operator precedence” is the collection of rules about which “operators” (such as + and = and such) take “precedence,” that is, which are calculated first. In mathematics, an expression such as 2´3+4´5 is the same as (2´3)+(4´5); the multiplication happens before the addition. That means that multiplication “takes precedence over” addition, or multiplication “has higher precedence than” addition.

There are no fewer than 16 levels of operator precedence in C. It turns out having that many rules can make C programs slightly harder to read sometimes, but much easier to write. That’s not the only way to make that tradeoff, but it’s the C way. The levels of operator precedence are summarized in Table XVI.1.

Summary of operator precedence (highest to lowest).
---------------------------------------------------------------------------------------------------------------
Level                         Operators
---------------------------------------------------------------------------------------------------------------
1                                x[y] (subscript)
                                  x(y) (function call)
                                  x.y (member access)
                                  x->y (member pointer access)
                                  x++ (postincrement)
                                  x-- (postdecrement)
2                                 ++x (increment)
                                 --x (decrement)
                                   &x (address-of)
                                  *x (pointer indirection)
                                  +x (same as x, just as in mathematics)
                                  -x (mathematical negation)
                                   !x (logical negation)
                                  ~x (bitwise negation)
                                   sizeof x and sizeof(x_t) (size in bytes)
3                                 (x_t)y (type cast)
4                                  x*y (multiplication)
                                   x/y (division)
                                    x%y (remainder)
5                                  x+y (addition)
                                    x-y (subtraction)
6                                  x<<y (bitwise left shift)
                                    x>>y (bitwise right shift)
7                                   x<y, x>y, x<=y, x>=y (relation comparisons)
8                                  x==y, x!=y (equality comparisons)
9                                  x&y (bitwise AND)
10                                 x^y (bitwise exclusive OR)
11                                 x|y (bitwise OR)
12                                 x&&y (logical AND)
13                                 x||y (logical OR)
14                                x?y:z (conditional)
15                               x=y, x*=y, x/=y, x+=y, x-=y, <<=, >>=, &=, ^=, |= (assignment; right           
16                                x,y (comma)
----------------------------------------------------------------------------------------------------------------
The highest level of precedence is postfix expressions, things that go after an expression. The next highest level is prefix or unary expressions, things that go before an expression. The next highest level after that istype cast.

NOTE
The most important thing to know about operator precedence is that *p++ means the same thing as *(p++); that is, the ++ operates on the pointer, not the pointed-to thing. Code such as *p++ = *q++ is very common in C. The precedence is the same as that for (*(p++)) = (*(q++)). In English, that means, “Increment q by one but use its old value, find the thing q points to, decrement p by one but use its old value, and assign the thing pointed to by q to the thing pointed to by p.” The value of the whole expression is the value of the thing originally pointed to by q. You’ll see code like this again and again in C, and you’ll have many opportunities to write code like this. You canlook up the other operator precedence rules when you can’t remember them. To be a good C programmer, though, you’ll have toknow what *p++ means without much conscious thought.

The original C compiler was written for a computer that had instructions to handle constructs such as *p++ and *p++ = *q++ incredibly efficiently. As a result, a lot of C code is written that way. As a further result, because there’s so much C code like that, people who design new computers make sure that there are very efficient instructions to handle these C constructs.

The next level of precedence is multiplication, division, and division remainder (also known as modulus).
After that comes addition and subtraction. Just as in mathematical expressions, 2*3+4*5 means the same
thing as (2*3)+(4*5).
The next level of precedence is bitwise shifting.
The next levels are the relational comparisons (such as x<y) and then the equality comparisons (x==y and
x!=y).
The next three levels are bitwise AND, exclusive OR, and OR, respectively.

NOTE

The third most important thing to know about operator precedence (after what *p++ and x=y=z mean) is that x&y==z is not the same as (x&y)==z. Because the precedence of the bitwise operators is lower than that of the comparison operators, x&y==z is the same as x&(y==z). That means “See whether y and z are equal (1 if they are, 0 if they aren’t), then bitwise AND x and the result of the comparison.” This is a far less likely thing to do than “bitwise AND x and y and see whether the resultis equal to z.” One might argue that the precedence of the bitwise operators should be higher than that of the comparison operators. It’s about 20 years too late to do anything about it. If you want to compare the results of a bitwise operation with something else, you need parentheses.

The next levels are the logical operators, such as x&&y and x||y. Note that logical AND has higher precedence than logical OR. That reflects the way we speak in English. For example, consider this: if (have_ticket && have_reservation
|| have_money && standby_ok) {
goto_airport();
}

In English, you would say, “If you have a ticket and you have a reservation, or if you have money and it’s OK to fly standby, go to the airport.” If you override the precedence with parentheses, you have a very
different condition:
/* not a recommended algorithm! */
if (have_ticket
&& (have_reservation || have_money)
&& standby_ok) {
goto_airport();
}

In English, you would say, “If you have a ticket, and if you have a reservation or you have money, and it’s OK to fly standby, go to the airport.”

The next level of precedence is the conditional expression, x?y:z. This is an if-then-else construct that’s in expression, not a statement. Sometimes conditional expressions make code much simpler; sometimes they’re obscure. Conditional expressions are right associative, which means that
a ? b : c ? d : e
means the same thing as this:
a ? b : (c ? d : e)
This is very much like an else-if construct.
The next level of precedence is assignment. All the assignment operators have the same precedence. Unlike all the other C binary operators, assignment is “right associative”; it’s done right to left, not left to right. x+y+z is the same as (x+y)+z, and x*y*z is the same as (x*y)*z, but x=y=z is the same as x=(y=z).

NOTE
The second most important thing to know about operator precedence (after what *p++ means) is what x=y=z means. Because assignment is right associative, it means x=(y=z), or in English, “Assign the value of z to y, and then assign that value to x.” It’s very common to see code such as this:
a = b = c = d = 0;
This assigns zero to d, then c, then b, and finally a, right to left.
The lowest level of precedence in C is the comma operator. The comma operator takes two expressions, evaluates the first one, throws it away, and evaluates the second one. This makes sense only if the first expression has a side effect, such as assignment or a function call. The comma and assignment operators are often used in for statements:
for (i=0, count=0; i < MAX; ++i) {
if (interesting(a[i]) {
++count;
}
}

Cross Reference:

I.6: Other than in a for statement, when is the comma operator used?
I.12: Is left-to-right or right-to-left order guaranteed for operator precedence?
I.13: What is the difference between ++var and var++?
I.14: What does the modulus operator do?
II.13: When should a type cast be used?
II.14: When should a type cast not be used?
VII.1: What is indirection?

No comments:

Post a Comment