C Language

One Blankety-Blank Trap After Another

It helps to know about C's traps and pitfalls, and it never hurts to know a few arcane C rules to amaze your programming friends and baffle your boss. The next exercise serves both purposes. Read the following code slowly and carefully. Then before reading the solution, write down what you think the value of x is at the end of this sequence of statements.

 float x=4.0;
 float y=2.0;
 float *z;

Did you remember that the post-increment ++ adds 1 to y after getting the value of y (2.0) to use in the expression? Did you also notice that z points to the same memory location as x and thus has the same value, 4.0? Did you come up with 2.0/4.0, or 0.5, as the result? Or did you find, deep in your C manual, that C uses "greedy" lexical analysis (i.e., tries to make the next identifier or operator in the input stream as long as possible) and thus treats /* as the beginning of a comment, not as the division operator followed by the dereferencing operator? The resulting expression is the same as:

 x=y++   /*z
  ... */

So the value of x is 2.0, and the compiler digests most of the rest of the program as a comment. One absent blank (between / and *) makes a world of difference. This may seem like a contrived problem, but consider several seemingly simpler, and more likely, assignment statements:

 z = &y;
 x = y + *z;
 x = ++ *z;
 x = *z ++;

The second statement adds the value of y (2.0) and the contents of the location z points to (also 2.0) and puts the result in x. No surprises here. The third statement increments the contents of the location z points to (changing the value from 2.0 to 3.0) and assigns the new value to x. No surprises here either. But the final statement, which looks similar to the others, is actually quite different. This statement assigns 3.0 (the new value of *z) to x - so far so good - and then increments the address stored in z, rather than the contents of the location z points to. Because unary increment operators (++ and -) bind more tightly than the dereferencing operator (*), you have to use parentheses to apply post-increments to a dereferenced pointer:

 x = (*z) ++;

Of course, if we rewrite the previous expressions using the contents_of macro,

 x = y++/contents_of(z);
 x = y + contents_of(z);
 x = ++ contents_of(z);
 x = contents_of(z) ++;

the lack of blanks and C's tricky operator precedence aren't a problem - another bonus of using readability macros.