Strangely, the history of C-style programming language doesn’t start with C.
Dennis Ritchie explains well the challenges of C’s birth in this article.
When reading it, it becomes obvious that C inherited a part of its language design from its predecessor BPCLBCPL, and especially the operators. The section “Neonatal C” of the aforementioned article explains how BCPL’s &
and |
were enriched with two new operators &&
and ||
. The reasons were:
- different priority was required due to its use in combination with
==
- different evaluation logic: left-to-right evaluation with short-circuit short-circuit (i.e when
a
isfalse
ina&&b
,b
is not evaluated).
Interestingly, this doubling does not create any ambiguity for the reader: a && b
will not be misinterpreted as a(&(&b))
. From a parsing point of view, there is no ambiguity either: &b
could make sense if b
were an lvalue, but it would be a pointer whereas the bitwise &
would require an integer operand, so the logical andAND would be the only reasonable choice.
BCPL already used ~
for bitwise negation. So from a point of view of consistency, it could have been doubled to give a ~~
to give it its logical meaning. Unfortunately this would have been extremely ambiguous since ~
is a unary operator: ~~b
could also mean ~(~b))
. This is why another symbol had to be chosen for the missing negation.