TL; DR
C hat die Operatoren !
und ~
von einer anderen Sprache geerbt . Beide &&
und ||
wurden Jahre später von einer anderen Person hinzugefügt.
Lange Antwort
Historisch gesehen entwickelte sich C aus den frühen Sprachen B, die auf BCPL beruhten, das auf CPL beruhte, das auf Algol beruhte.
Algol , der Urgroßvater von C ++, Java und C #, definierte wahr und falsch auf eine Weise, die sich für Programmierer intuitiv anfühlte: „Wahrheitswerte, die als Binärzahl betrachtet werden (wahr entspricht 1 und falsch ist 0), sind das gleiche wie der innere Integralwert ”. Ein Nachteil davon ist jedoch, dass logisch und bitweise nicht die gleiche Operation sein kann: Auf jedem modernen Computer ist ~0
-1 anstelle von 1 und ~1
-2 anstelle von 0. (Sogar auf einem 60 Jahre alten Mainframe ist ~0
- 0 oder INT_MIN
, ~0 != 1
auf jeder jemals hergestellten CPU, und der C-Sprachstandard verlangt dies seit vielen Jahren, während sich die meisten seiner Tochtersprachen überhaupt nicht darum kümmern, Vorzeichen und Größe oder das eigene Komplement zu unterstützen.)
Algol hat dies umgangen, indem es verschiedene Modi hatte und die Operatoren im Booleschen und im Integralmodus unterschiedlich interpretierte. Das heißt, eine bitweise Operation war eine für Integer-Typen, und eine logische Operation war eine für Boolesche Typen.
BCPL hatte einen separaten Booleschen Typ, aber einen einzelnen not
Operator , sowohl für bitweise als auch für logische nicht. Die Art und Weise, wie dieser frühe Vorläufer von C diese Arbeit machte, war:
Der R-Wert von true ist ein Bitmuster, das vollständig aus Einsen besteht. Der R-Wert von false ist Null.
Beachten Sie, dass true = ~ false
(Sie werden feststellen , dass sich der Begriff rvalue in den Sprachen der C-Familie zu einem völlig anderen Begriff entwickelt hat. Wir würden das heute als „Objektrepräsentation“ in C bezeichnen.)
Diese Definition würde es logisch und bitweise ermöglichen, nicht dieselbe maschinensprachliche Anweisung zu verwenden. Wenn C diesen Weg gegangen wäre, würden Header-Dateien auf der ganzen Welt sagen #define TRUE -1
.
Aber die Programmiersprache B war schwach typisiert und hatte weder Boolesche noch Gleitkommatypen. Alles war das Äquivalent zu int
seinem Nachfolger C. Dies machte es für die Sprache zu einer guten Idee, zu definieren, was passiert ist, wenn ein Programm einen anderen Wert als true oder false als logischen Wert verwendet. Zuerst definierte es einen wahrheitsgemäßen Ausdruck als "ungleich Null". Dies war auf den Minicomputern, auf denen es lief und die ein CPU-Null-Flag hatten, effizient.
Damals gab es eine Alternative: Dieselben CPUs hatten auch ein negatives Flag, und der Wahrheitswert von BCPL war -1, sodass B möglicherweise stattdessen alle negativen Zahlen als wahr und alle nicht negativen Zahlen als falsch definiert hat. (Es gibt einen Rest dieses Ansatzes: Viele Systemaufrufe in UNIX, die von denselben Personen zur selben Zeit entwickelt wurden, definieren alle Fehlercodes als negative Ganzzahlen. Viele ihrer Systemaufrufe geben bei einem Fehler einen von mehreren negativen Werten zurück.) Also sei dankbar: es hätte schlimmer kommen können!
Aber die Definition TRUE
wie 1
und FALSE
wie 0
in B bedeutet , dass die Identität true = ~ false
nicht mehr zu halten, und sie hatte die starke Typisierung fallen gelassen , die Algol eindeutig zu machen zwischen bitweise und logische Ausdrücke erlaubt. Das erforderte einen neuen logisch-nicht-Operator, und die Designer wählten ihn aus !
, möglicherweise, weil es bereits !=
einen ungleichen Operator gab, der durch ein Gleichheitszeichen wie ein vertikaler Strich aussieht. Sie folgten nicht der gleichen Konvention wie &&
oder ||
weil es noch keine gab.
Möglicherweise sollte dies der Fall sein: Der &
Operator in B ist fehlerhaft. In B und C, 1 & 2 == FALSE
obwohl 1
und 2
beiden truthy Werte sind, und es gibt keine intuitive Art und Weise die logische Operation in B. um auszudrücken , dass ein Fehler C versuchte , war teilweise zu korrigieren , indem das Hinzufügen &&
und ||
, aber das Hauptanliegen war zu der Zeit zu Endlich kann der Kurzschluss funktionieren und Programme können schneller ausgeführt werden. Der Beweis dafür ist, dass es kein ^^
: gibt, 1 ^ 2
ist ein wahrer Wert, obwohl beide Operanden wahr sind, aber es kann nicht von einem Kurzschluss profitieren.