Section 4. Expressions

work?

The subexpression i++ causes a side effect -- it modifies i's
value -- which leads to undefined behavior if i is also
referenced elsewhere in the same expression. (Note that
although the language in K&R suggests that the behavior of this
expression is unspecified, the ANSI/ISO C Standard makes the
stronger statement that it is undefined -- see question 5.23.)

prints 49. Regardless of the order of evaluation, shouldn't it
print 56?

Although the postincrement and postdecrement operators ++ and --
perform the operations after yielding the former value, the
implication of "after" is often misunderstood. It is not
guaranteed that the operation 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 (see question 5.23).
Don't even try to find out
how your compiler implements such things (contrary to the
ill-advised exercises in many C textbooks); as K&R wisely point out,
"if you don't know how they are done on various machines, that
innocence may help to protect you."

on several compilers. Some gave i the value 2, some gave 3, but
one gave 4. I know the behavior is undefined, but how could it
give 4?

A compiler may do anything it likes when faced with undefined
behavior (and, within limits, with implementation-defined and
unspecified behavior), including doing what you expect. It's
unwise to depend on it, though. See also question 5.18.

There is a special exception for those operators, (as well as the ?: operator); each of them does imply a sequence point (i.e. left-to-right evaluation is guaranteed). Any book on C should make this
clear.

work?

Under C's integral promotion rules, the multiplication is
carried out using int arithmetic, and the result may overflow
and/or be truncated before being 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 the code (long int)(a * b) would not
have the desired effect.