Hm. I suppose I'll ask... 1) What was the original rationale for that
design choice?
[DISCLAIMER: This whole thing is second-hand knowledge from memory.
I believe Dennis Ritchie once wrote an article on the topic; check
Google and Google Groups if you really want to know.]
Some pre-C language (i.e., maybe B, BCPL, or intermediates) had the
clever idea of overloading & and | in boolean contexts; that is,
(i < j & j < k)
would short-circuit and produce a boolean result (which in this
context means an integer 0 or 1), while
(i + j & j + k)
would *not* short-circuit (not even if i+j==0) and not necessarily
produce a boolean result.
When pre-standard C came along, it inherited this overloading. But
then the designers said, "Hey, this is really confusing!" and decided
to split & and | into (& and |) and (&& and ||), one pair for arithmetic
contexts and one pair for boolean contexts.
So some poor fool probably had to go through all the old pre-C code,
doing a search-and-replace on
if (i==j & j==k)
changing it to
if (i==j && j==k)
and so on. It would have been too much to ask of him to actually
change the *precedence* of the operators, as well as the spelling!
So && and || sneaked into the precedence table right below the
old & and | operators, and nobody had to add any parentheses.
2) Why didn't C99 address it?
What would C99 have done? Changed the precedence tables? Do
you have *any* idea how confusing that would be, especially when
some hapless newbie Googles "c precedence table" and finds that
the top two results contradict each other? Not to mention the
vast body of C89 code out there that would break if the precedences
were changed, or the annoyance it would cause to the designers of
C-like languages like C++, Java, et al. - which of the two precedence
tables would *they* use now?
Moral: Design well, and design early, because once a re-design
becomes a good idea, it's no longer a good idea.
-Arthur