Ich habe dieses einfache Programm:
#include <stdio.h>
struct S
{
int i;
};
void swap(struct S *a, struct S *b)
{
struct S temp;
temp = *a /* Oops, missing a semicolon here... */
*a = *b;
*b = temp;
}
int main(void)
{
struct S a = { 1 };
struct S b = { 2 };
swap(&a, &b);
}
Wie auf zB ideone.com zu sehen, gibt dies einen Fehler:
prog.c: In function 'swap': prog.c:12:5: error: invalid operands to binary * (have 'struct S' and 'struct S *') *a = *b; ^
Warum erkennt der Compiler das fehlende Semikolon nicht?
Hinweis: Diese Frage und ihre Antwort sind durch diese Frage motiviert . Obwohl es ähnliche Fragen gibt , habe ich nichts gefunden, was die Freiformkapazität der C-Sprache erwähnt, die diese und verwandte Fehler verursacht.
Antworten:
C ist eine Freiformsprache . Das heißt, Sie können es auf viele Arten formatieren und es wird immer noch ein legales Programm sein.
Zum Beispiel eine Aussage wie
könnte wie geschrieben werden
oder wie
Also, wenn der Compiler die Zeilen sieht
es denkt, es bedeutet
Das ist natürlich kein gültiger Ausdruck und der Compiler wird sich darüber beschweren, anstatt das fehlende Semikolon. Der Grund, warum es nicht gültig ist, ist, dass
a
es sich um einen Zeiger auf eine Struktur handelt. Daher*a * a
wird versucht, eine Strukturinstanz (*a
) mit einem Zeiger auf eine Struktur (a
) zu multiplizieren .Der Compiler kann das fehlende Semikolon zwar nicht erkennen, meldet aber auch den völlig unabhängigen Fehler in der falschen Zeile. Dies ist wichtig zu beachten, da unabhängig davon, wie oft Sie auf die Zeile schauen, in der der Fehler gemeldet wird, dort kein Fehler vorliegt. Manchmal müssen Sie bei solchen Problemen in den vorherigen Zeilen nachsehen , ob sie in Ordnung und fehlerfrei sind.
Manchmal müssen Sie sogar in einer anderen Datei suchen, um den Fehler zu finden. Wenn beispielsweise eine Header-Datei eine Struktur definiert, die zuletzt in der Header-Datei ausgeführt wurde, und das Semikolon zum Beenden der Struktur fehlt, liegt der Fehler nicht in der Header-Datei, sondern in der Datei, die die Header-Datei enthält.
Und manchmal wird es sogar noch schlimmer: Wenn Sie zwei (oder mehr) Header-Dateien einfügen und die erste eine unvollständige Deklaration enthält, wird der Syntaxfehler höchstwahrscheinlich in der zweiten Header-Datei angezeigt.
Damit verbunden ist das Konzept der Follow-up - Fehler. Einige Fehler, die normalerweise auf fehlende Semikolons zurückzuführen sind, werden als mehrere Fehler gemeldet . Aus diesem Grund ist es wichtig, beim Beheben von Fehlern von oben zu beginnen, da durch das Beheben des ersten Fehlers möglicherweise mehrere Fehler verschwinden.
Dies kann natürlich dazu führen, dass jeweils ein Fehler behoben wird und häufig neu kompiliert wird, was bei großen Projekten umständlich sein kann. Das Erkennen solcher Folgefehler ist jedoch mit Erfahrung verbunden, und nachdem Sie sie einige Male gesehen haben, ist es einfacher, die tatsächlichen Fehler herauszufinden und mehr als einen Fehler pro Neukompilierung zu beheben.
quelle
temp = *a * a = *b
könnte ein gültiger Ausdruck sein, wenn eroperator*
überladen wäre. (Die Frage ist jedoch als "C" markiert.)Es gibt drei Dinge zu beachten.
*
in C kann sowohl ein unärer als auch ein binärer Operator sein. Als unärer Operator bedeutet es "Dereferenzierung", als binärer Operator bedeutet er "multiplizieren".Das Ergebnis dieser beiden Tatsachen ist, wenn wir analysieren.
Der erste und der letzte
*
werden als unär interpretiert, der zweite*
als binär. Aus syntaktischer Sicht sieht dies in Ordnung aus.Erst nach dem Parsen, wenn der Compiler versucht, die Operatoren im Kontext ihrer Operandentypen zu interpretieren, wird ein Fehler angezeigt.
quelle
Einige gute Antworten oben, aber ich werde näher darauf eingehen.
Dies ist tatsächlich ein Fall, in
x = y = z;
dem beidenx
undy
dem Wert von zugewiesen wirdz
.Was Sie sagen, ist
the contents of address (a times a) become equal to the contents of b, as does temp
.Kurz gesagt,
*a *a = <any integer value>
ist eine gültige Aussage. Wie bereits erwähnt,*
dereferenziert der erste einen Zeiger, während der zweite zwei Werte multipliziert.quelle
y
handelt es sich jedoch nicht einmal um eine Variable, sondern um den Ausdruck*a *a
, und Sie können dem Ergebnis einer Multiplikation keine Zuordnung zuweisen.Die meisten Compiler analysieren Quelldateien der Reihe nach und melden die Zeile, in der sie feststellen, dass etwas nicht stimmt. Die ersten 12 Zeilen Ihres C-Programms können der Start eines gültigen (fehlerfreien) C-Programms sein. Die ersten 13 Zeilen Ihres Programms können nicht. Einige Compiler notieren den Ort von Dingen, auf die sie stoßen, die an und für sich keine Fehler sind, und lösen in den meisten Fällen später im Code keine Fehler aus, sind jedoch möglicherweise in Kombination mit etwas anderem nicht gültig. Beispielsweise:
Die Erklärung
int foo;
an sich wäre vollkommen in Ordnung. Ebenso die Erklärungfloat foo;
. Einige Compiler zeichnen möglicherweise die Zeilennummer auf, in der die erste Deklaration angezeigt wurde, und ordnen dieser Zeile eine Informationsnachricht zu, um dem Programmierer zu helfen, Fälle zu identifizieren, in denen die frühere Definition tatsächlich die fehlerhafte ist. Compiler behalten möglicherweise auch die mit so etwas wie a verknüpften Zeilennummern beido
, die gemeldet werden können, wenn die zugehörigenwhile
nicht an der richtigen Stelle angezeigt werden . In Fällen, in denen der wahrscheinliche Ort des Problems unmittelbar vor der Zeile liegt, in der der Fehler entdeckt wird, müssen Compiler im Allgemeinen keinen zusätzlichen Bericht für die Position hinzufügen.quelle