Note that the "->" operator works on *any* value, not just pointers.
Well, not *any* value. Early C was quick to convert an integer to
a pointer, which is what is required here. I'm not sure it tolerated
a float here, and it certainly didn't tolerate a struct.
Since this "early C" did not look at the left hand side of the
"." and "->" operators, it really did require different operators
to achieve different effects.
Sort of. x->y is fundamentally equivalent to (*x).y, so you can
always write a program using just one of the two operators. So
in this sense one of the two (usually x->y) can be thought of as
pure syntactic sugar.
These odd aspects of C were fixed
even before the very first C book came out, but -- as with the
"wrong" precedence for the bitwise "&" and "|" operators -- the
historical baggage went along for the ride.
It's not just baggage. C is unique among programming languages
in minimizing the amount of compiler guessing. Some languages
let you write *x in place of x (to use C terminology), or f in
place of f(). (Algol 68 called these guesses "dereferencing" and
"deproceduring", and defined four different kinds of contexts for
disambiguating.) In C, you *can* write f in place of &f (where f
is a function name) and, as of C90 at least, you can write pf(x)
in place of (*pf)(x). With C99 you can write even sin(x) in place
of sinf(x), but the list of generics is strictly bounded.
We could indeed now accept x.y for x a pointer, but keeping both
x->y and x.y is entirely in keeping with the C philosophy of
requiring explicit notation.
P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com