Запутанная ошибка формата C Программа fscanf

Я создаю игру Connect 4 на C, и часть игры загружает и сохраняет состояние игры. Способ сохранения - через фрагмент кода ниже

      if (strncmp (temp, "save", 5) == 0){

      int r, c;

      // this writes the game settings to a file
      int *rows = &num_rows;
      int *cols = &num_columns;
      int *len = &length_to_win;
      FILE *fp = fopen("gameSave.txt", "w+");
      fprintf(fp, "%d ", *rows);
      fprintf(fp, "%d ", *cols);
      fprintf(fp, "%d ", *len);
      fprintf(fp, "\n\n");

      for (r = 0; r < num_rows; r++) {
        for (c = 0; c < num_columns; c++) {
          fprintf(fp, "%c ", aPtr[r][c]);
          }
        fprintf(fp, "\n");
      }

      printf("Game Saved\n");
      fclose(fp);

  }

«temp» относится к пользовательскому вводу, который обычно представляет собой номер столбца, в который игрок хочет поместить свою фигуру; однако, если они введут «сохранить», то он выполнит этот фрагмент кода и создаст файл, который выглядит следующим образом.

cat gameSave.txt 
5 5 4 

0 1 0 1 0 
1 0 1 0 1 
0 1 0 1 0 
9 9 9 9 9 
9 9 9 9 9

5 5 4 относится к столбцам строк и длине для выигрыша, а 2D-массив под ним (который разделен двумя \n) представляет собой символьное представление доски (это действительно символы, а не целые числа). Итак, моя проблема в том, что я использую этот фрагмент кода ниже для загрузки данных сохранения в середине игры.

  if (strncmp (temp, "load", 5) == 0){

    int r, c;

    // this loads the game settings into game
    FILE *fp = fopen("gameSave.txt", "r");
    fscanf(fp, "%d %d %d", &num_rows, &num_columns, &length_to_win);
    fscanf(fp, "\n\n");

    aPtr = malloc(num_rows * sizeof(char*));

    for (i = 0; i < num_rows; i++){
        aPtr[i] = malloc(num_columns * sizeof (char));
    }

    for (r = 0; r < num_rows; r++) {
      for (c = 0; c < num_columns; c++) {
        fscanf(fp, "%c ", aPtr[r][c]);
      }
        fscanf(fp, "\n");
      }

    printf("Game Loaded\n");
    fclose(fp);
  }

это успешно загружает игровые настройки (строки, столбцы и длину для победы); однако строки кода, которые должны загружать фактическое состояние игры (ниже)

for (r = 0; r < num_rows; r++) {
  for (c = 0; c < num_columns; c++) {
    fscanf(fp, "%c ", aPtr[r][c]);
  }
    fscanf(fp, "\n");
  }

предупреждают меня, в частности, о строке

fscanf(fp, "%c ", aPtr[r][c]);

предупреждение

"предупреждение: формат указывает тип 'char *', но аргумент имеет тип 'int' [-Wformat]"

это сбивает с толку... потому что по сути это та же самая строка кода, которая используется в блоке сохранения

fprintf(fp, "%c ", aPtr[r][c]);

и это не доставляет мне никаких проблем... Есть какие-нибудь мысли о том, что именно происходит? Если вы хотите запустить его самостоятельно, просто закомментируйте тот код загрузки, который предназначен для извлечения фактического состояния игры. Полный код ниже:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>



void initialize(int num_rows, int num_cols, char **aPtr) {
    int i, r, c;


    for (r = 0; r < num_rows; r++) {
        for (c = 0; c < num_cols; c++) {
            aPtr[r][c] = '9';
        }
    }
}
// end of initialize


void printBoard(int num_rows, int num_columns, char **aPtr) {
    int row, col; 
  int r, c;

  for (r = 0; r < num_rows; r++) {
    for (c = 0; c < num_columns; c++) {
      printf("%c ", aPtr[r][c]);
    }
    printf("\n");
}

    printf("\n");
    puts("------ Connect *Four ------");
    puts("Connect X Command Line Game");

    // for fancy top of board frame
    printf("&&");
    for(col = 1; col < num_columns; col++) {
        printf("====");
    }
    printf("===");
    printf("&&\n");

    // for the rows/columns of the board
    for(row = num_rows - 1; row >= 0; row--){
        printf("|");
        for(col = 0; col < num_columns; col++){
            if(aPtr[row][col] == '0') {
                printf("| X ");
            }
            else if(aPtr[row][col] == '1') {
                printf("| O ");
            }
            else {
                printf("|   ");
            }      
        }
        puts("||");
    }

   // for fancy bottom of board frame
    printf("&&");
    for(col = 1; col < num_columns; col++) {
        printf("====");
    }
    printf("===");
    printf("&&\n");
    printf("  ");
    if (col < 100){
        for(col = 0; col < num_columns; col++) {
            if (col < 10) {
                printf(" %d  ", col + 1);
            }
            else {
                printf("%d  ", col + 1);
            }
        }
        puts("\n");
    }
}
// end of printBoard


/*Global var to hold the current winner value*/
char winnerVal = '0';

int checkFullBoard(int num_rows, int num_columns, char **aPtr) {
  for (int i = 0; i < num_columns; i++) {
    if (aPtr[num_rows - 1][i] == '9') {
      return 0;
    }
  }
  return 1;
}


/* 
 * Checks for the first avalible cell to insert token in given column.
 * NOTE: This fuction is designed based on row 0 being the bottom (lowest row of the board)
 * This means that tokens are inserted from row 0 upward. Based on your tests, this should not cause
 * any problems.
*/
int checkForColHeight(int num_rows, int num_columns, int column, char **aPtr) {
  for (int i = 0; i < num_rows; i++) {
    if (aPtr[i][column] == '9') {
      return i;
    }     
  }
  return -1;
}


int place_token(char player, int column, int num_rows, int num_columns, char **aPtr) {

  /*Check for invalid Parameters*/
  if(column > (num_columns - 1) || column < 0 || (player != '1' && player != '0') 
    || num_columns <= 0 || num_rows <= 0) {;
    return -1;
  } 

  int firstOpenRow = checkForColHeight(num_rows, num_columns, column, aPtr);
  if (firstOpenRow == -1) { 
    return -1;
  }else {
    aPtr[firstOpenRow][column] = player;
    return 1;
  }
}


char checkForSeries(int direction, int num_rows, int num_columns, int length_to_win, int r, int c, char **aPtr) {
  switch (direction) {
    /*Horizontal*/
    case 0:           
      for (int i = 1; i < length_to_win; i++) {
        if (aPtr[r][c] == '9' ) {
          return '2';
        }else if (aPtr[r][c] != aPtr[r][c + i] ) {
          return '2';
        }
      }
      return aPtr[r][c];
      break;      
    /*Vertical*/
    case 1: 
      for (int i = 1; i < length_to_win; i++) {
        if (aPtr[r][c] == '9' ) {
          return '2';
        }else if (aPtr[r][c] != aPtr[r + i][c] ) {
          return '2';
        }
      }
      return aPtr[r][c];     
      break;
    /*Left Diag*/
    case 2:
      for (int i = 1; i < length_to_win; i++) {
        if (aPtr[r][c] == '9' ) {
          return '2';
        }else if (aPtr[r][c] != aPtr[r + i][c - i] ) {
          return '2';
        }
      }
      return aPtr[r][c]; 
      break;
    /*Right Diag*/
    case 3:
      for (int i = 1; i < length_to_win; i++) {
        if (aPtr[r][c] == '9' ) {
          return '2';
        }else if (aPtr[r][c] != aPtr[r + i][c + i] ) {
          return '2';
        }
      }
      return aPtr[r][c]; 
      break;    
    return '2';
  }
  return '0';
}


/*Interate over each row and column. For each cell in the row, check for series of tokens*/
int checkHorizontal(int num_rows, int num_columns, int length_to_win, char **aPtr){  
  int r, c;
  for (r = 0; r < num_rows; r++) {    
    for(c = 0; c < num_columns - (length_to_win - 1); c++) {      
      char winner = checkForSeries(0, num_rows, num_columns, length_to_win, r, c, aPtr);
      if(winner != '2') {
        winnerVal = winner;       
        return 1;
      }     
    }
  }
  return 0;
}

/*Interate over each row and column. For each cell in the row, check for series of tokens*/
int checkVertical(int num_rows, int num_columns, int length_to_win, char **aPtr){  
  int r, c;
  for (c = 0; c < num_columns; c++) {   
    for(r = 0; r < num_rows - (length_to_win - 1); r++) {     
      char winner = checkForSeries(1, num_rows, num_columns, length_to_win, r, c, aPtr);
      if(winner != '2') {
        winnerVal = winner;       
        return 1;
      }     
    }
  }
  return 0;
}

/*Interate over each row and column. For each cell in the row, check for series of tokens*/
int checkDiagLeft(int num_rows, int num_columns, int length_to_win, char **aPtr){  
  int r, c;
  for (r = 0; r < num_rows - (length_to_win - 1); r++) {    
    for(c = num_columns - 1; c > (length_to_win - 2); c--) {      
      char winner = checkForSeries(2, num_rows, num_columns, length_to_win, r, c, aPtr);
      if(winner != '2') {
        winnerVal = winner;       
        return 1;
      }     
    }
  }
  return 0;
}

/*Interate over each row and column. For each cell in the row, check for series of tokens*/
int checkDiagRight(int num_rows, int num_columns, int length_to_win, char **aPtr){
  // printf("%s\n", "Check Right Diag: ");
  int r, c;
  for (r = 0; r < num_rows - (length_to_win - 1); r++) {
    // printf("%s", "Row #: ");
    // printf("%d\n", r);
    for(c = 0; c < num_columns - (length_to_win - 1); c++) {
      // printf("%s", "Col #: ");
      // printf("%d\n", c);
      char winner = checkForSeries(3, num_rows, num_columns, length_to_win, r, c, aPtr);
      if(winner != '2') {
        winnerVal = winner;
        // printf("%s", "Row Location: ");
        // printf("%d\n", r);
        // printf("%s", "Col Location: ");
        // printf("%d\n", c);
        return 1;
      }     
    }
  }
  return 0;
}


/*Return the integer representation of the winning player, -1 if a tie or error*/
char winner(int num_rows, int num_columns, int length_to_win, char **aPtr) {

  /*Check for invalid Parameters*/
  if (length_to_win <= 0 || length_to_win > num_columns || num_columns <= 0 || num_rows <= 0) {
    return '2';
  } 
  if (checkHorizontal(num_rows, num_columns, length_to_win, aPtr) 
    || checkVertical(num_rows, num_columns, length_to_win, aPtr)
    || checkDiagLeft(num_rows, num_columns, length_to_win, aPtr)
    || checkDiagRight(num_rows, num_columns, length_to_win, aPtr)
    ) {
    return winnerVal; 
  }
  if(checkFullBoard(num_rows, num_columns, aPtr)) {
    return '2';
  }
  return '2';
}


// *******************************************************************************************************
// *******************************************************************************************************

int main (int argc, char *argv[]) {

    setvbuf(stdout, NULL, _IONBF, 0);
    int num_rows = 7;
    int num_columns = 7;
    int length_to_win = 4;
    int i;
    int index;
    char **aPtr;
  // FILE *fp = fopen("test.txt", "r");

    printf("Starting Game\n");  



    // this loop checks for command line arguments and sets game variables accordingly.
    for(index = 0; index < argc; ++index) {

        if ( strncmp( argv[index], "-h", 5) == 0 ) {
            num_rows =atoi(argv[index + 1]);
        }
        if ( strncmp( argv[index], "-height", 5) == 0 ) {
            num_rows =atoi(argv[index + 1]);
        }
        if ( strncmp( argv[index], "-w", 5) == 0 ) {
            num_columns = atoi(argv[index + 1]);
        }
        if ( strncmp( argv[index], "-width", 5) == 0 ) {
            num_columns = atoi(argv[index + 1]);
        }
        if ( strncmp( argv[index], "-s", 5) == 0 ) {
            num_rows = atoi(argv[index + 1]);
            num_columns = atoi(argv[index + 1]);
        }
        if ( strncmp( argv[index], "-square", 5) == 0 ) {
            num_rows = atoi(argv[index + 1]);
            num_columns = atoi(argv[index + 1]);
        }
        if ( strncmp( argv[index], "-c", 5) == 0 ) {
            length_to_win = atoi(argv[index + 1]);
        }
        if ( strncmp( argv[index], "-connect", 5) == 0 ) {
            length_to_win = atoi(argv[index + 1]);
        }
    }


    // these conditionals check for valid board size
    if (num_rows <= 0 || num_columns <= 0 ){
        printf("%s\n","You entered a width or length that was invalid." );
    }
    if (length_to_win <= 0 || length_to_win > (num_rows - 1)) {
        printf("%s\n","You entered a winning length that was invalid." );
    }



    // create the space for the board
    aPtr = malloc(num_rows * sizeof(char*));

    for (i = 0; i < num_rows; i++){
        aPtr[i] = malloc(num_columns * sizeof (char));
    }

    initialize(num_rows, num_columns, aPtr);
  int answer;
  int attmpt;
  char player = '0';

  printf("%s\n", "*********************");
  printf("%s\n", "   Starting Board   ");
  printf("%s\n", "*********************");
  puts("\n");
  printBoard(num_rows, num_columns, aPtr);
  printf("Player: %cs Turn\n", player + 1); 
  puts("\n");



  /*Start game loop*/
  while(1) {       

      // prompts the user to select which column they want their piece to be placed
      // -1 on the temp because the first column is technically 0 so if a player
      // wants to place their piece in column "1", it'll be placed at index[0] accordingly
      printf("%s\n", "Enter Column # To Place Token"); 
      int column;
      char temp[20];
      char temp2[20];       
      scanf("%s", temp); 

      if (strncmp (temp, "save", 5) == 0){

          int r, c;

          // this writes the game settings to a file
          int *rows = &num_rows;
          int *cols = &num_columns;
          int *len = &length_to_win;
          FILE *fp = fopen("gameSave.txt", "w+");
          fprintf(fp, "%d ", *rows);
          fprintf(fp, "%d ", *cols);
          fprintf(fp, "%d ", *len);
          fprintf(fp, "\n\n");

          for (r = 0; r < num_rows; r++) {
            for (c = 0; c < num_columns; c++) {
              fprintf(fp, "%c ", aPtr[r][c]);
              }
            fprintf(fp, "\n");
          }

          printf("Game Saved\n");
          fclose(fp);

      }

      if (strncmp (temp, "load", 5) == 0){

        int r, c;

        // this loads the game settings into game
        FILE *fp = fopen("gameSave.txt", "r");
        fscanf(fp, "%d %d %d", &num_rows, &num_columns, &length_to_win);
        fscanf(fp, "\n\n");

        aPtr = malloc(num_rows * sizeof(char*));

        for (i = 0; i < num_rows; i++){
            aPtr[i] = malloc(num_columns * sizeof (char));
        }

        for (r = 0; r < num_rows; r++) {
          for (c = 0; c < num_columns; c++) {
            fscanf(fp, "%c ", aPtr[r][c]);
          }
            fscanf(fp, "\n");
          }

        printf("Game Loaded\n");
        fclose(fp);
      }


      column = atoi(temp) - 1;
      attmpt = place_token(player, column, num_rows, num_columns, aPtr);

      if ((column < 0 || column > (num_columns - 1)) && (strncmp (temp, "save", 5) != 0) && (strncmp (temp, "load", 5) != 0)) {
          printf("%s\n","You entered a column that was invalid. Please try again." );
          continue;
      }

      if (attmpt != 1 && (strncmp (temp, "save", 5) != 0) && (strncmp (temp, "load", 5) != 0)) {
          printf("%s\n","This row is already full. Please try again." );
          continue;
      }

      printf("%s\n", "************************");
      printf("%s\n", "      Board Updated     ");
      printf("%s\n", "************************");  
      puts("\n");  
      printBoard(num_rows, num_columns, aPtr);
      puts("\n");



      if ((strncmp (temp, "save", 5) != 0) && (strncmp (temp, "load", 5) != 0)) {
          if (checkFullBoard(num_rows, num_columns, aPtr)) {
              printf("%s\n","This game is a tie. Thanks for Playing.\n");
              return 0;
            }
      }

      // this if-statement will constantly be run while the game progresses, 
      // meaning that winner will be called at every turn and 
      // all of the win conditions will be checked until a winner is found
      char isWin = winner(num_rows, num_columns, length_to_win, aPtr);
      if(isWin != '2') {
          printf("Player: %c is the winner! Thanks for Playing.\n", isWin + 1);
          printf("Play again? (enter 'y' to continue)\n");
          scanf("%s", temp2);

          if (strncmp (temp2, "y", 5) == 0){
            initialize(num_rows, num_columns, aPtr);
            printBoard(num_rows, num_columns, aPtr);
            puts("\n");
          }
          else {
            printf("Game over, goodbye!\n");
            return 0;
          }
      }

      // if a winner is not found then this if/else will continue to switch
      // between players at the end of each turn
      if ((strncmp (temp, "save", 5) != 0) && (strncmp (temp, "load", 5) != 0) && (strncmp (temp2, "y", 5) != 0)) {
          if (player == '1') {
            player = '0';
        }
          else {
              player = '1';
          }
      }
      memset(temp, 0, sizeof temp);
      memset(temp2, 0, sizeof temp2);

      printf("Player: %cs Turn\n", player +1);
  } // end of while loop


    return 0;
}

person Pig_Mug    schedule 06.03.2017    source источник
comment
Замените fscanf(fp, "%c ", aPtr[r][c]); на fscanf(fp, " %c", &aPtr[r][c]); -- поставьте пробел перед %c, и вам нужно указать указатель на fscanf().   -  person Barmar    schedule 06.03.2017
comment
@ Бармар, ааа, понятно. Спасибо за находку.   -  person Pig_Mug    schedule 06.03.2017


Ответы (1)


fscanf(fp, "%c ", aPtr[r][c]);

должно быть:

fscanf(fp, " %c", &aPtr[r][c]);

Поместите пробел перед %c, чтобы пропустить новую строку и любые другие пробелы перед чтением символа. И вы должны указать указатель на элемент массива для сохранения.

person Barmar    schedule 06.03.2017