Was ist falsch an diesem C-Code von 1988?

Lesezeit: 9 Minuten

Benutzeravatar von César
César

Ich versuche, dieses Stück Code aus dem Buch “The C Programming Language” (K & R) zu kompilieren. Es ist eine Bare-Bones-Version des UNIX-Programms wc:

#include <stdio.h>

#define IN   1;     /* inside a word */
#define OUT  0;     /* outside a word */

/* count lines, words and characters in input */
main()
{
    int c, nl, nw, nc, state;

    state = OUT;
    nl = nw = nc = 0;
    while ((c = getchar()) != EOF) {
        ++nc;
        if (c == '\n')
            ++nl;
        if (c == ' ' || c == '\n' || c == '\t')
            state = OUT;
        else if (state == OUT) {
            state = IN;
            ++nw;
        }
    }
    printf("%d %d %d\n", nl, nw, nc);
}

Und ich erhalte folgenden Fehler:

$ gcc wc.c 
wc.c: In function ‘main’:
wc.c:18: error: ‘else’ without a previous ‘if’
wc.c:18: error: expected ‘)’ before ‘;’ token

Die 2. Auflage dieses Buches ist von 1988 und ich bin ziemlich neu in C. Vielleicht hat es mit der Compiler-Version zu tun oder vielleicht rede ich nur Unsinn.

Ich habe in modernem C-Code eine andere Verwendung von gesehen main Funktion:

int main()
{
    /* code */
    return 0;
}

Ist dies ein neuer Standard oder kann ich immer noch eine typlose Hauptdatei verwenden?

  • Keine Antwort, aber ein weiteres Stück Code zum genaueren Betrachten, || c = '\t'). Scheint das dasselbe zu sein wie der andere Code in dieser Zeile?

    – Benutzer7116

    27. Dezember 2011 um 3:19 Uhr

  • 32 Upvotes für eine Debugging + Tippfehler-Frage?!

    – Leichtigkeitsrennen im Orbit

    27. Dezember 2011 um 15:48 Uhr

  • @TomalakGeret’kal: Weißt du, alte Sachen werden mehr geschätzt (Wein, Gemälde, C-Code)

    – Sergio Tulentsev

    27. Dezember 2011 um 17:29 Uhr


  • @César: Ich habe durchaus das Recht, meine Meinung zu äußern, und ich danke Ihnen, dass Sie nicht versuchen, sie zu zensieren. Zufälligerweise ist dies keine Website zum Debuggen Ihres Codes und zum Beheben Ihrer Tippfehler, bei denen es sich um “lokalisierte” Probleme handelt, die niemandem sonst helfen werden. Es ist eine Website für Fragen zu Programmiersprachen, nicht um Ihre grundlegende Debugging- und Referenzarbeit für Sie zu erledigen. Der Skilllevel ist völlig egal. Lesen Sie die FAQ, und vielleicht auch diese Metafrage.

    – Leichtigkeitsrennen im Orbit

    28. Dezember 2011 um 15:18 Uhr


  • @TomalakGeret’kal natürlich kannst du deine Meinung äußern und ich werde deinen Kommentar nicht zensieren, obwohl er unkonstruktiv ist. Die FAQ habe ich bereits gelesen. Ich bin ein begeisterter Programmierer nach einem fragen eigentliches Problem, mit dem ich konfrontiert bin

    – César

    28. Dezember 2011 um 15:26 Uhr

Benutzeravatar von user7116
Benutzer7116

Ihr Problem liegt bei Ihren Präprozessordefinitionen von IN und OUT:

#define IN   1;     /* inside a word */
#define OUT  0;     /* outside a word */

Beachten Sie, wie Sie in jedem davon ein nachgestelltes Semikolon haben. Wenn der Präprozessor sie erweitert, sieht Ihr Code ungefähr so ​​aus:

    if (c == ' ' || c == '\n' || c == '\t')
        state = 0;; /* <--PROBLEM #1 */
    else if (state == 0;) { /* <--PROBLEM #2 */
        state = 1;;

Das zweite Semikolon verursacht die else keine Vorgeschichte haben if als Streichholz, weil Sie keine geschweiften Klammern verwenden. Entfernen Sie also die Semikolons aus den Präprozessordefinitionen von IN und OUT.

Die hier gelernte Lektion ist die Präprozessoranweisungen müssen nicht mit einem Semikolon enden.

Außerdem sollten Sie immer Zahnspangen verwenden!

    if (c == ' ' || c == '\n' || c == '\t') {
        state = OUT;
    } else if (state == OUT) {
        state = IN;
        ++nw;
    }

Es gibt kein Hängen-else Mehrdeutigkeit im obigen Code.

  • Zur Verdeutlichung, das Problem ist nicht der Abstand, sondern die Semikolons. Sie brauchen sie nicht in Präprozessoranweisungen.

    – Dan

    27. Dezember 2011 um 3:21 Uhr

  • @ Dan danke für die Klarstellung! Und die Semikolons waren tatsächlich das Problem! Danke Leute!

    – César

    27. Dezember 2011 um 3:23 Uhr

  • @César: gerne geschehen. Der Verstrebungsvorschlag wird Sie hoffentlich in Zukunft vor Ärger bewahren, hat mir auf jeden Fall geholfen!

    – Benutzer7116

    27. Dezember 2011 um 3:33 Uhr

  • @César: Es ist auch eine gute Idee, sich daran zu gewöhnen, Makros in Klammern zu setzen, da Sie im Allgemeinen möchten, dass das Makro zuerst ausgewertet wird. In diesem Fall spielt es keine Rolle, da der Wert ein einzelnes Token ist, aber das Weglassen von Klammern kann zu unerwarteten Ergebnissen führen, wenn ein Ausdruck definiert wird.

    – Stil

    27. Dezember 2011 um 8:25 Uhr

  • “brauche sie nicht” != “sollte sie nicht haben”. ersteres ist immer wahr; Letzteres ist kontextabhängig und in diesem Szenario das relevantere Problem.

    – Leichtigkeitsrennen im Orbit

    27. Dezember 2011 um 15:46 Uhr


Das Hauptproblem bei diesem Code ist, dass es so ist nicht der Code von K&R. Es enthält Semikolons nach den Makrodefinitionen, die im Buch nicht vorhanden waren, was, wie andere betont haben, die Bedeutung ändert.

Außer wenn Sie eine Änderung vornehmen, um den Code zu verstehen, sollten Sie ihn in Ruhe lassen, bis Sie ihn verstanden haben. Sie können nur Code, den Sie verstehen, sicher ändern.

Dies war wahrscheinlich nur ein Tippfehler Ihrerseits, aber es verdeutlicht die Notwendigkeit des Verständnisses und der Aufmerksamkeit für Details beim Programmieren.

  • Ihr Rat ist nicht besonders konstruktiv für jemanden, der Programmieren lernt. Das Ändern von Code ist genau das, was Sie unter den Details der Programmierung verstehen.

    – Benutzer7116

    27. Dezember 2011 um 21:49 Uhr

  • @sixlettervariables: Und dabei sollten Sie wissen, welche Änderungen Sie vorgenommen haben, und so wenig Änderungen wie möglich vornehmen. Wenn das OP die Änderungen absichtlich vorgenommen und so wenig wie möglich geändert hätte, hätte er diese Frage wahrscheinlich nicht gestellt, da ihm klar gewesen wäre, was los war. Er hätte das Makro für IN ohne Fehler geändert und dann das Makro für OUT mit den beiden Fehlern, von denen sich der zweite über das gerade hinzugefügte Semikolon beschwert hätte.

    – jmoreno

    27. Dezember 2011 um 22:07 Uhr

  • Es scheint, als würden Sie wahrscheinlich nicht wissen, dass Sie sie nicht einfügen sollen, es sei denn, Sie machen den Fehler, ein Semikolon am Ende einer Präprozessor-Direktive einzufügen. Sie könnten es für bare Münze nehmen, Sie könnten eine Menge Code lesen und feststellen, dass sie nie da zu sein scheinen. Oder das OP könnte es vermasseln, indem es sie einschließt, nach dem “bizarren” Fehler fragt und herausfindet: Hoppla, für Präprozessoranweisungen sind keine Semikolons erforderlich! Das ist Programmierung, keine Episode von Scared Straight.

    – Benutzer7116

    27. Dezember 2011 um 22:17 Uhr


  • @sixlettervariables: Ja, aber wenn der Code nicht funktioniert, ist der offensichtliche erste Schritt zu sagen: “Oh, ok, was ich dann ohne irgendeinen Grund von dem Code geändert habe, der in einem Buch des Erfinders von C geschrieben wurde, war wahrscheinlich der Problem. Ich werde das dann einfach rückgängig machen.“

    – Leichtigkeitsrennen im Orbit

    28. Dezember 2011 um 0:42 Uhr

  • Lassen Sie uns diese Diskussion im Chat fortsetzen

    – Benutzer7116

    28. Dezember 2011 um 1:54 Uhr

Benutzeravatar von onemach
einmach

Nach den Makros dürfen keine Semikolons stehen,

#define IN   1     /* inside a word */
#define OUT  0     /* outside a word */

und das sollte es wohl sein

if (c == ' ' || c == '\n' || c == '\t')

  • Danke, die Semikolons waren das Problem. Der zweite war ein Tippfehler!

    – César

    27. Dezember 2011 um 3:24 Uhr

  • Beim nächsten Mal bitte einfügen genau Code, den Sie verwenden, direkt aus Ihrem Texteditor.

    – Leichtigkeitsrennen im Orbit

    27. Dezember 2011 um 15:46 Uhr

  • @TomalakGeret’kal Nun, ich habe es nicht getan und ich werde es tun, aber wie hast du es gefunden?

    – Einmach

    28. Dezember 2011 um 0:34 Uhr

  • @onemach: Du hast das gesagt ; war ein Tippfehler, der das Problem nicht beeinflusst hat, was bedeutet, dass ein Tippfehler in Ihrer Frage und nicht in dem von Ihnen eingegebenen Code vorliegt eigentlich Gebraucht.

    – Leichtigkeitsrennen im Orbit

    28. Dezember 2011 um 0:41 Uhr

Benutzeravatar von Óscar López
Oscar López

Die Definitionen von IN und OUT sollten wie folgt aussehen:

#define IN   1     /* inside a word  */
#define OUT  0     /* outside a word */

Die Semikolons verursachten das Problem! Die Erklärung ist einfach: Sowohl IN als auch OUT sind Präprozessordirektiven, im Wesentlichen ersetzt der Compiler alle Vorkommen von IN durch eine 1 und alle Vorkommen von OUT durch eine 0 im Quellcode.

Da der ursprüngliche Code ein Semikolon nach der 1 und der 0 hatte, erzeugte das zusätzliche Semikolon nach der Zahl beim Ersetzen von IN und OUT im Code einen ungültigen Code, zum Beispiel diese Zeile:

else if (state == OUT)

Sah am Ende so aus:

else if (state == 0;)

Aber was du wolltest war folgendes:

else if (state == 0)

Lösung: Entfernen Sie das Semikolon nach den Zahlen in der ursprünglichen Definition.

Benutzeravatar von Jayan
Jayan

Wie Sie sehen, gab es ein Problem in Makros.

GCC hat eine Option für Stoppen nach der Vorverarbeitung. (-E) Diese Option ist nützlich, um das Ergebnis der Vorverarbeitung zu sehen. Tatsächlich ist die Technik wichtig, wenn Sie mit einer großen Codebasis in c/c++ arbeiten. Typischerweise haben Makefiles ein Ziel, das nach der Vorverarbeitung gestoppt werden soll.

Zum schnellen Nachschlagen: Die SO-Frage behandelt die Optionen – Wie sehe ich eine C/C++-Quelldatei nach der Vorverarbeitung in Visual Studio?. Es beginnt mit vc++, hat aber auch unten erwähnte gcc-Optionen.

Benutzeravatar von BillRobertson42
BillRobertson42

Nicht gerade ein Problem, aber die Deklaration von main() ist auch veraltet, es sollte so etwas sein.

int main(int argc, char** argv) {
    ...
    return 0;
}

Der Compiler nimmt einen int-Rückgabewert für eine Funktion ohne einen an, und ich bin sicher, dass der Compiler/Linker die fehlende Deklaration für argc/argv und den fehlenden Rückgabewert umgehen wird, aber sie sollten vorhanden sein.

Benutzeravatar von Peter Mortensen
Peter Mortensen

Versuchen Sie, explizite Klammern um Codeblöcke hinzuzufügen. Das K&R-Stil kann mehrdeutig sein.

Sehen Sie sich Zeile 18 an. Der Compiler teilt Ihnen mit, wo das Problem liegt.

    if (c == '\n') {
        ++nl;
    }
    if (c == ' ' || c == '\n' || c == '\t') { // You're missing an "=" here; should be "=="
        state = OUT;
    }
    else if (state == OUT) {
        state = IN;
        ++nw;
    }

  • Vielen Dank! Tatsächlich funktionierte der Code ohne die geschweiften Klammern im zweiten if 🙂

    – César

    27. Dezember 2011 um 3:26 Uhr

  • +1. Nicht nur zweideutig, sondern etwas gefährlich. Wenn (falls) Sie eine Zeile zu Ihrer hinzufügen if Wenn Sie später vergessen, die geschweiften Klammern hinzuzufügen, weil Ihr Block jetzt mehr als eine Zeile umfasst, kann es eine Weile dauern, bis dieser Fehler behoben ist …

    – Die 111

    27. Dezember 2011 um 3:26 Uhr


  • @The111 Ist mir noch nie passiert. Ich glaube immer noch nicht, dass das ein echtes Problem ist. Ich benutze den klammerlosen Stil seit über einem Jahrzehnt, ich habe nie vergessen, die Klammern hinzuzufügen, wenn ich den Körper eines Blocks erweitere.

    – Konrad Rudolf

    27. Dezember 2011 um 9:59 Uhr


  • @The111: In diesem Fall brauchten ein paar SO-Mitarbeiter eine Handvoll Minuten: P Und wenn Sie ein Programmierer sind, der in der Lage ist, Anweisungen zu einem hinzuzufügen if -Klausel und “Vergessen”, die geschweiften Klammern zu aktualisieren, dann sind Sie kein sehr guter Programmierer.

    – Leichtigkeitsrennen im Orbit

    27. Dezember 2011 um 15:47 Uhr


1420470cookie-checkWas ist falsch an diesem C-Code von 1988?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy