Ошибка сегментации при комментировании 2 бесплатных (игра Сокобан) - PullRequest
2 голосов
/ 18 марта 2019

Со вчерашнего дня я пытаюсь устранить ошибку сегментации, она сводит меня с ума ... Итак, у меня есть эта программа, которую я использую для реализации игры Сокобан, в которой дана карта, и план вроде«NSEW» дается, что означает, что я хочу, чтобы игрок двигался на север, затем на юг, затем и т. Д. На данной карте.

map* replay(map* map_loaded, int length, char* plan){
    map* new_map=move(map_loaded,plan[0]);
    map* old_map=deep_copy(new_map);
    for (int i=1 ; i<length ; i++){
        free(new_map->p_char);
        free(new_map);
        new_map=move(old_map,plan[i]);
        free(old_map->p_char);
        free(old_map);
        old_map=deep_copy(new_map);
    }
    free(old_map->p_char);
    free(old_map);
    return new_map;
}

Карта - это структура, определяемая следующим образом:

typedef struct map map;
struct map{
    int width;
    int height;
    char* p_char;
};

Функция перемещения выполняет одно движение;

Deep_copy создает глубокую копию структуры карты:

map* deep_copy(map* map_loaded){

    map* new_map=malloc(sizeof(map));

    char* p_array=map_loaded->p_char;
    int width=map_loaded->width;
    int height=map_loaded->height;
    new_map->width=width;
    new_map->height=height;

    char* p_new=malloc(sizeof(char)*width*height);
    for (int i=0 ; i<width*height ; i++){
        p_new[i]=p_array[i];
    }
    new_map->p_char=p_new;
    return(new_map);
}

И что происходит, если я оставляю код таким образом, если выбранная длина (количество движений, которое я хочу сделать игроку) слишком велика (зависит от карты и движений), при выполнении программы replay.c:

int main(int argc, char *argv[]){
    if (argc != 4) {
        fprintf(stderr, "You must provide a file name or enough elements!\n");
        exit(EXIT_FAILURE);
    }

    map* map_loaded=load(argv[1]);

    int length=atoi(argv[2]);

    char* plan=argv[3];


    map* new_map=replay(map_loaded,length,plan);
    print_map(new_map);

    free(new_map->p_char);
    free(new_map);
    free(map_loaded->p_char);
    free(map_loaded);
    return 1;
}

Я получаюошибка сегментации ... Но если я сделаю:

//free(old_map->p_char);
//free(old_map);

Все работает отлично !!Я действительно не понимаю, почему ... Проблема в том, что, если я сделаю это free, valgrind скажет мне, что у меня не так много свободных, как allocs, что нормально ...

Я был бы очень признателен за любую помощьучитывая ... Заранее спасибо, если вы достаточно смелы, чтобы прочитать меня до этого момента!

Редактировать: вот моя функция перемещения;

   map* move(map* map_loaded, char dir){
    int pos=Position(map_loaded);
    int width=map_loaded->width;

    map* new_map=deep_copy(map_loaded);
    char* p_new_array=new_map->p_char;


    switch(dir){
        case 'N':
        if (p_new_array[pos-width]=='#'){ // Si il y a un mur au dessus
                return(map_loaded);
            }
            if ((p_new_array[pos-width]=='$' && p_new_array[pos-2*width]=='$')
              ||(p_new_array[pos-width]=='$' && p_new_array[pos-2*width]=='*') 
              ||(p_new_array[pos-width]=='*' && p_new_array[pos-2*width]=='$') 
              ||(p_new_array[pos-width]=='*' && p_new_array[pos-2*width]=='*')){ //s'il y a 2 caisses au dessus
                return(map_loaded);
            }
            if ((p_new_array[pos-width]=='$' && p_new_array[pos-2*width]=='#') //s'il y a une caisse au niveau -1 et un mur au niveau -2
              ||(p_new_array[pos-width]=='*' && p_new_array[pos-2*width]=='#')){
                return(map_loaded);
            }

            // On vérifie d'abord s'il y a une caisse à déplacer

            if (p_new_array[pos-width]=='$' || p_new_array[pos-width]=='*'){
                if (p_new_array[pos-2*width]=='.'){
                    p_new_array[pos-2*width]='*';
                }   
                else {
                    p_new_array[pos-2*width]='$';
                }

                if (p_new_array[pos-width]=='*'){
                    p_new_array[pos-width]='.';
                }
                else{
                    p_new_array[pos-width]=' ';
                }   
            }


            //On change le char en position du joueur 

            if (p_new_array[pos]=='+'){
                p_new_array[pos-width]='@';
                p_new_array[pos]='.';

            }
            else if (p_new_array[pos-width]=='.'){
                p_new_array[pos-width]='+';
                p_new_array[pos]=' ';
            }
            else {
                p_new_array[pos-width]='@';
                p_new_array[pos]=' ';
            } 
            break;



        case 'S':
        if (p_new_array[pos+width]=='#'){ // Si il y a un mur en dessous
                return(map_loaded);
            }
            if ((p_new_array[pos+width]=='$' && p_new_array[pos+2*width]=='$')
              ||(p_new_array[pos+width]=='$' && p_new_array[pos+2*width]=='*') 
              ||(p_new_array[pos+width]=='*' && p_new_array[pos+2*width]=='$') 
              ||(p_new_array[pos+width]=='*' && p_new_array[pos+2*width]=='*')){//s'il y a 2 caisses au dessus
                return(map_loaded);
            }
            if ((p_new_array[pos+width]=='$' && p_new_array[pos+2*width]=='#') //s'il y a une caisse au niveau +1 et un mur au niveau +2
              ||(p_new_array[pos+width]=='*' && p_new_array[pos+2*width]=='#')){
                return(map_loaded);
            }
             // On vérifie d'abord s'il y a une caisse à déplacer

            if (p_new_array[pos+width]=='$' || p_new_array[pos+width]=='*'){
                if (p_new_array[pos+2*width]=='.'){
                    p_new_array[pos+2*width]='*';
                }   
                else {
                    p_new_array[pos+2*width]='$';
                }

                if (p_new_array[pos+width]=='*'){
                    p_new_array[pos+width]='.';
                }
                else{
                    p_new_array[pos+width]=' ';
                }   
            }

             //On change le char en position du joueur 

            if (p_new_array[pos]=='+'){
                p_new_array[pos+width]='@';
                p_new_array[pos]='.';

            }
            else if (p_new_array[pos+width]=='.'){
                p_new_array[pos+width]='+';
                p_new_array[pos]=' ';
            }
            else {
                p_new_array[pos+width]='@';
                p_new_array[pos]=' ';
            } 
            break;




        case 'W':
        if (p_new_array[pos-1]=='#'){ // Si il y a un mur en dessous
                return(map_loaded);
            }
            if ((p_new_array[pos-1]=='$' && p_new_array[pos-2]=='$')
              ||(p_new_array[pos-1]=='$' && p_new_array[pos-2]=='*') 
              ||(p_new_array[pos-1]=='*' && p_new_array[pos-2]=='$') 
              ||(p_new_array[pos-1]=='*' && p_new_array[pos-2]=='*')){ //s'il y a 2 caisses à gauche
                return(map_loaded);
            }
            if ((p_new_array[pos-1]=='$' && p_new_array[pos-2]=='#') //s'il y a une caisse au niveau -1 et un mur au niveau -2
              ||(p_new_array[pos-1]=='*' && p_new_array[pos-2]=='#')){
                return(map_loaded);
            }

            // On vérifie d'abord s'il y a une caisse à déplacer

            if (p_new_array[pos-1]=='$' || p_new_array[pos-1]=='*'){
                if (p_new_array[pos-2]=='.'){
                    p_new_array[pos-2]='*';
                }   
                else {
                    p_new_array[pos-2]='$';
                }

                if (p_new_array[pos-1]=='*'){
                    p_new_array[pos-1]='.';
                }
                else{
                    p_new_array[pos-1]=' ';
                }   
            }


            //On change le char en position du joueur 

            if (p_new_array[pos]=='+'){
                p_new_array[pos-1]='@';
                p_new_array[pos]='.';

            }
            else if (p_new_array[pos-1]=='.'){
                p_new_array[pos-1]='+';
                p_new_array[pos]=' ';
            }
            else {
                p_new_array[pos-1]='@';
                p_new_array[pos]=' ';
            } 
            break;


        case 'E':
        if (p_new_array[pos+1]=='#') {// Si il y a un mur à droite
                return(map_loaded);
            }
            if ((p_new_array[pos+1]=='$' && p_new_array[pos+2]=='$')
              ||(p_new_array[pos+1]=='$' && p_new_array[pos+2]=='*') 
              ||(p_new_array[pos+1]=='*' && p_new_array[pos+2]=='$') 
              ||(p_new_array[pos+1]=='*' && p_new_array[pos+2]=='*')){ //s'il y a 2 caisses à droite
                return(map_loaded);
            }
            if ((p_new_array[pos+1]=='$' && p_new_array[pos+2]=='#') //s'il y a une caisse au niveau +1 et un mur au niveau +2
              ||(p_new_array[pos+1]=='*' && p_new_array[pos+2]=='#')){
                return(map_loaded);
            }

            // On vérifie d'abord s'il y a une caisse à déplacer

            if (p_new_array[pos+1]=='$' || p_new_array[pos+1]=='*'){
                if (p_new_array[pos+2]=='.'){
                    p_new_array[pos+2]='*';
                }   
                else {
                    p_new_array[pos+2]='$';
                }

                if (p_new_array[pos+1]=='*'){
                    p_new_array[pos+1]='.';
                }
                else{
                    p_new_array[pos+1]=' ';
                }   
            }


            //On change le char en position du joueur 

            if (p_new_array[pos]=='+'){
                p_new_array[pos+1]='@';
                p_new_array[pos]='.';

            }
            else if (p_new_array[pos+1]=='.'){
                p_new_array[pos+1]='+';
                p_new_array[pos]=' ';
            }
            else {
                p_new_array[pos+1]='@';
                p_new_array[pos]=' ';
            } 
            break;

        }
    return(new_map);
}

А вот функция загрузки;

char* Array_Creator(char* filename){
    FILE* p_file = fopen(filename, "r");
    char* p_array = NULL;
    if (p_file == NULL) {
        exit(EXIT_FAILURE);
    }
    else{   
        size_t size=1;
        int c;
        while (getc(p_file)!=EOF) {
            size++;
        }
        fseek(p_file,0,SEEK_SET);
        while ((c=getc(p_file))!='\n' && c!=EOF) { //on se débarasse de la première ligne
            size--;
        }
        p_array=(char*)malloc(sizeof(char)*size);
        if (p_array!=NULL) { //si jamais le malloc ne fonctionne pas
            for(size_t i=0; i<size-1; i++) {
            p_array[i]=(char)getc(p_file);


            if (p_array[i] == '\n') { // si le caractère est une nouvelle ligne, on s'en sépare
                i--; // on ajuste alors la taille et l'indice
                size--;
                }
            }
            p_array[size-1]='\0';
        }
        fclose(p_file);
    }
    return p_array;
}

//La fonction Dimensions permet de récupérer les dimensions d'une map

couple Dimensions(char *p_char){

    FILE *p_file = NULL;
    p_file=fopen(p_char, "r");

    if (p_file == NULL) {
        fprintf(stderr, "Cannot read file %s!\n", p_char);
        exit(EXIT_FAILURE);
    }


    couple dim={0,0}; //la structure couple est déf dans le loader.h

    int width         = 0;
    int height        = 0;
    int fscanf_result = 0;

    fscanf_result = fscanf(p_file, "%d %d\n", &width, &height);
    if (fscanf_result != 2) {
        fprintf(stderr, "First line is not syntactically correct!\n");
        exit(EXIT_FAILURE);
    }

    dim.x=width;
    dim.y=height;
    fclose(p_file);
    return dim;
}


//La fonction Load est celle demandée et permettant de charger une carte ; elle utilise Dimensions et Array_Creator

map* load(char *filename){
    map* map_loaded=malloc(sizeof(map)); //on alloue dynamiquement la map

    //Dans un premier temps on récupère les dimensions

    couple map_dim={0,0};  
    map_dim=Dimensions(filename);
    map_loaded->width=map_dim.x;
    map_loaded->height=map_dim.y;

    //Dans un second temps on définit le tableau 1D contenant l'ensemble des éléments de la map

    char* p_char=Array_Creator(filename);
    map_loaded->p_char=p_char;
    return map_loaded;
}

Командная строка, например: ./replay ./data/soko.in 4 7 "NSWSESNWW"

1 Ответ

1 голос
/ 19 марта 2019

В функции move следует изменить все

return(map_loaded); => return(new_map);

. Будет дважды загружено map_loaded, когда функция перемещения вернет map_loaded

Анализ кода

map *replay(map *map_loaded, int length, char *plan)
{
    map *new_map = move(map_loaded, plan[0]); // [2] set new_map to map_loaded
    map *old_map = deep_copy(new_map);
    for (int i = 1 ; i < length ; i++) {
        free(new_map->p_char);
        free(new_map);                        // [3] new_map be freed, 
                                              // equivalent map_loaded be freed
        new_map = move(old_map, plan[i]);
        ...
    }
    ...
    return new_map;
}

int main(int argc, char *argv[])
    ...
    map *map_loaded = load(argv[1]);         // [1] map_loaded be malloc
    ...
    map *new_map = replay(map_loaded, length, plan);
    ...
    free(map_loaded->p_char);
    free(map_loaded);                        // [4] map_loaded be freed
    ...
}
...