Programm beendet sich nicht (endlose while-Schleife)



  • Guten Tag,

    ich lerne gerade Grundlagen von C und habe ein kleines Programm geschrieben, das Buchstaben aus einer Zeichenkette vertauschen soll.

    Leider scheint es sich nach der Eingabe des zweiten Buchstabens immer aufzuhängen und ich bin ich nicht sicher warum.

    Bin schon müde und wahrscheinlich ist der Fehler ziemlich offensichtlich. Für Hilfe wäre ich trotzdem sehr dankbar.

    Der Code lautet:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define MAX 40
    
    void ersetze_zeichen(char* s, char zei_alt, char zei_neu);
    
    int main(void) {
        char zei_1, zei_2;
        char zkette[MAX];
        strcpy(zkette,"Ersetze e durch t");
        printf("Geben Sie das Zeichen ein das getauscht werden soll.\n");
        scanf("%c",&zei_1);
        printf("Geben Sie das Zeichen ein das eingesetzt werden soll.\n");
        scanf("%c",&zei_2);
        ersetze_zeichen(zkette, zei_1, zei_2);
        printf("%s",zkette);
        return EXIT_SUCCESS;
    }
    
    void ersetze_zeichen(char* s, char zei_alt, char zei_neu) {
            while (*s) {
                if (*s == zei_alt) {
                    *s = zei_neu;
                    s++;
                }        
            }
    }
    
    

    Danke im Voraus.

    Gruß,
    Kintaro



  • if (*s == zei_alt) {
      *s = zei_neu;
     }
    s++; // immer inkrementieren, nicht nur bei erfülllter Bedingung
    


  • @Kintaro777 sagte in Programm beendet sich nicht (endlose while-Schleife):

        scanf("%c",&zei_2);
    

    Nach dem letzten scanf() steht noch ein Newline ('\n') in stdin. Mit einem Leerzeichen vor %c kannst Du Whitespace (Leerzeichen, Tabs, Newlines, ...) verwerfen:

         scanf(" %c", &zei_2);
         //     ^
    

    Hier:

    void ersetze_zeichen(char* s, char zei_alt, char zei_neu) {
            while (*s) {
                if (*s == zei_alt) {
                    *s = zei_neu;
                    s++;
                }        
            }
    }
    

    wird s nur inkrementiert wenn die Bedingung vom if zutrifft. s sollte aber bei jedem durchlauf der while-Schleife inkrementiert werden:

    void ersetze_zeichen(char *s, char zei_alt, char zei_neu)
    {
        while (*s) {
            if (*s == zei_alt) {
                *s = zei_neu;
            }        
            ++s;
        }
    }
    

    Eine for-Schleife ist wahrscheinlich eloganter:

    void ersetze_zeichen(char *s, char zei_alt, char zei_neu)
    {
        for(; *s; ++s)
            if (*s == zei_alt)
                *s = zei_neu;
    }
    


  • @Kintaro777 sagte in Programm beendet sich nicht (endlose while-Schleife):

    char zkette[MAX];
    strcpy(zkette,"Ersetze e durch t");
    

    Oder gleich

    char zkette[] = "Ersetze e durch t";
    


  • Vielen Dank.

    Mein größter Fehler lag natürlich an der falsch verknüpften Inkrementierung.

    Meine ursprüngliche Idee war auch die Verwendung einer for-schleife.

    Die While-Schleife, die Verwendung von [MAX] und von strcpy stammen aus der Musterlösung, die leider wirklich häufig auch bei anderen Aufgaben fehlerhaft ist.

    Zum Glück gibt es Foren wie diese wo echte Fachleute weiterhelfen 😛



  • @Kintaro777 sagte in Programm beendet sich nicht (endlose while-Schleife):

    Die While-Schleife, die Verwendung von [MAX] und von strcpy stammen aus der Musterlösung, die leider wirklich häufig auch bei anderen Aufgaben fehlerhaft ist.

    Hast du die gesamte ersetze_zeichen 1:1 aus der Musterlösung genommen? Oder hast du da irgendwas leicht abgeändert? Falls es nicht 100% exakt übernommen wurde poste mal den entsprechenden Abschnitt der Musterlösung. Vielleicht hast du ja etwas geändert ohne zu merken dass du damit das Verhalten änderst.

    z.B. würde folgendes funktionieren:

    void ersetze_zeichen(char* s, char zei_alt, char zei_neu) {
            while (*s) {
                if (*s == zei_alt)
                    *s = zei_neu;
                s++;
            }
    }
    

    Bis auf die Einrückung (die für das Verhalten irrelevant ist) ist die einzige Änderung gegenüber dem was du gepostet hast dass ich zwei Klammern weggenommen habe. Was halt das Verhalten des Programms verändert.



  • Ich habe es jetzt mal ein bisschen abgeändert

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #define MAX 40
    
    void ersetze_zeichen(char *s, char zei_alt, char zei_neu);
    
    int main(void) {
        char zkette[] = "Ersetze e durch x";
        char zei_1, zei_2;
        printf("\n%s\n",zkette);
        printf("Geben Sie das Zeichen ein, das ersetz werden soll.\n");
        scanf("%c",&zei_1);
        printf("Geben Sie das Zeichen ein, das eingesetzt werden soll.\n");
        scanf("%c",&zei_2);
        ersetze_zeichen(zkette,zei_1,zei_2);
        printf("\n%s",zkette);
        return EXIT_SUCCESS;
    }
    
    void ersetze_zeichen(char *s, char zei_alt, char zei_neu) {
        for (; *s; s++) {
            if (*s == zei_alt)
                *s = zei_neu;
            }
    }
    
    

    Die Konsole gibt nun aber immer folgendes aus:

    Ersetze e durch x
    Geben Sie das Zeichen ein, das ersetz werden soll.
    e
    Geben Sie das Zeichen ein, das eingesetzt werden soll.

    Ers
    tz

    Mit strcpy hat es irgendwie funktioniert.
    Wahrscheinlich versteht der compiler das Leerzeichen immer als Abbruchbedingung. Gibt es da eine Lösung das zu vermeiden?

    Gruß
    Kintaro



  • @hustbaer Moment ich schreibe hier sofort mal die Musterlösung rein:

    #include <stdio.h>
    #include <string.h>
    #define MAX 40
    
    void ersetze_zeichen(char *s, char zei_alt, char zei_neu);
    
    int main(void) {
        unsigned char zkette[MAX];
    
        strcpy(zkette,"Ersetze e durch x");
        printf("\n%s",zkette);
        
        ersetze_zeichen(zkette,'e','x');
        printf("\n%s",zkette);
    return 0;
    }
    
    void ersetze_zeichen(char *s, char zei_alt, char zei_neu) {
        while (*s) {
            if (*s == zei_alt)
                *s = zei_neu;
            s++;
        }
    }
    

    Witzigerweise bekomme ich mit der Musterlösung die Fehlermeldung:

    ./ersetzezeichenbuch.c: In function ‘main’:
    ./ersetzezeichenbuch.c:10:12: error: pointer targets in passing argument 1 of ‘strcpy’ differ in signedness [-Werror=pointer-sign]
    strcpy(zkette,"Ersetze e durch x");
    ^~~~~~
    In file included from ./ersetzezeichenbuch.c:2:0:
    /usr/include/string.h:121:14: note: expected ‘char * restrict’ but argument is of type ‘unsigned char *’
    extern char *strcpy (char *__restrict __dest, const char *__restrict __src)
    ^~~~~~
    ./ersetzezeichenbuch.c:13:21: error: pointer targets in passing argument 1 of ‘ersetze_zeichen’ differ in signedness [-Werror=pointer-sign]
    ersetze_zeichen(zkette,'e','x');
    ^~~~~~
    ./ersetzezeichenbuch.c:5:6: note: expected ‘char *’ but argument is of type ‘unsigned char *’
    void ersetze_zeichen(char *s, char zei_alt, char zei_neu);
    ^~~~~~~~~~~~~~~
    cc1: all warnings being treated as errors

    Und nur wenn ich "unsigned" als Typdefinition entferne funktioniert das Programm.



  • Zu deinem eigenen Code:
    So wie @Swordfish schon geschrieben hat, füge noch ein Leerzeichen beim scanf-Aufruf ein:

    printf("Geben Sie das Zeichen ein, das ersetz werden soll.\n");
    scanf(" %c",&zei_1);
    printf("Geben Sie das Zeichen ein, das eingesetzt werden soll.\n");
    scanf(" %c",&zei_2);
    

    so daß Whitespaces überlesen werden. Bei dir wurde das Newline-Zeichen ('\n') eingelesen und somit entstehen bei der Ausgabe mehrere Zeilen.

    Und bei der Musterlösung sollte nur

    char zkette[MAX];
    

    verwendet werden.
    Da hat derjenige wohl die Warnungen des Compilers ignoriert (und erst gar nicht mit der Option "treat warnings as errors" kompiliert).



  • @Kintaro777 und ist dir auch klar, warum die Schleife in der Lösung funktioniert, bei dir aber nicht?



  • @Th69 @Swordfish Oh ja danke, das hatte ich überlesen und so funktioniert es auch mit meiner (korrigierten) Lösung.



  • @manni66 Ja das ist mir klar.. Wie schon vermutet war der Fehler ja ziemlich offensichtlich.


Anmelden zum Antworten