CBFalconer said:
Because the C [parsing] rules [for various operators] are weird
and wonderful, and totally confusing to anyone used to a rational
set of rules.
They have always seemed very well thought out to me, but I understand
from other postings of yours that we will not agree on this.
The main exception to this is the binding of "&" and "|". Dennis
Ritchie explained their mis-placement in a Usenet posting many years
ago. Once upon a time, C did not have "&&" and "||" operators.
Instead, if you wanted to check whether p was non-NULL before
accessing p->field, you wrote [%]:
if (p != NULL & p->field)
The compiler would figure out that you meant "&&" by seeing that
the "&" operator was placed in a boolean context -- i.e., the
test inside an if (or while loop or similar) -- and do a logical
AND instead of a bitwise AND. After experience with this, the
Bell Labs folks decided to put in a separate "&&" operator, so
that not only could you write:
if (p != NULL && p->field)
(with the obvious meaning), but you could also do:
result = p != NULL && p->field;
(with the logical AND meaning, rather than the bitwise AND you
would get with a single "&" -- remember that prototypic-C was
extremely cavalier about types, so the compiler would cheerfully
treat the pointer as an integer here).
When the operators were split, the syntax was not adjusted, leaving
the now-always-bitwise & and | operators in the "wrong" positions,
precedence/binding-wise. The result is that:
if (x & MASK == 0)
binds as:
if (x & (MASK == 0))
instead of the "more obvious":
if ((x & MASK) == 0)
because prototypic-C took this to mean:
if (x && MASK == 0)
which clearly *should* (and still does) bind as:
if (x && (MASK == 0))
-----
[%] Actually, "p && p->field" would be more likely, or perhaps
"p != 0 && p->field". Not only was the language quite casual about
interchanging integers and pointers, so were programmers, who
rather often wrote odd code like:
0770440->bar = addr;
In those days, no casts were required, and none used.