Anscheinend kompiliert ein Compiler in gcc/C wann
if ((x=0)){ some code }
verwendet wird, während wann
if (x=0){ some code }
verwendet wird, weigert sich der Compiler zu kompilieren.
Was sind die Unterschiede zwischen zwei?
Als Anmerkung, ich weiß, was der Unterschied zwischen ist x==0
und x=0
. Ich untersuche gerade, wie sich C verhält, wenn es auf einige seltsame Codes stößt.
Es gibt keinen Unterschied, Code-weise.
Alles, was passiert, ist dieser Spruch x=0
Anstatt von x==0
ist ein so häufiger Fehler, dass die meisten Compiler eine Warnung (oder in Ihrem Fall einen Fehler) ausgeben, wenn sie ihn sehen. Der zusätzliche Satz Klammern ist ein gängiger Trick, um den Compiler zum Schweigen zu bringen – das Äquivalent zu sagen: “Ja, ich wollte das wirklich tun”.
Beides ist syntaktisch korrektes C und der Compiler muss damit klarkommen. Aber der Compiler kann je nach Konfiguration eine Warnung oder sogar einen Fehler ausgeben (z. B. -Werror in gcc), weil einer von ihnen so verdächtig ist, dass Sie niemals erwarten würden, dass dies beabsichtigt ist. Wenn Sie etwas wie verwenden if (x = 0) { ... }
(Ordnen Sie Null zu x
und führe den Block aus, wenn Null nicht Null ist), meinst du fast immer tatsächlich if (x == 0) { ... }
(Führen Sie den Block aus, wenn x
ist null).
Kommen wir nun zum Warum if ((x = 0)) { ... }
wird nicht als verdächtig genug angesehen, um die gleiche Art von Warnung zu rechtfertigen (dieser spezielle Code ist immer noch verdächtig, da die Bedingung immer mit Null ausgewertet wird und der Körper niemals ausgeführt wird) …
Es gibt eine von einigen C-Entwicklern (ich bin einer von ihnen) verwendete Redewendung, bei der Sie eine Zuweisung in Klammern setzen und die Funktion nutzen, dass sogar die Zuweisung selbst einen Wert hat, und zwar den zugewiesenen Wert.
Beispiel:
#include <stdio.h>
int main(int argc, char **argv)
{
int c;
while ((c = getchar()) != '\n')
printf("Character: '%c' (0x%02x)\n", c, c);
return 0;
}
Testen Sie das Beispiel:
$ ./test
Hello!
Character: 'H' (0x48)
Character: 'e' (0x65)
Character: 'l' (0x6c)
Character: 'l' (0x6c)
Character: 'o' (0x6f)
Character: '!' (0x21)
Entscheidend war der Zustand (c = getchar()) != '\n'
wo Sie zuerst das Ergebnis zuweisen getchar()
zu c
und dann auf einen bestimmten Wert prüfen. In diesem Fall lesen wir Zeichen nacheinander von der Standardeingabe bis zum und einer Zeile (technisch gesehen bis zum Lesen von a \n
Charakter). Der Hauptvorteil dieser Vorgehensweise besteht darin, dass Sie die Füllung füllen können getchar()
in die Prüfung. Andernfalls müssten Sie die Kommanotation verwenden, eine Endlosschleife mit Unterbrechung, oder sie sowohl vor die Schleife als auch an das Ende der Schleife setzen.
Manchmal vergleichen Sie mit Werten ungleich Null, wie z \n
, -1
und ähnliches, aber manchmal vergleichen Sie mit Null oder, wenn Sie mit Zeigern arbeiten, mit NULL
. Suchen wir ein Beispiel für NULL
was bei der Speicherzuweisung ziemlich üblich ist.
char *p;
if ((p = malloc(50)) == NULL) {
...handle error...
}
Man könnte es natürlich schreiben als:
char *p;
p = malloc(50);
if (p == NULL) {
...handle error...
}
Aber je nach Geschmack könnte man auch verwenden:
char *p;
if (!(p = malloc(50))) {
...handle error...
}
Oder drehen Sie es sogar umgekehrt (was übrigens dagegen spricht mein Bevorzugung, immer zuerst den Fehlerfall zu behandeln):
char *p;
if ((p = malloc(50))) {
...do stuff...
} else {
...handle error...
}
Im letzten Fall ist die Bedingung (p = malloc(50))
was genau äquivalent ist p = malloc(50)
letzteres ist aber wegen des bereits erwähnten höchst verdächtig gemeinsames Fehler bei der Durchführung einer Zuweisung anstelle eines Vergleichs in C und abgeleiteten Sprachen. Beachten Sie, dass es nicht nur um verdächtige Compiler geht, sondern auch um Menschen, die den Code lesen und sich den potenziellen Fehler ansehen.
Die redundanten Klammern sind einfach ein Mittel, um den Lesern und dem Compiler mitzuteilen, dass diese Zuweisung definitiv beabsichtigt ist und dass es sich nicht um ein Auftreten dieses häufigen Fehlers handelt.
Der Code sollte sich nicht “weigern”, zu kompilieren, es sei denn, Sie haben es getan -Werror
. Wenn Sie Warnungen aktiviert haben, wird Ihnen möglicherweise Folgendes angezeigt:
Warnung: Schlägt Klammern um die als Wahrheitswert verwendete Zuweisung vor [-Wparentheses]
while (*dest++ = *src++)
Insbesondere die GCC-Dokumente Sagen Sie dies über den Zweck der Warnung:
Warnen Sie, wenn Klammern in bestimmten Kontexten weggelassen werden, z. B. wenn eine Zuweisung in einem Kontext vorhanden ist, in dem ein Wahrheitswert erwartet wird, oder wenn Operatoren verschachtelt sind, deren Vorrang häufig verwirrt wird.
Und ich kann nicht verstehen, warum jemand das tun möchte
if ((x=0)) {...}
da diese Bedingung immer falsch sein wird. (Oderx=N
für jede KonstanteN
.) Wenn dies ein Code ist, auf den Sie in freier Wildbahn gestoßen sind, hat vielleicht jemand versucht, die Warnung zu unterdrücken, ohne zu verstehen, wofür die Warnung war.– flauschige
21. September 2014 um 17:10 Uhr
Übrigens, ein einfacher Trick, um viele Fälle zu erkennen, in denen = anstelle von == verwendet wurde, besteht darin, die Konstante immer auf der linken Seite zu platzieren — ‘if(0=x)’ erzeugt ein can’t-assign-to – Ständiger Fehler.
– Keschlam
21. September 2014 um 18:04 Uhr
@keshlam Auch bekannt als Yoda-Bedingungen.
– Paläc
21. September 2014 um 18:05 Uhr
Auch bekannt als Ihren Code weniger lesbar machen, weil Sie befürchten, dass Sie nicht wissen, wie man eintippt. 🙂
– chao
22. September 2014 um 4:09 Uhr
Welche Version von GCC und mit welchen Switches? Mit
-Wall
schlägt Red Hat 4.8.3-1 vor Klammern um die als Wahrheitswert verwendete Zuweisungaber es lässt sich gut kompilieren …– Denis
22. September 2014 um 4:25 Uhr