Entschuldigen Sie, wenn diese Frage naiv ist. Betrachten Sie das folgende Programm:
#include <stdio.h>
int main() {
int i = 1;
i = i + 2;
5;
i;
printf("i: %d\n", i);
}
Im obigen Beispiel erscheinen die Anweisungen 5;
und i;
völlig überflüssig, der Code wird jedoch standardmäßig ohne Warnungen oder Fehler kompiliert (gcc gibt jedoch eine warning: statement with no effect [-Wunused-value]
Warnung aus, wenn mit ausgeführt wird -Wall
). Sie haben keine Auswirkungen auf den Rest des Programms. Warum werden sie also überhaupt als gültige Aussagen angesehen? Ignoriert der Compiler sie einfach? Gibt es irgendwelche Vorteile, solche Aussagen zuzulassen?
;
danach gesetzt wird. Es würde die Sprache komplizieren, weitere Regeln hinzuzufügen, wenn Ausdrücke keine Anweisungen sein könnenprintf()
? Die Anweisung5;
lautet im Wesentlichen "Mach was5
auch immer (nichts) und ignoriere das Ergebnis. Deine Aussageprintf(...)
lautet" Mach wasprintf(...)
auch immer macht und ignoriere die Ergebnisse (der Rückgabewert vonprintf()
) ". C behandelt diese gleich. Dies ermöglicht auch Code wie"(void) i;
woi
ist " Ein Parameter für eine Funktion, die Sievoid
printf()
einen Effekt hat, selbst wenn Sie den Wert ignorieren, den es schließlich zurückgibt. Im Gegensatz dazu5;
hat überhaupt keine Wirkung.Antworten:
Ein Vorteil beim Zulassen solcher Anweisungen besteht darin, dass Code von Makros oder anderen Programmen erstellt wird und nicht von Menschen geschrieben wird.
Stellen Sie sich als Beispiel eine Funktion vor
int do_stuff(void)
, die bei Erfolg 0 oder bei Misserfolg -1 zurückgeben soll. Es kann sein, dass die Unterstützung für "Zeug" optional ist, und Sie könnten eine Header-Datei haben, die dies tutStellen Sie sich nun einen Code vor, der nach Möglichkeit etwas tun möchte, sich aber möglicherweise nicht wirklich darum kümmert, ob er erfolgreich ist oder fehlschlägt:
Wenn
STUFF_SUPPORTED
0 ist, erweitert der Präprozessor den Aufruffunc2
zu einer Anweisung, die gerade gelesen wirdund so sieht der Compiler-Pass nur die Art von "überflüssiger" Aussage, die Sie zu stören scheint. Doch was kann man noch tun? Wenn Sie
#define do_stuff() // nothing
, dann wird der Code infunc1
brechen. (Und Sie haben immer noch eine leere Anweisungfunc2
, die nur liest;
, was vielleicht noch überflüssiger ist.) Wenn Sie andererseits tatsächlich einedo_stuff()
Funktion definieren müssen, die -1 zurückgibt, können die Kosten eines Funktionsaufrufs anfallen aus keinem guten Grund.quelle
((void)0)
.assert
.Einfache Anweisungen in C werden durch Semikolon abgeschlossen.
Einfache Anweisungen in C sind Ausdrücke. Ein Ausdruck ist eine Kombination aus Variablen, Konstanten und Operatoren. Jeder Ausdruck führt zu einem Wert eines bestimmten Typs, der einer Variablen zugewiesen werden kann.
Allerdings könnten einige "Smart Compiler" 5 verwerfen; und ich; Aussagen.
quelle
void
hat keinen Wert.Aussagen ohne Wirkung sind zulässig, da es schwieriger wäre, sie zu verbieten, als sie zuzulassen. Dies war relevanter, als C zum ersten Mal entworfen wurde und die Compiler kleiner und einfacher waren.
Eine Ausdrucksanweisung besteht aus einem Ausdruck, gefolgt von einem Semikolon. Sein Verhalten besteht darin, den Ausdruck auszuwerten und das Ergebnis (falls vorhanden) zu verwerfen. Normalerweise besteht der Zweck darin, dass die Bewertung des Ausdrucks Nebenwirkungen hat, aber es ist nicht immer einfach oder sogar möglich festzustellen, ob ein bestimmter Ausdruck Nebenwirkungen hat.
Ein Funktionsaufruf ist beispielsweise ein Ausdruck, sodass ein Funktionsaufruf gefolgt von einem Semikolon eine Anweisung ist. Hat diese Aussage irgendwelche Nebenwirkungen?
Es ist unmöglich zu sagen, ohne die Implementierung von zu sehen
some_function
.Wie wäre es damit?
Wahrscheinlich nicht - aber wenn
obj
definiert alsvolatile
, dann tut es.Ermöglicht , jeden Ausdruck in einem gemacht werden expression-Anweisung durch ein Semikolon macht die Sprachdefinition einfacher. Das Erfordernis, dass der Ausdruck Nebenwirkungen hat, würde die Sprachdefinition und den Compiler komplexer machen. C basiert auf einem konsistenten Regelwerk (Funktionsaufrufe sind Ausdrücke, Zuweisungen sind Ausdrücke, ein Ausdruck gefolgt von einem Semikolon ist eine Anweisung) und ermöglicht es Programmierern, das zu tun, was sie wollen, ohne sie daran zu hindern, Dinge zu tun, die möglicherweise sinnvoll sind oder nicht.
quelle
Die Anweisungen, die Sie ohne Auswirkung aufgelistet haben, sind Beispiele für eine Ausdrucksanweisung , deren Syntax in Abschnitt 6.8.3p1 des C-Standards wie folgt angegeben ist:
Der gesamte Abschnitt 6.5 ist der Definition eines Ausdrucks gewidmet, aber lose ausgedrückt besteht ein Ausdruck aus Konstanten und Bezeichnern, die mit Operatoren verknüpft sind. Insbesondere kann ein Ausdruck einen Zuweisungsoperator enthalten oder nicht und er kann einen Funktionsaufruf enthalten oder nicht.
Also jeder gefolgt Ausdruck durch ein Semikolon qualifiziert als Ausdruck Aussage. Tatsächlich ist jede dieser Zeilen aus Ihrem Code ein Beispiel für eine Ausdrucksanweisung:
Einige Operatoren enthalten Nebenwirkungen, wie z. B. den Satz von Zuweisungsoperatoren und die Operatoren vor / nach dem Inkrementieren / Dekrementieren, und der Funktionsaufrufoperator
()
kann abhängig von der Funktion der betreffenden Funktion einen Nebeneffekt haben. Es ist jedoch nicht erforderlich, dass einer der Bediener eine Nebenwirkung hat.Hier ist ein weiteres Beispiel:
Dies ruft eine Funktion auf und verwirft das Ergebnis, genau wie der Aufruf
printf
in Ihrem Beispiel, aber im Gegensatzprintf
zum Funktionsaufruf selbst hat dies keine Nebenwirkung.quelle
Manchmal sind solche Aussagen sehr praktisch:
Oder wenn das Referenzhandbuch uns auffordert, nur die Register zu lesen, um etwas zu archivieren - zum Beispiel um ein Flag zu löschen oder zu setzen (sehr häufige Situation in der uC-Welt)
https://godbolt.org/z/6wjh_5
quelle
*SREG
es flüchtig ist,*SREG;
hat es keine Auswirkung auf das im C-Standard angegebene Modell. Der C-Standard gibt an, dass er eine beobachtbare Nebenwirkung hat.