Forum: PC-Programmierung Taschenrechner in C


von Leyla S. (keeke)


Lesenswert?

Hallo,

ich habe versucht einen TR zu programmieren, aber stoße auf ein Problem. 
Und zwar weiß ich nicht wie man eine do while Schleife beendet.
Eine einfache Leerzeile vor %c würde zwar mein Problem lösen. Aber ich 
will die do while Schleife einfügen. Ich hoffe jemand kann mir helfen.
1
#include <stdio.h>
2
int main(void)
3
4
{
5
    int zahl1, zahl2;
6
    char rechenart;
7
8
    printf("1.Zahl:\n");
9
    scanf("%d", &zahl1);
10
    printf("2.Zahl:\n");
11
    scanf("%d", &zahl2);
12
    do{printf("Wähle die Rechenart (+,-,)?\n");
13
    scanf("%c", &rechenart);
14
    }while(getchar() != '\n');
15
16
17
    switch(rechenart)
18
    {
19
        case '+':
20
    printf("%d %c %d = %d", zahl1, rechenart, zahl2, (zahl1 + zahl2));
21
    break;
22
        case '-':
23
    printf("%d %c %d = %d", zahl1, rechenart, zahl2, (zahl1 - zahl2));
24
    break;
25
    }
26
    return 0;
27
}

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Leyla S. schrieb:
> Und zwar weiß ich nicht wie man eine do while Schleife beendet.

break.

von Karl H. (kbuchegg)


Lesenswert?

Leyla S. schrieb:

> Aber ich
> will die do while Schleife einfügen.

Und wozu soll die Schleife gut sein?


Die macht hier
1
     do{printf("Wähle die Rechenart (+,-,)?\n");
2
     scanf("%c", &rechenart);
3
     }while(getchar() != '\n');
in der Form überhaupt keinen Sinn.

von keeke (Gast)


Lesenswert?

Weil ich den char sonst nicht einlesen kann.

von Leyla S. (keeke)


Lesenswert?

Mit break hat es doch nicht geklappt.

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

Man muss das break auch richtig anwenden.

Was stört dich am Leerzeichen vor dem %c ?

Dann liest du das erste Zeichen ein, das kein Whitespace ist.

Oder möchtest du sicherstellen, dass nur '+' oder '-' eingelesen wurde?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

scanf() direkt (ohne vorheriges Einlesen der Zeile in einen Puffer und
dann sscanf()) war schon immer eklig zu bedienen.

Dein Beispiel funktioniert durchaus auch jetzt schon, nur die Eingabe
ist halt, ähem, gewöhnungsbedürftig. :-)
1
$ ./tr
2
1.Zahl:
3
23 42-
4
2.Zahl:
5
Wähle die Rechenart (+,-,)?
6
23 - 42 = -19$

von Yalu X. (yalu) (Moderator)


Lesenswert?

Leyla S. schrieb:
>     scanf("%d", &zahl2);
>     do{printf("Wähle die Rechenart (+,-,)?\n");
>     scanf("%c", &rechenart);
>     }while(getchar() != '\n');

keeke schrieb:
> Weil ich den char sonst nicht einlesen kann.

Das ist trotzdem ziemlich krumm und funktioniert deswegen auch nicht so,
wie du es gerne hättest.

Betrachte die ganze Sache mal der Reihe nach:
1
    scanf("%d", &zahl2);

hinterlässt das Linefeed-Zeichen im Eingabepuffer, weil es nicht mehr
zur eingelesenen Zahl gehört. Da der Linefeed beim darauffolgenden scanf
(mit dem "%c"-Format) stören würde, muss er irgendwie aus dem Puffer
entfernt werden. Das ginge bspw. mit einem simplen
1
    getchar();

Falls der Benutzer nach nach der Zahl versehentlich noch ein paar andere
Zeichen eingegeben hat, kann man die ebenfalls überlesen, indem man
getchar() mehrmals bis zum Erreichen des Zeilenendes aufruft:
1
    while(getchar() != '\n');

Jetzt ist der Eingabepuffer garantiert geleert und die darauffolgende
Eingabe mit
1
    scanf("%c", &rechenart);

startet wie gewünscht mit dem ersten Zeichen einer neuen Eingabezeile.

Zusammengefasst sieht der entsprechende Codeabschnitt also so aus:
1
    printf("2. Zahl:\n");
2
    scanf("%d", &zahl2);
3
    printf("Wähle die Rechenart (+,-)?\n");
4
    while(getchar() != '\n'); // löscht den Eingabepuffer bis zum Zeilenende
5
    scanf("%c", &rechenart);

Alternativ kannst du die geaamte Rechenoperation auch in einer einzelnen
Zeile abfragen:
1
    printf("Rechenoperation in der Formn  x op y  eingeben: ");
2
    scanf("%d %c%d", &zahl1, &rechenart, &zahl2);

Vor und nach den beiden Zahlen darf dabei beliebiger Whitespace stehen,
so dass bspw. folgende Eingabe möglich sind:
1
3+4
2
3 - 4
3
  234      +      2455

usw.

Erkärung des Formatstrings:

Das erste %d überliest ggf. Whitespace am Anfang der Zeile und liest
dann eine Zahl ein.

Das darauffolgende Leerezeichen überliest ggf. Whitespace vor dem
Operatorzeichen.

Das %c liest das Operatorzeichen.

Das zweite %d überliest ggf. weiteren Whitespace und liest dann die
zweite Zahl ein.

Das Ganze funktioniert auch problemlos in einer Schleife, wenn das
Programm mehrere Rechenoperationen nacheinander ausführen soll. Der
Linefeed, der nach dem zweiten %d im Eingabepuffer übrig bleibt, wird
dabei vom ersten %d des scanf-Aufrufs im nächsten Schleifendurchlauf
überlesen.

Es lohnt sich, die Anleitung von scanf einmal genau durchzulesen, um
sich darüber klar zu werden, was die einzelnen Formatelemente im Detail
tun. Das betrifft insbesondere die jeweilige Behandlung von Whitespace,
denn die ist nicht immer intuitiv erfassbar.

von Leyla S. (keeke)


Lesenswert?

Vielen Dank. Jetzt habe ich es verstanden :)

von Leyla S. (keeke)


Lesenswert?

Warum braucht man die do while Schleife hier nicht, aber in diesem Code 
beim Einlesen von drei Zeichen? Wenn ich do entferne klappt das Programm 
immer noch.

„
1
int main(void)
2
{
3
    char x, y, z;
4
5
    printf("Bitten geben Sie das 1.Zeichen ein: \n");
6
    do { scanf("%c",&x); }
7
    while (getchar() != '\n');
8
    printf("Bitten geben Sie das 2.Zeichen ein: \n");
9
    do { scanf("%c",&y); }
10
    while (getchar() != '\n');
11
    printf("Bitten geben Sie das 3.Zeichen ein: \n");
12
    do { scanf("%c",&z); }
13
    while (getchar() != '\n');
14
    return 0;
15
}

von Dirk B. (dirkb2)


Lesenswert?

Weil das dann eine while-Schleife ist. Und eigentlich auch nur sein 
braucht.

Du willst ja alle überflüssigen Zeichen überlesen, die noch in der Zeile 
sind.

Bei der do-while-Schleife wird aber immer nochmal das scanf ausgeführt.

Die meisten C-Buchautoren kennen die Funktion vom Lerrzeichen im 
scanf-Formatstring nicht. Darum werden wilde Konstrukte mit Schleifen 
oder gets gebaut.
Bei
1
  printf("Bitten geben Sie 3 Zeichen ein: \n");
2
  scanf(" %c %c %c", &x, y&, &z);

kann man die Zeichen hintereinander (abc), mit Whitespace getrennt (  a 
b   c)
oder auch in extra Zeilen
(a
b
c) und auch gemischt einlesen. (a


bc)

Die Klammern dienen nur zur Kennzeichnung der Eingabe

von Leyla S. (keeke)


Lesenswert?

Okey die Art und Weise wie gelesen wird, danke dafür. Aber bei dem Code 
von Yalu, was ich eben gepostet habe. Wieso braucht man da eine do while 
Schleife.
Würde ein [c]while(getchar() != '\n');[\c] nicht ausreichen?

Dankeschön :)

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

Leyla S. schrieb:
> Aber bei dem Code
> von Yalu, was ich eben gepostet habe. Wieso braucht man da eine do while
> Schleife.

Die do-while-Schleifen stammen alle von dir.

von Karl H. (kbuchegg)


Lesenswert?

Leyla S. schrieb:
> Warum braucht man die do while Schleife hier nicht, aber in diesem Code
> beim Einlesen von drei Zeichen? Wenn ich do entferne klappt das Programm
> immer noch.

****** ACHTUNG: Update geenüber dem vorhergehenden (und mitlerweile 
gelöschten) Posting *********

Weil das %d den noch vorhandenen \n aus dem Eingabestrom entfernt, was 
ein %c nicht tut.

OK, Machen wir ein Gedankenexperiment.
Du bist Computer. Du hältst dich exakt an die Regeln.
Bei einem %d liest du, wobei deine Lesestrategie so aussieht:
  wenn im Eingabestrom ein Leeerzeichen oder ein \n kommt, dann 
überliest
  du das einfach. Solange bis du auf etwas stösst was kein derartiges
  Whitespace Zeichen ist. Alles danach folgende versuchst du Zeichen für
  Zeichen zu einer Zahl zusammenzusetzen, so lange bis du auf etwas 
stösst
  was ganz sicher nicht mehr zur Zahl gehören kann. Das lässt du dann im
  Eingabestrom übrig, so dass sich die nächste Leseoperation mit diesem
  Zeichen beschäftigen kann.

Bei einem %c liest du genau 1 Zeichen vom Eingabestrom. Egal was dieses 
Zeichen tatsächlich ist. Genau eines und nicht mehr

Dein Eingabestrom sei die Zeichenfolge
1
  \n345 \n765\na
und die Operationen sind
1
   scanf( "%d", &i );
2
   scanf( "%d", &j );
3
   scanf( "%c", &c );

wie läuft da die Abarbeitung und welche Werte landen in welchen 
Variablen?

Fang so an
Der erste scanf( "%d", &i ) ist drann.
Aus dem Eingabestrom
1
  \n345 \n765\na
wird das erste Zeichen geholt. Das hier
1
  \n345 \n765\na
2
^
3
*
Das ist ein Leerzeichen. Getreu Vorschrift ignoriert der %d dieses 
Leerzeichen und holt sich das nächste zeichen. Das ist dann das hier
1
  \n345 \n765\na
2
 ^
3
 *
Wieder ein Leerzeichen. Laut Vorschrift wird auch das ignoriert und 
weiter gelesen. Das nächste Zeichen ist ein \n
1
  \n345 \n765\na
2
   ^
3
   *
wieder: wird ignoriert. Nächstes Zeichen
1
  \n345 \n765\na
2
    ^
3
    *
Aha. Das Zeichen '3'. Damit ist die Ignorierphase für Whitespace vorbei. 
Ab jetzt gilt: es wird solange bearbeitet, solange das Zeichen zu einer 
int-Zahl gehören kann. '3' kann Teil eines int sein, daher gehört es zur 
Zahl. Nächstes zeichen
1
  \n345 \n765\na
2
     ^
3
     *
'4'. Passt. Kann zur Zahl gehören. Die Zahl könnte als 34 sein. Nächstes 
Zeichen
1
  \n345 \n765\na
2
      ^
3
      *
'5'. Kann immer noch zur Zahl gehören. Die Zahl wäre also 345. Nächstes 
Zeichen
1
  \n345 \n765\na
2
       ^
3
       *
Ein Leerzeichen. Nope. Das kann ganz sicher nicht mehr zur Zahl gehören. 
D.h. die Zahl war 345. Damit ist der erste scanf, bzw. sein %d 
abgearbeitet und der nächste kommt zum Zug
1
...
2
   scanf( "%d", &j );
3
...
wieder ein %d.
Wie immer bei einem %d beginnt auch der damit, Whitespace (also 
Leerzeichen, tabulatoren und Zeilenvorschub zu ignorieren. Das ist seine 
Strategie. Mal sehen, wo waren wir?
1
[code]
2
  \n345 \n765\na
3
       ^
4
       *
Ein Leerzeichen. Passt. wird ignoriert. Näächstes Zeichen
1
  \n345 \n765\na
2
         ^
3
         *
Ein \n. Wird ignoriert. Nächstes
1
  \n345 \n765\na
2
          ^
3
          *
EIn '7'. D.h. es taucht das erste mal ein nicht Whitespace Zeichen auf. 
Damit ist die Ignorierphase beendet. Die Zahl hat begonnen und könnte 7 
sein. Nächstes Zeichen
1
  \n345 \n765\na
2
          ^
3
          *
Ein '6'. Passt zu einem int. Zahl könnte also 76 sein. Nächstes Zeichen
1
  \n345 \n765\na
2
            ^
3
            *
'5'. passt immer noch. Zahl könnte 765 sein. Nächstes
1
  \n345 \n765\na
2
              ^
3
              *
Ein \n. Nope der passt nicht mehr. Die Zahl war also 765 und mit dem \n 
soll sich die nächste Eingabeoperation rumschlagen.
Damit ist dieser scanf beendet und der nächste kommt drann.
1
...
2
   scanf( "%c", &c );
Achtung: Das ist jetzt ein %c!
Der funktioniert anders. Der liefert einfach nur das nächste zeichen. 
Egal was das ist.
nun, welches ist das?
1
  \n345 \n765\na
2
              ^
3
              *
Der \n. Ganz klar. D.h. der %c gibt sich mit dem \n zufrieden. Das ist 
das nächste Zeichen und das liefert er.


Nur: Das war nicht ganz das, was du eigentlich wolltest. Du warst auf 
das 'a' aus, das da in der Eingabe noch wartet. Daher: Du musst das \n, 
welches noch im Eingabestrom noch lauert gezielt überlesen. Bzw. wenn 
die Eingabe
1
  \n345 \n765    \na
gelautet hätte, dann eben auch noch all die Leerzeichen bis zu (und 
inklusive) dem \n. Denn erst dann ist das nächste zeichen ein 'a' und 
der
1
...
2
   scanf( "%c", &c );
würde das dann auch nach c lesen.

: Bearbeitet durch User
von Leyla S. (keeke)


Lesenswert?

Danke für deine ausführliche Antwort.

von Leyla S. (keeke)


Lesenswert?

Dirk B. schrieb:
> Leyla S. schrieb:
>> Aber bei dem Code
>> von Yalu, was ich eben gepostet habe. Wieso braucht man da eine do while
>> Schleife.
>
> Die do-while-Schleifen stammen alle von dir.

Ah Sorry, das war gar nicht von Yalu.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.