Saturday, April 5, 2008

C language Technical Interview 2008 questions 5

Expressions

. Why doesn't this code:
a[i] = i++;
work?
A: The subexpression i++ causes a side effect -- it modifies i's value -- which leads to undefined behavior since i is also referenced elsewhere in the same expression, and there's no way to determine whether the reference (in a[i] on the left-hand side) should be to the old or the new value. (Note that although the language in K&R suggests that the behavior of this expression is unspecified, the C Standard makes the stronger statement that it is undefined.

2. Under my compiler, the code
int i = 7;
printf("%d\n", i++ * i++);
prints 49. Regardless of the order of evaluation, shouldn't it print 56?

A: Although the postincrement and postdecrement operators ++ and -- perform their operations after yielding the former value, the implication of "after" is often misunderstood. It is *not* guaranteed that an increment or decrement is performed immediately after giving up the previous value and before any other part of the expression is evaluated. It is merely guaranteed that the update will be performed sometime before the expression is considered "finished" (before the next "sequence
point," in ANSI C's terminology; ). In the example, the compiler chose to multiply the previous value by itself and to perform both increments afterwards.

The behavior of code which contains multiple, ambiguous side effects has always been undefined. (Loosely speaking, by "multiple, ambiguous side effects" we mean any combination of ++, --, =, +=, -=, etc. in a single expression which causes the same object either to be modified twice or modified and then inspected. This is a rough definition; Don't even try to find out how your compiler implements such things (contrary to the ill-advised exercises in many C
textbooks);

3. I've experimented with the code
int i = 3;
i = i++;
on several compilers. Some gave i the value 3, and some gave 4. Which compiler is correct?

A: There is no correct answer; the expression is undefined. Also, note that neither i++ nor ++i is the same as i+1. If you want to increment i, use i=i+1, i+=1, i++, or ++i, not some combination.

4. Here's a slick expression:
a ^= b ^= a ^= b
It swaps a and b without using a temporary.
A: Not portably, it doesn't. It attempts to modify the variable a twice between sequence points, so its behavior is undefined.
For example, it has been reported that when given the code
int a = 123, b = 7654;
a ^= b ^= a ^= b;
the SCO Optimizing C compiler (icc) sets b to 123 and a to 0.

5. Can I use explicit parentheses to force the order of evaluation I want? Even if I don't, doesn't precedence dictate it?
A: Not in general.
Operator precedence and explicit parentheses impose only a partial ordering on the evaluation of an expression. In the expression
f() + g() * h()
although we know that the multiplication will happen before the addition, there is no telling which of the three functions will be called first.
When you need to ensure the order of subexpression evaluation, you may need to use explicit temporary variables and separate statements.

6. But what about the && and operators?
I see code like "while((c = getchar()) != EOF && c != '\n')" ...
A: There is a special "short-circuiting" exception for those operators. The right-hand side is not evaluated if the left- hand side determines the outcome (i.e. is true for or false for &&). Therefore, left-to-right evaluation is guaranteed, as it also is for the comma operator. Furthermore, all of these operators (along with ?:) introduce an extra internal sequence point

7. How can I understand these complex expressions? What's a "sequence point"?
A: A sequence point is a point in time (at the end of the evaluation of a full expression, or at the , &&, ?:, or comma operators, or just before a function call) at which the dust has settled and all side effects are guaranteed to be complete. The ANSI/ISO C Standard states that
Between the previous and next sequence point an object shall have its stored value modified at
most once by the evaluation of an expression. Furthermore, the prior value shall be accessed
only to determine the value to be stored.
The second sentence can be difficult to understand. It says that if an object is written to within a full expression, any and all accesses to it within the same expression must be for the purposes of computing the value to be written. This rule effectively constrains legal expressions to those in which the accesses demonstrably precede the modification.

8. So given
a[i] = i++;
we don't know which cell of a[] gets written to, but i does get incremented by one, right?
A: *No*. Once an expression or program becomes undefined, *all* aspects of it become undefined. See questions 3.2, 3.3, 11.33, and 11.35.

9. If I'm not using the value of the expression, should I use i++ or ++i to increment a variable?
A: Since the two forms differ only in the value yielded, they are entirely equivalent when only their side effect is needed. (However, the prefix form is preferred in C++.)

10. Why doesn't the code
int a = 1000, b = 1000;
long int c = a * b;
work?
A: Under C's integral promotion rules, the multiplication is carried out using int arithmetic, and the result may overflow or be truncated before being promoted and assigned to the long int left-hand side. Use an explicit cast to force long arithmetic:
long int c = (long int)a * b;
Note that (long int)(a * b) would *not* have the desired effect.
A similar problem can arise when two integers are divided, with the result assigned to a floating-point variable; the solution is similar, too.

11. I have a complicated expression which I have to assign to one of two variables, depending on a condition. Can I use code like this?
((condition) ? a : b) = complicated_expression;
A: No. The ?: operator, like most operators, yields a value, and you can't assign to a value. (In other words, ?: does not yield an "lvalue".) If you really want to, you can try something like
*((condition) ? &a : &b) = complicated_expression;
although this is admittedly not as pretty.

No comments: