it-swarm.com.de

Warum ist der logische NOT-Operator in C-Sprachen "!" und nicht "~~"?

Für binäre Operatoren haben wir sowohl bitweise als auch logische Operatoren:

& bitwise AND
| bitwise OR

&& logical AND
|| logical OR

NOT (ein unärer Operator) verhält sich jedoch anders. Es gibt ~ für bitweise und! für logisch.

Ich erkenne, dass NOT eine unäre Operation im Gegensatz zu AND und OR ist, aber ich kann mir keinen Grund vorstellen, warum die Designer sich entschieden haben, von dem Prinzip abzuweichen, dass Single bitweise und Double logisch ist, und ging für ein anderes Zeichen stattdessen. Ich denke, Sie könnten es falsch lesen, wie eine doppelte bitweise Operation, die immer den Operandenwert zurückgibt. Aber das scheint mir kein wirkliches Problem zu sein.

Gibt es einen Grund, warum ich vermisse?

42
Martin Maat

Seltsamerweise beginnt die Geschichte der Programmiersprache im C-Stil nicht mit C.

Dennis Ritchie erklärt die Herausforderungen von Cs Geburt in dieser Artikel .

Beim Lesen wird deutlich, dass C einen Teil seines Sprachdesigns von seinem Vorgänger BCPL und insbesondere von den Operatoren geerbt hat. Im Abschnitt "Neugeborenes C" des oben genannten Artikels wird erläutert, wie BCPLs & Und | Mit zwei neuen Operatoren && Und || Angereichert wurden. Die Gründe waren:

  • aufgrund der Verwendung in Kombination mit == war eine andere Priorität erforderlich.
  • unterschiedliche Auswertelogik: Auswertung von links nach rechts mit Kurzschluss (dh wenn afalse in a&&b ist, b wird nicht ausgewertet).

Interessanterweise schafft diese Verdoppelung keine Mehrdeutigkeit für den Leser: a && b Wird nicht als a(&(&b)) falsch interpretiert. Unter dem Gesichtspunkt der Analyse gibt es auch keine Mehrdeutigkeit: &b Könnte sinnvoll sein, wenn b ein Wert wäre, aber es wäre ein Zeiger, während das bitweise & Erfordern würde ein ganzzahliger Operand, daher wäre das logische UND die einzig vernünftige Wahl.

BCPL hat bereits ~ Für die bitweise Negation verwendet. Unter dem Gesichtspunkt der Konsistenz hätte es also verdoppelt werden können, um einen ~~ Zu geben, um ihm seine logische Bedeutung zu geben. Leider wäre dies äußerst zweideutig gewesen, da ~ Ein unärer Operator ist: ~~b Könnte auch ~(~b)) bedeuten. Deshalb musste für die fehlende Negation ein anderes Symbol gewählt werden.

110
Christophe

Ich kann mir keinen Grund vorstellen, warum die Designer beschlossen haben, von dem Prinzip abzuweichen, dass einfach hier bitweise und doppelt logisch ist.

Das ist überhaupt nicht das Prinzip; Sobald Sie das erkennen, macht es mehr Sinn.

Der bessere Weg, an & Vs && Zu denken, ist nicht binär und boolesch. Der bessere Weg ist, sie als eifrig und faul zu betrachten. Der Operator & Führt die linke und rechte Seite aus und berechnet dann das Ergebnis. Der Operator && Führt die linke Seite und dann nur dann die rechte Seite aus, wenn dies zur Berechnung des Ergebnisses erforderlich ist.

Anstatt über "binär" und "boolesch" nachzudenken, sollten Sie darüber hinaus darüber nachdenken, was wirklich passiert. Die "binäre" Version ist nur führt die Boolesche Operation für ein Array von Booleschen Werten aus, das in ein Word gepackt wurde.

Also lasst es uns zusammenstellen. Ist es sinnvoll, eine faule Operation durchzuführen auf einem Array von Booleschen Werten? Nein, da es keine "linke Seite" gibt, die zuerst überprüft werden muss. Es gibt 32 "linke Seiten", die zuerst überprüft werden müssen. Also beschränken wir die faulen Operationen auf einen single Boolean, und daher kommt Ihre Intuition, dass einer von ihnen "binär" und einer "Boolean" ist, von dort, aber das ist ein Konsequenz des Designs, nicht des Designs selbst!

Und wenn Sie es so sehen, wird klar, warum es keinen !! Und keinen ^^ Gibt. Keiner dieser Operatoren verfügt über die Eigenschaft, dass Sie die Analyse eines der Operanden überspringen können. Es gibt kein "faul" not oder xor.

Andere Sprachen machen dies klarer; Einige Sprachen verwenden and, um "eifrig und" zu bedeuten, aber and also, um beispielsweise "faul und" zu bedeuten. Und andere Sprachen machen auch klarer, dass & Und && Nicht "binär" und "boolesch" sind; In C # können beide Versionen beispielsweise Boolesche Werte als Operanden verwenden.

51
Eric Lippert

TL; DR

C hat die Operatoren ! Und ~ Von einer anderen Sprache geerbt. Sowohl && Als auch || 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 basierten, die auf CPL basierten, die auf ALGOL basierte.

ALGOL , der Urgroßvater von C++, Java und C #, definiert wahr und falsch auf eine Art und Weise, die sich anfühlte Intuitiv für Programmierer: „Wahrheitswerte, die als Binärzahl (wahr entsprechend 1 und falsch bis 0) gleich dem intrinsischen Integralwert sind. Ein Nachteil davon ist jedoch, dass logisch und bitweise nicht die sein können Gleiche Operation: Auf jedem modernen Computer ist ~0 gleich -1 statt 1 und ~1 gleich -2 statt 0. (Selbst auf einem 60 Jahre alten Mainframe, bei dem ~0 steht für -0 oder INT_MIN, ~0 != 1 auf jeder jemals hergestellten CPU, und der C-Sprachstandard verlangt dies seit vielen Jahren, während die meisten seiner Tochtersprachen sich nicht einmal die Mühe machen, Vorzeichen zu unterstützen -und-Größe oder überhaupt-Ergänzung.)

ALGOL hat dies umgangen, indem es verschiedene Modi hatte und Operatoren im booleschen und integralen Modus unterschiedlich interpretierte. Das heißt, eine bitweise Operation war eine für ganzzahlige Typen und eine logische Operation war eine für boolesche Typen.

BCPL hatte einen separaten booleschen Typ, aber ein einzelner not -Operator , sowohl für bitweise als auch für logisch 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 zu etwas völlig anderem in Sprachen der C-Familie entwickelt hat. Wir würden das heute „die Objektdarstellung“ nennen. in C.)

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 #define TRUE -1 Sagen.

Aber die Programmiersprache B war schwach typisiert und hatte keine booleschen oder sogar Gleitkommatypen. Alles war das Äquivalent von int in 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 wahr oder falsch als logischen Wert verwendet. Es definierte zuerst einen wahrheitsgemäßen Ausdruck als "ungleich Null". Dies war auf den Minicomputern, auf denen es ausgeführt wurde und die ein CPU-Null-Flag hatten, effizient.

Zu dieser Zeit 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: UNIX, das von denselben Personen zur gleichen Zeit entwickelt wurde, definiert alle Fehlercodes als negative Ganzzahlen. Viele seiner Systemaufrufe geben bei einem Fehler einen von mehreren unterschiedlichen negativen Werten zurück.) Seien Sie also dankbar: es hätte schlimmer sein können!

Die Definition von TRUE als 1 Und FALSE als 0 In B bedeutete jedoch, dass die Identität true = ~ false Nicht mehr bestand und die Starke Typisierung, die es ALGOL ermöglichte, zwischen bitweisen und logischen Ausdrücken zu unterscheiden. Dies erforderte einen neuen logisch-nicht-Operator, und die Designer wählten !, Möglicherweise weil ungleich gleich bereits != War, was wie ein vertikaler Balken durch ein Gleichheitszeichen aussieht. Sie folgten nicht der gleichen Konvention wie && Oder ||, Da noch keine existierte.

Sie sollten wohl haben: Der Operator & In B ist wie geplant defekt. In B und in C ist 1 & 2 == FALSE, Obwohl 1 Und 2 Beide Wahrheitswerte sind, und es gibt keine intuitive Möglichkeit, die logische Operation in B auszudrücken. Das war ein Fehler C versuchte, dies teilweise durch Hinzufügen von && Und || Zu korrigieren, aber das Hauptanliegen war zu dieser Zeit, endlich einen Kurzschluss zum Laufen zu bringen und Programme schneller laufen zu lassen. Der Beweis dafür ist, dass es keinen ^^ Gibt: 1 ^ 2 Ist ein wahrer Wert, obwohl beide Operanden wahr sind, aber er kann nicht von einem Kurzschluss profitieren.

22
Davislor