# C Programmierung - Taschenrechner - Programm startet sich nach Nutzereingabe nicht neu?



## NeKo LeXuS (25. März 2015)

Hallo Leute,

für die meisten von euch ist diese Frage wahrscheinlich ein Leichtes, ich finde allerdings nicht den Fehler: ich habe einen Taschenrechner auf C-Basis programmiert. 

Nach dem die Operation ausgewählt wurde, zwei Zahlen eingegeben wurden und das Ergebnis ausgespuckt wurde, hat der Nutzer die Möglichkeit, das Programm mit "j" neu zu starten. Dabei ruft er wieder die Prozedur start(); auf, bricht allerdings (nach der ersten Eingabe innerhalb der start();-Prozedur) ab. Wieso? 

Anbei mein Quellcode:


```
#include <stdio.h> 
#include <stdlib.h>
#include <time.h>
#include <string.h>
/* TASCHENRECHNER V. 0.1
Leider bricht das Programm ab, sobald sich der User entscheidet, durch die Prozedur retry(); das Programm neu zu starten.
Das Programm beginnt dann wieder bei start(); , bricht danach allerdings bei der Eingabe direkt ab. Woran kann das liegen?*/

int a = 0;
int b = 0;
char operation;

void start() {
    printf("TASCHENRECHNER VON M. A.\n\n");

    printf("Auszufuehrende Operation (+, -, /, *): ");
    scanf_s("%c", &operation);

    printf("Bitte die erste Zahl eingeben: ");
    scanf_s("%d", &a);

    printf("Bitte die zweite Zahl eingeben: ");
    scanf_s("%d", &b);
}

void rechnen() { // hier wäre eine switch-Verzweigung eleganter, ich werde das später implementieren.
    if (operation == '+') {
        int summe = a + b;
        printf("Ergebnis: %d\n\n", summe);
    }
    else if (operation == '-') {
        int subtraktion = a - b;
        printf("Ergebnis: %d\n\n", subtraktion);
    }
    else if (operation == '*') {
        int multiplikation = a * b;
        printf("Ergebnis: %d\n\n", multiplikation);
    }
    else if (operation == '/') {
        int division = a / b;
        printf("Ergebnis: %d\n\n", division);
    }
    getchar();
}

void retry() {
    printf("Nomma? j/n\n");
    char i;
    scanf_s("%c", &i);

    if (i == 'j') {
        start();
    }
    else if (i == 'n') {
        printf("Tschau.\n");
    }
}

void main() {
    start();
    rechnen();
    retry();
}
```

Danke schon mal für die Hilfe!


----------



## bingo88 (25. März 2015)

Du müsstest das eigentlich in einer Schleife machen:

```
int exit = 0;

while (exit == 0)
{
    start();
    rechne();
    exit = retry();
}

mit

int retry() {
    printf("Nomma? j/n\n");
    char i;
    scanf_s("%c", &i);

    if (i == 'j') {
       return 0;
    }
    else if (i == 'n') {
        printf("Tschau.\n");
        return 1;
    }
}
```

Das Problem ist nämlich, dass du im retry() zwar start() aufrufst, aber dann nicht rechnest. Außerdem würde das auch so nur 1x wiederholbar sein.


----------



## DarkMo (25. März 2015)

jup, bingo hats schön geschrieben. was du verständnismäßig vllt verwechselst: wenn du bei retry() wieder start() aufrufst, kommst du am ende nicht wieder in der main() direkt nach start() (vor dem rechnen()) raus ^^ das erinnert mich an so jump-marks (oder wie sich das schimpft) bei assembler. da hat man im code ne jump-position gesetzt und konnte dann mit jmp oder goto? ich glaub ich werf grad viele sprachen durcheinander xD und jedenfalls dann der name dieser jump-location wieder dort an diese code stelle hüpfen. wenn der c-code mit einer schleife compiliert wird, wird das sicher in assembler dann auch damit umgesetzt *denk*

is zwar schon völlig ab vom eigentlichen thema, aber als pseudo code kannstes dir so vorstellen:
#start
rechne()
jmp #start

ist wie gesagt sicher weit weg von der wirklichen umsetzung, aber vom prinzip her stimmt es glaub ich einigermaßen ^^ jedenfalls hast DU in deinem code sowas nicht. wiederholbares zeugs muss in ne schleife und die schleife braucht ne gescheite abbruchbedingung


----------



## NeKo LeXuS (27. März 2015)

Super, vielen Dank euch beiden!

*DarkMo*: Nee, Pseudo-Code ist nie ab vom Thema. Das ist perfekt zur Verdeutlichung. Und meine Fehlerquelle kann ich nun nachvollziehen. 

Ja, ich glaube der GoTo-Befehl funktioniert so... habe mich damit jedoch nie befasst, da man jenen Befehl meiden sollte.

*bingo88*: Einfach und clever. Leider wäre ich da SO nicht drauf gekommen. While-Schleife ja, aber ich hätte wahrscheinlich irgendwas mit bool und return true bzw. return false versucht. Bzw. das hatte ich vorgestern sogar versucht und bin gescheitert.
Wie auch immer, das Problem ist nun behoben - vielen Dank!!

/edit:

Ich sehe gerade, dass er zwar das Programm bei start(); nach der Eingabe eines 'j' startet, allerdings kann man die Rechenoperation nicht mehr auswählen, er möchte jetzt direkt die beiden Zahlen wissen und spuckt aber dann auch kein Ergebnis aus. Wieso ignoriert er diese Zeile:

    printf("Auszufuehrende Operation (+, -, /, *): ");
    scanf_s("%c", &operation);

?


----------



## bingo88 (29. März 2015)

NeKo LeXuS schrieb:


> Ja, ich glaube der GoTo-Befehl funktioniert so... habe mich damit jedoch nie befasst, da man jenen Befehl meiden sollte.


Goto hat so einen schlechten Ruf, weil es oft missbräuchlich verwendet wurde. Aber gerade bei C, das ja kein Exception-Handling kann, ist das ein sehr wertvolles Werkzeug. In deinem Beispiel hätte das in der Tat aber nichts verloren 



NeKo LeXuS schrieb:


> While-Schleife ja, aber ich hätte wahrscheinlich irgendwas mit bool und  return true bzw. return false versucht. Bzw. das hatte ich vorgestern  sogar versucht und bin gescheitert.


Du scheinst mir mit Visual Studio zu arbeiten. Der MSVC ist kein reiner C-Compiler, wenn du *.cpp Sourcedateien anlegst, kompiliert der standardmäßig mit C++. In C gibt es dieses "bool" aus VS nicht, das ist eigentlich ein C++ Datentyp. In klassischem C gibt es einen Boolean Datentyp erst ab dem C99 Standard (_Bool), ehrlich gesagt habe ich aber noch nicht so viel produktiven C99 Code gesehen. Meistens findet man daher oft int mit 0 für False und ungleich 0 für True. Manchmal wird das auch über #defines oder enums abgebildet. Abgesehen davon gibt es auch einige Microsoft-spezifische Erweiterungen und Funktionen, die portablen Code verhindern (scanf_s wäre da ein Beispiel).



NeKo LeXuS schrieb:


> Ich sehe gerade, dass er zwar das Programm bei start(); nach der Eingabe  eines 'j' startet, allerdings kann man die Rechenoperation nicht mehr  auswählen, er möchte jetzt direkt die beiden Zahlen wissen und spuckt  aber dann auch kein Ergebnis aus. Wieso ignoriert er diese Zeile:
> 
> printf("Auszufuehrende Operation (+, -, /, *): ");
> scanf_s("%c", &operation);


Das sehe ich mir morgen mal an.

Edit: Das Problem ist vermutlich das scanf_s(). Das scanf (die libc Variante) liest normalerweise bis zum Zeilenumbruch (\n), belässt diesen aber im Input Stream. Das heißt, beim nächsten Aufruf sieht das scanf den alten Zeilenumbruch und hält das für eine weitere Eingabe. Wenn du ein getchar() nach dem scanf() machst, müsste der Zeilenumbruch aus dem Input Stream gelesen werden. scanf_s() ist allerdings eine Microsoft-Erweiterung, die außerdem einen Längenparameter erwartet (der bei dir übrigens fehlt, eigentlich sollte das mindestens eine Compilerwarnung geben). Ob die sich genauso verhält, kann ich jetzt nicht mit Sicherheit sagen, würde es aber mal vermuten.

Du solltest dir außerdem die Rückgabewerte von scanf_s ansehen. Bei fehlerhaften Eingaben passieren sonst auch unter Umständen komische Dinge.


----------



## boss3D (31. März 2015)

Zu bingo88s Edit ...
Ich habe das jetzt selber schnell ausprobiert: Offenbar bleibt wirklich (mind.) ein '\n' hängen, das vom nachfolgenden scanf_s als Eingabe angenommen wird. Die beste Lösung dürfte sein, ein Leerzeichen vor dem format specifier einzufügen um quasi "drüberzuspringen".

Bsp: scanf_s(*" %c"*, &bsp);

Im Übrigen brauchst Du doch keine >60 Zeilen für das bisschen?! 


Spoiler





```
#include <stdio.h>

void calc(char calcType, int one, int two) {
    int res = 0;

    switch (calcType) {
        case '+': res = one + two; break;
        case '-': res = one - two; break;
        case '/': res = one / two; break;
        case '*': res = one * two; break;
        default: printf("No matching case. Check calculation type input value.\n"); break;
    }

    printf("Result: %d\n\n", res);
}

int main(void) {
    char op = NULL, end = NULL;
    int fval = 0, sval = 0;

    for (;;) {
        printf("Please choose a calculation type (+, -, /, *): ");
        scanf_s(" %c", &op, 1);
        printf("First value: ");
        scanf_s(" %d", &fval, 1);
        printf("Second value: ");
        scanf_s(" %d", &sval, 1);
        calc(op, fval, sval);
        printf("Try again? (y/n) ");
        scanf_s(" %c", &end, 1);
        if (end == 'n') {
            break;
        }
    }

    return 0;
}
```



^^ Ich habe mich auch gewundert, was Du da alles für Header files eingebunden hast (v. a. time.h ???). Wofür glaubst Du die denn zu brauchen, oder wird das Programm noch erweitert?


----------

