• Hallo Besucher!

    Du bist neu im Forum? Dann registriere dich hier, um Diskussionen beizutreten oder eigene Themen zu erstellen. Für die Registrierung ist es erforderlich, dass du einen Spielaccount bei Die Stämme hast.

    Andernfalls kannst du dich hier direkt einloggen.

    Falls du dein Passwort vergessen hast, kannst du hier ein neues Passwort anfordern.

[C++] Auswirkung von geschweiften Klammern bei If

DeletedUser

Gast
Hallo zusammen

Ich lerne zurzeit C++ mithilfe des Buches "Einführung in die Programmierung mit C++" von Bjarne Stroustrup. Zurzeit programmiere ich das Beispiel mit dem Taschenrechner. Jedoch habe ich hier zu eine Frage. Ich habe aufgrund meines Programmierstils immer die If's mit geschweiften Klammern gemacht. Deswegen kam bei Kommando "q" (für Schliessen/Verlassen) immer, dass der Faktor ungültig sei (Fehlermeldung, Fenster schliesst sich mit Enter). Jetzt habe ich mal die geschweiften Klammern beim If weggelassen und jetzt schliesst sich das Fenster wie gewollt sofort. Jetzt kommt meine Frage, wieso funktioniert es ohne die geschweiften Klammern einwandfrei und mit nicht?

C++-Code ohne die geschweiften Klammern am erwähnten Ort:
Code:
/* 
 * File:   Taschenrechner.cpp
 * Author: FalkenaugeMihawk
 *
 * Created on 3. September 2012, 21:30
 */

#include <iostream>
#include <stdexcept>
#include <string>
using namespace std;

void user_error(string s) {
    throw runtime_error(s);
}

class Token {
    public:
        char kind;      //Token-Kategorie
        double value;   //Für Zahlen: ein Wert
        Token(char ch):kind(ch), value(0) {
            
        }
        
        Token(char ch, double val):kind(ch), value(val) {
            
        }
};

class Token_stream {
    public:  //Benutzerschnittstelle
        Token_stream();        //Erstelle einen Token_stream, der aus cin liest
        Token get();           //Liest ein Token ein
        void putback(Token t); //Legt ein Token zurück
   private: //Implementierungsdetails
        bool full;             //Befindet sich ein Token im Buffer?
        Token buffer;          //Hier legen wir ein Token ab, das mit putback() zurückgestellt wird
};

Token_stream::Token_stream():full(false), buffer(0) { //Kein Token im Puffer
     
}
        
Token Token_stream::get() {
        if(Token_stream::full) {
                Token_stream::full = false;
                return Token_stream::buffer;
        }
            
        char ch;
        cin >> ch;
            
        switch(ch) {
                case ';':
                case 'q':
                case '(':
                case ')':
                case '+':
                case '-':
                case'*':
                case '/':
                    return Token(ch);
                break;
                case '.':
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                {
                        cin.putback(ch);
                        double val;
                        cin >> val;
                        return Token('8', val);
                }
                break;
                default:
                        user_error("Ungueltiges Token");
                break;
        }
}
        
void Token_stream::putback(Token t) {
        if(Token_stream::full) {
                user_error("Putback(): Zurueckstellen nicht möglich, Puffer voll");
        }
        
        Token_stream::buffer = t;  //kopiere t in den Puffer
        Token_stream::full = true; //Puffer ist jetzt voll
}

Token_stream ts;     //Liefert get() und putback()
double expression(); //Ausdruck-Regel; behandelt + und -
double term();       //Term-Regel; behandelt *, / und %
double primary();    //Faktor-Regel; behandelt Zahlen und Klammern

int main() {
    try {
        cout << "Willkommen zu unserem einfachen Taschenrechnerprogramm.\n" << "Bitte gebe einen Ausdruck mit reellen Zahlen ein.\n"
              << "Ausdruck: ";
        
        double val = 0;
        while(cin) {
            Token t = ts.get();
            
            if(t.kind == 'q') //'q' steht für Verlassen
                break;
            
            if(t.kind == ';') { //';' steht für Jetzt ausgeben
                cout << "Ergebnis: " << val << endl;
            } else {
                ts.putback(t);
            }
            
            val = expression();
        }
        return 0;
    } catch(exception& e) {
        cerr << "Fehler: " << e.what() << endl;
        cin.get();
        return 1;
    }
}

double expression() {
    double left = term();  //Lies einen Term und werte ihn aus
    Token t = ts.get();    //Lies das nächste Token ein
    
    while(true) {
        switch(t.kind) {
            case '+':
                left += term();
                t = ts.get();
            break;
            case '-':
                left -= term();
                t = ts.get();
            break;
            default:
                ts.putback(t);
                return left;
            break;
        }
    }
}

double term() {
    double left = primary();
    Token t = ts.get();
    while(true) {
        switch(t.kind) {
            case '*':
                left *= primary();
                t = ts.get();
            break;
            case '/':
            {
                double d = primary();
                if(d == 0) {
                     user_error("Division durch Null");
                }
                
                left /= primary();
                t = ts.get();
            }
            break;
            default:
                ts.putback(t);
                return left;
            break;
        }
    }
}

double primary() {
    Token t = ts.get();
    switch(t.kind) {
        case '(':
        {
            double d = expression();
            t = ts.get();
            if(t.kind != ')') {
                user_error("')' erwartet");
            }
            
            return d;
        }
        break;
        case '8':
            return t.value;
        break;
        default:
            user_error("Faktor erwartet");
        break;
    }
}

Ich bedanke mich schon mal für die Aufklärung.

Freundliche Grüsse aus der Schweiz.
 

TimLim

Gast
Bei mir führen beide Varianten zum selben Ergebnis. Keine Errormeldung, nur schließen des Fensters. Warum auch nicht, ob mit oder ohne Klammern sollte es keinen Unterschied machen, so lange du nur eine Zeile betrachtest.
 

DeletedUser82294

Gast
Was für nen Compiler nutzt du denn?
Die Klammern dürften eigentlich keinen Unterschied machen.
 

DeletedUser

Gast
Bei mir führen beide Varianten zum selben Ergebnis. Keine Errormeldung, nur schließen des Fensters. Warum auch nicht, ob mit oder ohne Klammern sollte es keinen Unterschied machen, so lange du nur eine Zeile betrachtest.
Es ist so, dass ich mir angewöhnt habe, immer geschweiften Klammern zu machen. Es ist auch zum Lesen, imo, besser. Auch wenn es jetzt wegen einer Zeile keinen Unterschied macht, dennoch sieht man gerade, was dazu gehört und was nicht.

Was für nen Compiler nutzt du denn?
Die Klammern dürften eigentlich keinen Unterschied machen.
MinGW.

edit: Es funktioniert, wenn man direkt am Anfang ohne einen Ausdruck einzugeben, q eingibt und Enter drückt. Sobald man aber einen Ausdruck ausrechnen lies, geht es nicht mehr.
 
Zuletzt bearbeitet von einem Moderator:

DeletedUser

Gast
Hmmm....

Mir ist einmal ein ähnliches und seltsames Problem bei ANSI-C (uralt...) und dynamische Variablen (in meinem Fall dynamische Arrays) begegnet. - Ich nehme einfach mal an dass Dein "token buffer" ein ähnliches Konstrukt ist (Falls nicht, ignoriere den Rest).

Die Erklärung ging in etwa so und ist ähnlich zu uninitialisierten Variablen an sich.
Erst beim allerersten Belegen der dyn-Variable wird der (interne) Pointer auf die zu verwendende Speicherstelle(n) gesetzt.
Die geschweifte Klammer testet "zwangsweise" ob alle Pointer dazwischen irgendwohin führen, das führte beim allerersten Durchlauf zu einem Fehler...

Ich könnte mir vorstellen dass trotz des gewaltigen Unterschiedes zu OOP dieses Problem ("Zwangstest" bei "Zwangskompartimentierung") weiter besteht...
 

DeletedUser

Gast
Hmmm....

Mir ist einmal ein ähnliches und seltsames Problem bei ANSI-C (uralt...) und dynamische Variablen (in meinem Fall dynamische Arrays) begegnet. - Ich nehme einfach mal an dass Dein "token buffer" ein ähnliches Konstrukt ist (Falls nicht, ignoriere den Rest).

Die Erklärung ging in etwa so und ist ähnlich zu uninitialisierten Variablen an sich.
Erst beim allerersten Belegen der dyn-Variable wird der (interne) Pointer auf die zu verwendende Speicherstelle(n) gesetzt.
Die geschweifte Klammer testet "zwangsweise" ob alle Pointer dazwischen irgendwohin führen, das führte beim allerersten Durchlauf zu einem Fehler...

Ich könnte mir vorstellen dass trotz des gewaltigen Unterschiedes zu OOP dieses Problem ("Zwangstest" bei "Zwangskompartimentierung") weiter besteht...
Ich habe keine Ahnung, wovon du redest. :mrgreen:

Im Eingangspost ist das komplette Script - mit benutzerdefinierten Typ Token. Schau's dir an und sag mir dann, ob ich deine Erklärung verstehen muss oder ob ichs ignorieren kann. :mrgreen:
 

DeletedUser

Gast
Das ist das Problem, ich habe nie OOP (ObjektOrientierte Programmierung = das "++") richtig gelernt (oder gar angewendet), deswegen verstehe ich Dein Code nicht vollständig (Jedoch könntest Du wahrscheinlich alles was ich je in C geschrieben habe verstehen... ;) ).

Auf Malerei übertragen:
Ich habe intensiv gelernt mit Steinen Linen in den Fels zu ritzen während Du mit Datenhandschuhen 3-D Modelle erstellst, dennoch kenne ich mich mit Techniken die Hände ruhig zu halten besser aus... ;)

Allerdings kann ich Maschinencode und weiss (inzwischen nur noch grob) was ein Compiler/Interpreter aus der Hochsprache macht und was das für seltsame Effekte erzeugen kann...

Die Quintessenz ist (falls ich Recht haben sollte):
Dein Compiler hält sich so genau an die ursprünglichen Vorgaben von C dass eine nachlässige (aber nicht falsche!) Programmierung in Sonderfällen unerwartete Resultate erzeugt...

Lösungsmöglichkeiten:
1. Andere Compiler verwenden (evtl reicht sogar das nächste Update...)
2. Bei derartigen Konstrukten auf die geschweiften Klammern verzichten - und im Laufe der Zeit verstehen wieso...
3. Aufwändig bei derartigen Konstrukten die Initialisierung vornehmen - und im Laufe der Zeit verstehen wieso...
 
Zuletzt bearbeitet von einem Moderator:

DeletedUser

Gast
Das ist das Problem, ich habe nie OOP (ObjektOrientierte Programmierung = das "++") richtig gelernt (oder gar angewendet), deswegen verstehe ich Dein Code nicht vollständig (Jedoch könntest Du wahrscheinlich alles was ich je in C geschrieben habe verstehen... ;) ).

Auf Malerei übertragen:
Ich habe intensiv gelernt mit Steinen Linen in den Fels zu ritzen während Du mit Datenhandschuhen 3-D Modelle erstellst, dennoch kenne ich mit Techniken die Hände ruhig zu halten besser aus... ;)

Allerdings kann ich Maschinencode und weiss (inzwischen nur noch grob) was ein Compiler/Interpreter aus der Hochsprache macht und was das für seltsame Effekte erzeugen kann...

Die Quintessenz ist (falls ich Recht haben sollte):
Dein Compiler hält sich so genau an die ursprünglichen Vorgaben von C dass eine nachlässige (aber nicht falsche!) Programmierung in Sonderfällen unerwartete Resultate erzeugt...

Lösungsmöglichkeiten:
1. Andere Compiler verwenden (evtl reicht sogar das nächste Update...)
2. Bei derartigen Konstrukten auf die geschweiften Klammern verzichten - und im Laufe der Zeit verstehen wieso...
3. Aufwändig bei derartigen Konstrukten die Initialisierung vornehmen - und im Laufe der Zeit verstehen wieso...
Deine Übertragung auf Malerei ist noch beschissener. :mrgreen:

Ich versuch es jetzt mal mit Cygwin. Falls sich etwas ergeben hat, melde ich mich nochmal.

edit: Cygwin macht es auch nicht besser als MinGW.

edit2: Ich denke, ich habe das Problem gefunden...

Ich habe einen kleinen Test durchgeführt. Ich lasse mir immer direkt nach ts.get() in der Schleife und nachdem Ausgeben den Buffer ausgeben. Das Script ist folgendermasse angepasst (nur Schleife):
Code:
while(cin) {
            Token t = ts.get();
            cout << "Buffer: " << t.kind << " | " << t.value << "\n";
            
            if(t.kind == 'q') { //'q' steht für Verlassen
                break;
            }
            
            if(t.kind == ';') { //';' steht für Jetzt ausgeben
                cout << "Ergebnis: " << val << "\n";
                
                Token tk = ts.get();
                cout << "End-Buffer: " << tk.kind << " | " << tk.value << "\n";
            } else {
                ts.putback(t);
            }
            
            val = expression();
        }

Und das ist die Ausgabe:
Willkommen zu unserem einfachen Taschenrechnerprogramm.
Bitte gebe einen Ausdruck mit reellen Zahlen ein.
Ausdruck: 1+1;
Buffer: 8 | 1
Buffer: ; | 0
Ergebnis: 2
q
End-Buffer: q | 0

Das bedeutet wohl, dass der Buffer nach dem Ausgeben nicht geleert wird, und so das Kommando "q" irgendwo im Buffer versenkt wird - ohne Verarbeitung. Ich teste jetzt mal noch ein bisschen weiter. >_<
 
Zuletzt bearbeitet von einem Moderator:

DeletedUser141176

Gast
hab mit c++ nichts am hut, aber wenn es um die zeilen handelt
if(t.kind == 'q') //'q' steht für Verlassen
break;

dann kann ich folgendes vermuten:
1) break bricht die ausführung des oberliegenden blocks --> wenn du hier geschweifte klammer einfügst werden die befehle dadrinne nicht mehr ausgeführt(zu dem das es ohne funzt)
2) wenns tatsächlich darum geht können viele wege daraus führen, dadrunter:
-> eine boolische variable die den event abfängt und abgefragt wird für den code hinterher mit nohc einem break aus den anderen schleife
-> blocke so umbauen das dein break im oberen block angesiedelt ist.
-> bin mir nicht sicher aber glaube das program als object auf zwangsmässig geschloßen werden kann, etwa .free() oder .close() oder .flush()
bestimmt haben die anderen noch mehr möglichkeiten aber die sollten dir schonmal helfen

Mfg Eugen
 

DeletedUser

Gast
hab mit c++ nichts am hut, aber wenn es um die zeilen handelt


dann kann ich folgendes vermuten:
1) break bricht die ausführung des oberliegenden blocks --> wenn du hier geschweifte klammer einfügst werden die befehle dadrinne nicht mehr ausgeführt(zu dem das es ohne funzt)
2) wenns tatsächlich darum geht können viele wege daraus führen, dadrunter:
-> eine boolische variable die den event abfängt und abgefragt wird für den code hinterher mit nohc einem break aus den anderen schleife
-> blocke so umbauen das dein break im oberen block angesiedelt ist.
-> bin mir nicht sicher aber glaube das program als object auf zwangsmässig geschloßen werden kann, etwa .free() oder .close() oder .flush()
bestimmt haben die anderen noch mehr möglichkeiten aber die sollten dir schonmal helfen

Mfg Eugen
1) Break bricht in einer Schleife die Schleifenausführung ab.
2) Eine Variable macht in einer sich wieder aufrufenden Schleife keinen Sinn.
2.3) Ein Programm zwangsläufig zu schliessen ist keine Alternative zu einem schonenden Abbruch in dem man einfach die Schleife abbricht. Dann schliesst sich die Konsole ja selbst. ;)
 

DeletedUser

Gast
Geht es um eine bestimmte If-Anweisung und was passiert genau mit bzw ohne die Klammern :)
Poste hier doch mal das Programm und markiere die Stellen an denen du das Problem vermutest, falls du es nicht bereits schon gelöst hast ;)
 

DeletedUser

Gast
Geht es um eine bestimmte If-Anweisung und was passiert genau mit bzw ohne die Klammern :)
Poste hier doch mal das Programm und markiere die Stellen an denen du das Problem vermutest, falls du es nicht bereits schon gelöst hast ;)
Das Problem und der Source Code stehen im Eingangspost. Meine Vermutung ist, dass der Buffer nach dem ausgeben nicht leer ist. Siehe vorletzten Post von mir.
 

TimLim

Gast
edit: Es funktioniert, wenn man direkt am Anfang ohne einen Ausdruck einzugeben, q eingibt und Enter drückt. Sobald man aber einen Ausdruck ausrechnen lies, geht es nicht mehr.

Jo, stimmt. Aber der Fehler kommt sowohl mit als auch ohne {}. Die If wird auch nicht ausgeführt. Also wird bei ts.get() schon die Exception geworfen. Alles danach wird nicht ausgeführt. Der Error der mir geworfen wird ist "Faktor erwartet" und der wird in der primary() Funktion geworfen. Ich steig auf die schnelle durch die Funktionen nicht ganz durch aber vielleicht helfen dir diese Infos, falls du sie nicht schon selber hast.



Damit könnte deine Vermutung auch wieder zutreffen, dass der Buffer nicht leer ist und damit q keine Rechnung ist, also einen Error wirft.
 
Oben