Unerwünschte Symbole - Mein erstes C-Programm



  • Hallo Community,

    ich habe vor kurzem begonnen, mich mit der Programmiersprach C zu beschäftigen.
    Jetzt habe ich, um das bisher Erlernte ausführlich zu testen, ein eigenes kleines Programm auf Hangman-basis geschrieben.

    Das Programm läuft soweit ganz gut, doch es gibt einige Stellen die mir Kopfzerbrechen bereiten und ich hoffe, dass ihr mir weiterhelfen könnt.

    Zum Programmieren benutze ich Pelles C für Windows (64 bit).
    Ich habe das Programm sowohl als 64 Bit als auch 32 bit Konsolenanwendung erstellt.

    Ich bin mir sicher, dass das Prinizip von Hangman jedem klar ist.

    Erste Frage:

    char geratenebuchstaben[30]
    

    ... soll alle bissher bereits getippten Buchstaben speichern.
    Hier unterscheidet sich das verhalten der 64 bit Version von der 32 bit Version.
    Wärend die 32 Bit Version damit keine Probleme hat, scheint die 64 Bit Version diesen char schon mit zwei beliebigen Symbolen zu füllen, die auch beim Start des Programms ausgegeben werden.

    char geratenebuchstaben[28];
    

    Funktionier unter 64 Bit Version

    char geratenebuchstaben[29];
    

    Ab hier scheint der char nicht leer initialiert zu werden.

    char geratenebuchstaben[29] = {0};
    

    Funktionier unter 64 Bit Version. Aber warum nur so?

    Zweite Frage

    for(i=0; i < wortlaenge; i++){
    		if(isalpha(zuratendeswort[i]) && !strchr(umlaute, zuratendeswort[i])){
    			daswort[i] = toupper(zuratendeswort[i]);
    	  	}
    		else if(zuratendeswort[i] == '\204'){
    			daswort[i] = '\216';
    		}
    		else if(zuratendeswort[i] == '\224'){
    			daswort[i] = '\231';
    		}
    		else if(zuratendeswort[i] == '\201'){
    			daswort[i] = '\232';
    		}
    		else{
    			daswort[i] = zuratendeswort[i];
    		}
    	}
    

    Hier wird das zu erratene Wort Buchstabe für Buchstabe in einen neuen String kopiert und in Großbuchstaben umgewandelt.
    In der 32 Bit Version zeigen sich durch

    printf("%s\n", daswort);
    

    ungewollte Symbole am Ende des Strings.
    Dies scheint von der länge des Strings abhängig zu sein.
    Ich habe schon probiert das problem mit dem direkten anhängen von \0 zu beseitigen. Was aber nicht funktionierte.
    Woran könnt das liegen?

    Dritte und letzt Frage

    Mit der folgenden Funktion lasse ich die Buchstaben durch "-" ersetzen.

    for(i = 0; i <= j; i++){	// <-- zählt er so nicht einen mehr hoch als nötig... warum macht " i < j " bei der ausgabe probleme
    		if(wort1[i] == ' '){				// leerzeichen suchen
    			wort2[i] = ' ';
    		}
    		else if(strchr(pm, wort1[i])){		// satzzeichen suchen
    			wort2[i] = wort1[i];
    		}
    		else{
    			wort2[i] = '-';					// Buchstaben durch "-" ersetzen
    		}	
    	}
    

    Wenn ich schreibe

    i < j;
    

    wird auch hier am Ende des Strings ein unerwünschtes Symbol oder gar der ganz Text angehängt.
    Was von beiden nun geschieht hat mit der Art der Initialisierung der Variable zu tun, auf welche "char *wort2" zeigt.

    char versteckteswort[strlen(daswort)];
    

    Das sorgt dafür, dass der ganz Satz angehängt wird.

    char *versteckteswort = malloc(strlen(daswort));
    

    Das hingegen sorgt nur für ein unerwünschtes Symbol.
    Das hat aber auch was mit der länge des Strings zu tun. Das Problem tritt hier auf, wenn die länge des Strings der Malfolge der acht entspricht.
    Das ist wirklich sehr verwirrend.
    Schreibe ich hingegen

    i <= j
    

    funktionier es gut. Aber zählt er da nicht um eins mehr hoch als eigentlich nötig ist?

    printf("%d\t%d\n", (int)strlen(wort2), i);
    

    i ist immer eins größer als strlen(wort2).
    Hab ich da nur ein Verständnisproblem?

    Ich hoffe, dass ihr mir weiterhelfen könnt...
    Danke dafür schon einmal im Voraus.

    ps.: Da ich neu hier bin, und nichts dazu in den Hinweisen gefunden habe wollte ich wissen ob ich eigentlich, wenn das Programm fehlerfrei läuft, den Code mal hier komplett reinstellen kann? Ich würde gerne von euch wissen, ob es gut geschrieben ist oder was hätte anders und / oder besser geschrieben werden können.



  • egens schrieb:

    Ich bin mir sicher, dass das Prinizip von Hangman jedem klar ist.

    Na dann fall ich schon mal aus. Viel Glück!



  • Poste doch bitte mal den gesamten Code, oder - falls er zu groß ist - bastele uns ein Minimalbeispiel zusammen, das den Fehler bzw. die Fehler aufweist. Dann können wir dir auch schnell helfen. 🙂



  • Initialisiere immer deine Variablen, Arrays und Strukturen mit ={0};
    Das klappt unter 32/64/.. immer weil standardisiert.

    Das Rumgefummle mit deutschen Sonderzeichen erspare dir mal, suche nach stricmp,strcmpi,strcasecmp,etc. deines Compilers (weil kein Standard) und verwende diese mit zuvor gesetztem locale, also z.B.

    if( setlocale(LC_ALL,"deu") ) /*wobei deu evtl. an deine Compiler-Bibliothek anzupassen ist*/
    {
      if( !stricmp(deinstring,deinteststring) )
        puts("gleich");
    }
    


  • klingt für mich im ersten Moment trotzdem nach Nulltermininierung...

    Ein Char Array (benutzt man in C für Strings) besteht aus einer Aneinanderreihung einzelner Chars. Damit klar ist, wann der "String" vorbei ist, wird nach dem String ein '\0' ins char-Array gesetzt. Deshalb sollte Dein Char-Array auch immer die "maximale Länge des zu speichernden Strings + 1" haben.

    Zuerst mal wäre es sinnvoll, die char-Arrays z.B. mit 0 zu initialisieren.
    Generell kannst du dafür die Dir bekannte Syntax mit = { 0 }; beim Deklarieren verwenden oder Du befüllst eben dann nach.

    Wenn am "Ende des Strings" irgendwann unerwünschte Zeichen "ausgegeben" werden, dann hast Du entweder vor den unerwünschten Zeichen kein '\0' im char-Array oder die "ausgebende" Funktion überliest das '\0' (macht wohl eher keine standardkonforme Ausgabefunktion.

    Stelle bitte also erstmal sicher, dass Deine Char-Arrays (falls innerhalb einer Funktion deklariert und nicht "global") initialisiert sind und dann poste den vollständigen relevanten SourceCode inklusive der entsprechend ausgegebenen Fehlermeldung.



  • PrettyP schrieb:

    Wenn am "Ende des Strings" irgendwann unerwünschte Zeichen "ausgegeben" werden, dann hast Du entweder vor den unerwünschten Zeichen kein '\0' im char-Array oder die "ausgebende" Funktion überliest das '\0' (macht wohl eher keine standardkonforme Ausgabefunktion.

    Könnte ja z.B. auch sein, dass er trotz Nullinitialisierung unerwünschte Zeichen sieht, weil er den ganzen Puffer (und damit auch alle Nullen) füllt.



  • Wobei globale arrays aber automatisch mit {0} initialisiert werden.
    Aus dem Text des OP geht aber leider nicht hervor ob geratenebuchstaben global oder lokal ist



  • Wutz schrieb:

    setlocale(LC_ALL,"deu")
    

    Sollte

    setlocale(LC_ALL,".1252")
    

    eigentlich nicht überall funktionieren? Oder ist das auch wieder nur gefährliches Halbwissen?



  • ".1252" als Locale-String habe ich bislang noch nie gesehen. Bei mir (Debian) funktioniert das jedenfalls nicht.

    Ich würde jetzt erwarten, dass

    setlocale(LC_ALL, "");
    

    die richtige Locale auswählt - dafür ist "" im Standard ja vorgesehen (vgl. 7.11.1 (3)) - habe allerdings gerade kein Windows zur Hand, um das zu testen.



  • Erstmal vielen Dank für eure schnellen Antworten.
    Ich habe jetzt erstmal alle Umlautfunktionen rausgenommen, um mich auf das Wesentliche im Programm zu konzentrieren.

    Ich poste mal den Code und hoffe, dass 134 Zeilen nicht zu viel sind.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    
    #define RATEN 7
    #define MAX   100
    
    void WortVerstecken(char *wort1, char *wort2, char const *pm){
    	int i;
    	int j = strlen(wort1);
    
    	for(i = 0; i <= j; i++){	// <-- zählt er so nicht einen mehr hoch als nötig... warum macht " i < j " bei der ausgabe probleme
    		if(wort1[i] == ' '){				// leerzeichen suchen
    			wort2[i] = ' ';
    		}
    		else if(strchr(pm, wort1[i])){		// satzzeichen suchen
    			wort2[i] = wort1[i];
    		}
    		else{
    			wort2[i] = '-';					// Buchstaben durch "-" ersetzen
    		}	
    	}
    	printf("%d\t%d\n", (int)strlen(wort2), i);
    }
    
    int main(void){
    
    	char zuratendeswort[MAX] = {0};
    	char geratenebuchstaben[30] = {0};					
    
    	char meinbuchstabe;
    	const char *pm = "!?.,";			//Satzzeichen
    
    	int fehler = 0;
    	int erraten = 0;
    	int count = 0;
    	int i;
    
    	char *zgr;
    
    	printf("Satz oder Wort eingeben : ");
    	fgets(zuratendeswort, MAX, stdin);			// wort eingeben und zeilenumbruch (\n) durch stringende (\0) ersetzen
    	zgr = strrchr(zuratendeswort, '\n');
    	*zgr = '\0';
    
    	int wortlaenge = strlen(zuratendeswort);
    	char daswort[wortlaenge];
    	printf("\n%s\n\n", zuratendeswort);
    
    	// wort in großbuchstaben umwandel und in ein neuen char kopieren
    
    	for(i=0; i <= wortlaenge; i++){
    		if(isalpha(zuratendeswort[i])){
    			daswort[i] = toupper(zuratendeswort[i]);
    	  	}
    		else{
    			daswort[i] = zuratendeswort[i];
    		}
    	}
    
    	printf("%s\n\n", daswort);		// zeigt hinter dem wort irgendwelche zeichen an	!!!!
    
    	 char versteckteswort[strlen(daswort)]; 		// <-- bei 8, 16, 32 usw. zeichen probleme beim erstellen des dummy !!!!
    													//     fügt dem dummy den ganzen satz zusätzlich an				    !!!!
    													//																    !!!!
      //  char *versteckteswort = malloc(strlen(daswort));// <-- fügt dem  dummy ein unerwünschtes symbol an  	            !!!!
    	WortVerstecken(daswort, versteckteswort, pm);	// Wort-Dummy erstellen
    
    	while(strcmp(daswort,  versteckteswort) != 0 && fehler != RATEN){	// Schleife verleassen wenn das Wort erraten wurde
    																		// oder "MAX" Fehler gemacht wurden
    		printf("===============================================================================\n");
    		printf("\nBereits verwendete Buchstaben: ");
    
    		for(i=0; i<(int)strlen(geratenebuchstaben); i++)	// Liste der Bereits verwendeten Buchstaben
    		{													// Zeigt beim erten aufruf schon Symbole an				    !!!!
    			printf("%c ", geratenebuchstaben[i]);
    		}
    		printf("\n\n");
    
    		printf("            Das Gesuchte Wort: %s\n\n", versteckteswort);
    
    		printf("           Buchstabe eingeben: ");
    		scanf("%c", &meinbuchstabe);
    		fflush(stdin);										// Buchstabe eingeben und in Großbuchstaben umwandeln
    
    		if(isalpha(meinbuchstabe)){
    			meinbuchstabe = toupper(meinbuchstabe);
    		}
    
    		printf("\n===============================================================================\n");
    		if(!strrchr(geratenebuchstaben, meinbuchstabe))			// überprüfen ob der eingegeben Buchstabe
    		{														// noch nicht verwendet wurde.
    			geratenebuchstaben[count] = meinbuchstabe;			
    			geratenebuchstaben[count+1] = '\0';
    			count++;
    			for(i=0; i < strlen(daswort); i++){
    				if(daswort[i] == meinbuchstabe){
    					versteckteswort[i] = meinbuchstabe;			// gefunden buchstaben in dummy kopieren
    					erraten = 1;
    				}
    			}
    
    		}
    		else{
    			printf("\n!!! Den Buchstaben hattest du schon.\t>> %c\n\n", meinbuchstabe);
    		}
    
    		if(erraten == 0){
    			fehler++;
    			printf("\n!!! Das war leider falsch.\t\t>> Du hast noch %d versuche.\n\n", RATEN - fehler);
    		}
    		else{
    			erraten = 0;
    			printf("\n!!! Das war richtig.\n\n");
    		}
    
    	}
    
    	// Spielende: Ergebnis auswerten und passende Nachricht ausgeben
    	if(fehler < 10){
    		printf("!!! Du hast das Wort erraten.\t>> %s\n\n\n", versteckteswort);
    	}
    	else{
    		printf("!!! Mehr Glueck beim naechsten mal.\n\n\n");
    	}
    
    	return EXIT_SUCCESS;
    }
    

    Wenn ich jetzt ein Wort eingebe, dessen länge der Malfolge der Acht entspricht, wird beim raten der erst Buchstabe nie gefunden.



  • Zeile 46: Kannste ersetzen durch zuratendeswort[strlen(zuratendeswort) - 1] = '\0';

    Zeile 49: Es ist in C üblicher, Variablen am Anfang des Scopes zu deklarieren, da nicht alle Compiler das Schlucken.
    Zeile 50: Das ist in C(89) nicht üblich, Arraygrößen müssen konstant sein. (Zur Compilezeit feststehen). Zudem brauchst du strlen() + 1 Platz, und die Variable "wortlaenge" ist sinnlos.

    Zeile 55: Die Schleife kopiert das '\0' am Ende nicht mit, (was im übrigen auch ein Fehler wäre, da "daswort" zu klein ist). Wie auch immer, du müsstest es mit kopieren um einen gültigen String zu erhalten.

    Weiter habe ich erstmal nicht gelesen.

    Edit:
    Zeile 88: fflush(stdin) ist böse.



  • Danke für deine Hinweise.
    Ich werde das Programm jetzt erstmal überarbeiten, und mit feste Arraygrößen arbeiten.



  • Den Zeilenumbruch und die Grossschreibung ein wenig anders zusammen gesetzt.
    Zeile 40 bis 65:

    int i;
    
        printf("Satz oder Wort eingeben : ");
        fgets(zuratendeswort, MAX, stdin);            // wort eingeben und zeilenumbruch (\n) durch stringende (\0) ersetzen
    
        int wortlaenge = strlen(zuratendeswort);
        char daswort[wortlaenge];
        printf("\n%s\n\n", zuratendeswort);
    
        // wort in großbuchstaben umwandel und in ein neuen char kopieren
    
        for(i=0; i < wortlaenge-1; i++){	// nur bis \n lesen
            if(isalpha(zuratendeswort[i])){
                daswort[i] = toupper(zuratendeswort[i]);
              }
            else{
                daswort[i] = zuratendeswort[i];
            }
        }
        daswort[i] = '\0';		// ihr wisst schon
    
        printf("%s\n\n", daswort);        // zeigt hinter dem wort irgendwelche zeichen an    !!!!
    


  • Danke dafür...
    Das hat mir sehr geholfen. Ich hatte da eine kleinen Gedankenfehler.
    Ein Array fängt ja immer bei Null an zu zählen, dass hatte ich in dem Moment nicht berücksichtigt.

    Mir ist auf gefallen, bei einem kleinen Test, dass auch ohne

    daswort[i] = '\0';
    

    das '\n' scheinbar verschwunden ist. Wie kommt das? Und, wurde der String dann trotzdem mit '\0' beendet?

    Ich hatte das mit diesem kurzem Programm probiert.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define MAX	51
    
    int main(void){
    
    	char zew[MAX] = {0};	
    	char dmy[MAX] = {0};	
    
    	int i;
    
    	printf("Wort oder Satz eingeben: ");
    	fgets(zew, MAX, stdin);
    
    	for(i = 0; i < strlen(zew)-1; i++){
    		dmy[i] = zew[i];
    	}
    	//dmy[i] = '\0';
    
    	printf("%s hat %d zeichen\n", dmy, strlen(dmy));
    
    	return EXIT_SUCCESS;
    }
    


  • egens schrieb:

    Danke dafür...
    Das hat mir sehr geholfen. Ich hatte da eine kleinen Gedankenfehler.
    Ein Array fängt ja immer bei Null an zu zählen, dass hatte ich in dem Moment nicht berücksichtigt.

    Mir ist auf gefallen, bei einem kleinen Test, dass auch ohne

    daswort[i] = '\0';
    

    das '\n' scheinbar verschwunden ist. Wie kommt das? Und, wurde der String dann trotzdem mit '\0' beendet?

    Ich hatte das mit diesem kurzem Programm probiert.

    Die Null-Terminierung hast du bei deinem Testprogramm schon gegeben, weil die Arrays sauber initialisiert sind. Und das Newline hast du bei deiner for()-Schleife nicht mitkopiert (wozu soll die überhaupt gut sein?).



  • Darum geht es ja.
    Newline sollte nicht mitkopiert werden.

    Jetzt ist der Groschen gefallen, just in dem Moment wo ich schreiben wollte, dass ich nicht verstehe warum das newlin nicht mitkopiert wurde...

    Bei dem String "TEST\n" befindet sich newline im Array an vierter Stelle.
    diese for-Schleife ...

    for(i=0; i < strlen("TEST\n")-1; i++);
    

    ... würde aber in diesem Fall nur so lange durchlaufen wie i kleiner ist als 4 und nicht bis 4, wo sich newline befindet. Somit wird es nicht mehr mitkopiert.

    Manchmal sieht man den Wald vor lauter Bäumen nicht mehr. 🙂



  • egens schrieb:

    Darum geht es ja.
    Newline sollte nicht mitkopiert werden.

    Gegenfrage: Warum mußt du die Eingabe überhaupt kopieren - es geht doch wesentlich schneller, im Original-Array das Newline mit '\0' zu überschreiben als alle anderen Zeichen in ein zweites Array zu kopieren.



  • Das ist eigentlich nicht mehr wichtig. Ich habe das Programm schon dahingehend geändert, und der Text wird nicht mehr kopiert. Ich wollte nur wissen, warum ich vorher an dieser Stelle Probleme hatte. Wo mein Fehler war.
    Damit ich ihn später nicht wiederhole.



  • So, um zu einem Abschluss zu kommen, hier der Code der ohne Probleme funktioniert.
    Danke für eure Hilfe.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    
    #define MAX	51
    #define VSH 7
    
    int main(void){
    
    	char zew[MAX] = {0};	// Zu eratenes Wort
    	char dmy[MAX] = {0};	// Dummy
    
    	char bsn[30]  = {0};	// Buchstaben
    
    	char buf[3]	  = {0};
    	char ege;				// Eingabe
    
    	int wl  = 0;			// Wortlänge
    
    	int zlr = 0;			// zähler für Buchstaben
    	int gdn = 0;			// gefunden
    	int flr = 0;			// fehler
    
    	int i;
    
    	printf("Wort oder Satz eingeben: ");
    	fgets(zew, MAX, stdin);
    	wl = strlen(zew);
    	for(i = 0; i < wl-1; i++){                // Dummy erstellen und Wort in Großbuchstaben umwandeln
    		if(isalpha(zew[i])){
    			dmy[i] = '-';
    			zew[i] = toupper(zew[i]);
    		}
    		else{
    			dmy[i] = zew[i];
    		}
    	}
    	zew[i] = '\0';
    
    	while(strcmp(zew, dmy) != 0 && flr < VSH){
    
    		printf("===============================================================================\n\n");
    
    		printf("Bereits verwendete Buchstaben: ");
    		for(i = 0; i < (int)strlen(bsn); i++){
    			printf("%c ", bsn[i]);
    		}
    		printf("\n\n");
    
    		printf("     Gesuchtes Wort bze. Satz: %s\n\n", dmy);
    
    		printf("           Buchstabe eingeben: ");
    
    		scanf("%c", &ege);
    		fflush(stdin);
    
    		printf("\n\n===============================================================================\n\n");
    
    		if(isalpha(ege)){
    			ege = toupper(ege);
    		}
    
    		if(!strchr(bsn, ege)){
    			bsn[zlr] = ege;
    			zlr++;
    
    			for(i = 0; i < wl-1; i++){
    				if(zew[i] == ege){
    					dmy[i] = ege;
    					gdn = 1;
    				}
    			}
    		}
    		else{
    			printf("!!! Den Buchstaben hattest du schon.\t>> %c\n\n", ege);
    		}
    
    		if(gdn == 0){
    			flr++;
    			printf("!!! Das war falsch.\t\t\t>> Noch %d Versuche.\n\n", VSH-flr);
    		}
    		else{
    			printf("!!! Das war richtig.\n\n");
    			gdn = 0;
    		}
    
    	}
    	printf("===============================================================================\n\n");
    	if(flr < VSH){
    		printf("!!! Glueckwunsch, Du hast das gesuchte Wort erraten. >> %s\n\n\n", zew);
    	}
    	else{
    		printf("!!! Du hast leider Verloren. Mehr Glueck beim naechsten Mal.\n\n\n");
    	}
    
    	return EXIT_SUCCESS;
    }
    

    ps.: ja ih weiß fflush(stdin) ist böse 😉



  • Hier noch mal eine Version von mir, vielleicht mal ganz nett für dich zu sehen, wie man das noch hätte machen können. (Jetzt kommt bestimmt gleich wieder einer der eine Verbesserung vorschlägt :D)

    int compare(const char *base_str, char *dest_str, char c)
    {
      int rval = 0;
      while (*base_str)
      {
        if (*base_str == c)
        {
          *dest_str = c;
          ++rval;
        }
        ++base_str;
        ++dest_str;
      }
      return rval;
    }
    
    int main(int argc, char *argv[])
    {
      char secret[0x100];
      char word[0x100];
      int i, counter = 0, c;
    
      printf("Enter secret word: ");
      fgets(secret, sizeof(secret), stdin);
      secret[strlen(secret) - 1] = '\0';
      for (i = 0; i < strlen(secret); ++i) // geht schöner mit Pointern ;)
      {
        secret[i] = toupper(secret[i]);
        word[i] = '_';
      }
      word[i] = '\0';
    
      while (counter < 10)
      {
        printf("Enter char: ");
        while ((c = getchar()) == '\n')
          ;
        if (!compare(secret, word, toupper(c)))
          ++counter;
        printf("Actual word: %s\n", word);
        if (!strcmp(secret, word))
          break;
      }
      if (counter >= 10)
        puts("Game over :(");
      if (counter < 10)
        puts("You win!");
    
      return 0;
    }
    

Anmelden zum Antworten