Benötige Hilfe - Segmentation Fault



  • Hallo zusammen!

    Seit mehreren Tagen quält mich ein Problem:
    Ich muss für die Uni ein 4-gewinnt-Spiel programmieren. Vorkenntnisse waren keine vorhanden, trotzdem habe ich es mit viel Aufwand geschafft.
    Also kompiliert, mehrfach getestet - alles funktionierte einwandfrei. Dann habe ich das ganze auch auf Linux getestet und es wurde mir ein Segmentation Fault ausgegeben.

    Jetzt habe ich täglich stundenlang das Internet durchforstet, herumprobiert, mein Programm zwischendurch schon wieder komplett zerstört, wieder aufgebaut usw.
    Aber der Fehler bleibt bestehen.

    GDB zeigt mir immer folgendes an:

    #0  _IO_fgets (buf=0xbffff630 "\001", n=128, fp=0x2d584f58) at iofgets.c:52
    #1  0x08049872 in readInput () at assa.c:500
    #2  0x08049bbb in main (argc=2, argv=0xbffff7d4) at assa.c:609
    

    Der Fehler tritt allerdings nur auf, wenn ich das Spiel mit einem Kommandozeilen-Argument starte. Dieses Argument ist der Titel eines gespeicherten Save-Files, das geöffnet werden soll (was mit Windows ja auch funktioniert).

    Laut GDB ist der Fehler aber in der Funktion readInput, die Zeile 500 wo der Fehler auftreten soll kommentiere ich:

    void readInput()
    {
    char help[] = "help";
    char quit[] = "quit";
    char show[] = "show";
    char save[] = "save";
    char set_A[] = "set A";
       ...
    char set_F[] = "set F";
    char input[128];
    
    fgets(input, 128, stdin); //hier ist laut GDB der Segmentation Fault
    deleteN(input); //externe Funktion, entfernt \n und funktioniert
    int place = 0;
    
    	...//zwischen kommt ein counter, funktioniert auch.
     if(count_all_x == count_all_o)
        player_on_turn = 0;
     else
        player_on_turn = 1;
    
    if(strncmp(input, help, 4) == 0)
    {
      printf("..."); 
    }
    else if(strncmp(input, quit, 4) == 0)
        exit(QUIT);
    
    else if(strncmp(input, show, 4) == 0)
    {
        printField();
    }
    else if(strncmp(input, save, 4) == 0)
    {
      saveGame(input+5); //savegame-Funktion; funktioniert
    }
    else if(strncmp(input, set_A, 5) == 0)
    {
      place = 0;
      setToken(place, player_on_turn); //Spielfunktion, funktioniert auch        //einwandfrei
    }
        ...
    else if(strncmp(input, set_F, 5) == 0)
    {
      place = 5;
      setToken(place, player_on_turn);
    }
    else
      printf("Unknown command.\n");
    

    Und hier wird der Fehler auch angezeigt (vermutlich aber nur, weil es wieder auf obige Funktion geht):

    int main(int argc, char *argv[])
    {
      switch(argc)
      {
      case (2):
      {
      openSaveGame(argv[1]);
      while(1)
      {
    	printf("ep> ");
    	readInput();//in dieser Zeile ist der Fehler.
      }
      break;
    }
      ...
    }
    

    Da der Fehler allerdings nur auftritt, wenn mit Argumenten gestartet wird und das ganze dann über meine Savegame-Funktion läuft, vermute ich ja eher, dass da irgendwas nicht passt, deshalb poste ich die hier auch noch:

    void openSaveGame(char *input)
    {
    FILE *open_file_pointer;
    open_file_pointer = fopen(input, "rb");
    char temp_save[48];
    if(open_file_pointer == NULL) 
    {
      printf("Error loading file.\n");
      exit(FILEIOERROR);
    }
    else
    {
      fscanf(open_file_pointer, "%s\n", temp_save);
      //Walk through the char temp_save and put the posts into the matrix.
      int row;
      int column;
      int count_temp_save;
      for(row = 6; row >= 0; row--)
      {
        for(column = 0, count_temp_save = 4; count_temp_save <= 45; 
        column++, count_temp_save++)
        {
    	if((temp_save[count_temp_save]) == 'X')
    	  matrix[row][column] = 'X';
    	else if((temp_save[count_temp_save]) == 'O')
    	  matrix[row][column] = 'O';
    	else if((temp_save[count_temp_save]) == '-')
    	  matrix[row][column] = '-';
        }
    }
    printField(); //gibt das Spielfeld aus
    }
    fclose(open_file_pointer);
    }
    

    Ich hoffe, das ist jetzt nicht zu lang geworden.

    Das Ganze ist sicherlich sehr umständlich programmiert, das ist mir klar. Ich bin schon überrascht, dass ich es so weit hinbekommen habe.

    Wie gesagt, das ganze funktioniert auf Windows, auch mit Linux wird mein geladenes Spielfeld noch korrekt ausgegeben und dann wird mir (mit GDB) der oben genannte Segmentation Fault ausgegeben, auf den ich nicht komme. Valgrind habe ich auch ausprobiert, allerdings hat mir das auch nicht mehr geholfen.

    Ich hoffe, ihr könnt mir weiterhelfen. Die Sache hat mir schon zwei schlaflose Nächte bereitet und ein Profi sieht meinen Fehler vermutlich sofort. 😉

    Danke jedenfalls im Voraus!

    ~edit durch SeppJ: Korrekte Codetags.~


  • Mod

    Hallo,

    erst einmal eine Anmerkung, bevor ich es vergesse: Für Quellcode nimmst du am besten die sprachspezifischen Tags, nicht die generischen Codetags. Dann wird die Syntax bunt hervorgehoben, was sehr hilfreich ist. Für C also die Tags, die du mit dem Knopf unter dem 😡 -Smiley einfügen kannst. Da dies dein erster Beitrag ist, habe ich das in deinem Beitrag korrigiert.

    Zum Thema:
    Der Fehler springt mir nicht direkt ins Auge, das liegt da dran, dass du uns ein bisschen mit Informationen überhäufst. Jedenfalls ist der Fehler nicht direkt in der Zeile, wo der Absturz auftritt. Das ist auch ganz normal, denn die Symptome, die du beschreibst, sind ganz typisch für eine Art von Fehler, bei der du in deinem Programm irgendwo über die Grenzen von Feldern hinweg schreibst oder liest. Das heißt, der Fehler kann ganz woanders verursacht werden und sich dann später erst äußern. Das ist auch die Erklärung für das unterschiedliche Verhalten bei Linux und Windows: Der interne Speicheraufbau des Programms kann auf verschiedenen Systemen (oder auch mit verschiedenen Compilern oder gar nur Compilereinstellungen) unterschiedlich sein. Beim einen hast du halt zufällig eine kritische Stelle überschrieben, beim anderen nicht (es ist natürlich dennoch falsch, du merkst es bloß nicht).

    Du hast bereits versucht, den Fehler einzukreisen, was genau die richtige Vorgehensweise ist. Am Besten wäre es, wenn du versuchst, alles unnötige aus deinem Programm zu kürzen, so dass es so gerade noch den Fehler aufweist und sonst nichts. Also ein minimales, ausführbares Beispiel für den Fehler erstellen. Prüf dabei in jedem Schritt, ob der Fehler noch auftritt. Wenn du dabei irgendetwas änderst, so dass der Fehler nicht mehr auftritt, dann besteht eine groß Chance, dass der Fehler in dem gekürzten Teil liegt. Es kann gut sein, dass du den Fehler dabei selber findest. Falls nicht, kannst du dieses minimale Beispiel hier posten und wir können den Code einfach 1:1 kopieren und ausführen und sehen dann bei uns den Fehler in Aktion. Dann finden wir auch ganz schnell die Ursache.

    Du scheinst hier mit globalen Variablen zu arbeiten (matrix). Das ist ein typischer Grund, wieso man beim Programmieren solche Arten von Fehlern verursacht (weil man den Überblick verliert, was wann wo geändert wird) und macht die Fehlersuche dann auch extrem schwer (weil man keinen Überblick hat, was wann wo geändert werden könnte). Daher sind globale Variablen so gut es geht zu vermeiden. Normalerweise solltest du überhaupt gar keine benötigen.



  • Nach welcher Zeit tritt denn der Fehler auf?

    Benutzt du ein gespeichertes Spiel von Windows?

    Was mir an openSaveGame auffällt, ist die fehlende Längenangabe bei fscanf :

    fscanf(open_file_pointer, " %47s", temp_save);
    

    Wenn dein Spiel in einer Zeile abgespeichert ist, dann kannst du auch fgets nehmen.



  • Was ist denn matrix ?
    ein int[7][42] ?



  • Vielen Dank für eure Antworten. Ich bin im Moment ziemlich im Stress, werde eure Vorschläge am Nachmittag testen und mich dann ausführlicher melden.

    Furble Wurble schrieb:

    Was ist denn matrix ?
    ein int[7][42] ?

    Die Matrix ist ist ein char[7][6], gefüllt mit - (freier Platz), X und O.



  • Dann hast du da schon ein Problem.

    In openSaveGame wird column genauso oft inkrementiert wie count_temp_save

    Demnach kann das laden auch unter Windows nicht funktionieren.



  • DirkB schrieb:

    Dann hast du da schon ein Problem.

    In openSaveGame wird column genauso oft inkrementiert wie count_temp_save

    Demnach kann das laden auch unter Windows nicht funktionieren.

    Das wars! Super, danke. Habe die for-Schleife zuerst anders gehabt, dann ist mir column<=5 anscheinend rausgerutscht.

    Habs jetzt so gelöst:

    while(count_temp_save <= 45)
    {
      for(row = 0; row <= 6; row++)
      {
        for(column = 0; column <= 5; column++, count_temp_save++)
        {  
          if((temp_save[count_temp_save]) == 'X')
    	matrix[row][column] = 'X';
          else if((temp_save[count_temp_save]) == 'O')
    	matrix[row][column] = 'O';
          else if((temp_save[count_temp_save]) == '-')
    	matrix[row][column] = '-';
        }
      }
    }
    

    Und alles funktioniert einwandfrei! Komisch, dass GDB unter Windows da nicht gemeckert hat. Ich hätte ja nie gerechnet, dass es so "banal" ist, ich hab immer nur auf die Strings geschaut.

    Danke jedenfalls nocheinmal an alle, die sich hier eingebracht haben für den tollen Support! 👍



  • duffman schrieb:

    Habs jetzt so gelöst:...

    Jetzt streng nochmal Deine Gehirnzellen für Logik an. Und entferne evtl. vorhandene Redundanzen.

    Übrigens in C nimmt man als Laufbedingung normalerweise <, nicht <=.
    Das hat auch den Vorteil, dass Du die 7 Spalten und 6 Reihen "wiederfindest":

    for(int row=0, count_temp_save=0; row<6; row++)
      {
        for(int column=0; column<7; column++, count_temp_save++)
        {  
          ....
        }
      }
    

    Dein Spielfeld ist irgendwie gedreht, oder? 🙂



  • Furble Wurble schrieb:

    duffman schrieb:

    Habs jetzt so gelöst:...

    Jetzt streng nochmal Deine Gehirnzellen für Logik an. Und entferne evtl. vorhandene Redundanzen.

    Übrigens in C nimmt man als Laufbedingung normalerweise <, nicht <=.
    Das hat auch den Vorteil, dass Du die 7 Spalten und 6 Reihen "wiederfindest":

    for(int row=0, count_temp_save=0; row<6; row++)
      {
        for(int column=0; column<7; column++, count_temp_save++)
        {  
          ....
        }
      }
    

    Dein Spielfeld ist irgendwie gedreht, oder? 🙂

    Ich musste jetzt links oben zählen anfangen und nicht wie vorher links unten. Das passt jetzt auf jeden Fall so. Ich muss mir aber jetzt überlegen, warum es auch vorher richtig ausgegeben wurde, das macht nämlich gerade wenig Sinn für mich. 😉

    Ok, das mit der Laufbedingung habe ich nicht gewusst, wir haben in der Vorlesung eigentlich immer beides verwendet. Du hast aber auf jeden Fall recht, dass eine durchgängige Schreibweise klüger wäre.


Anmelden zum Antworten