Search code examples
csegmentation-faultfunction-calls

Standalone Code-Block Works, But Function Call W/ Same Code Does Not (C Program)


I have a connect 4 game that I am making. Everything works, and when you load the game on startup with

"./a.out -l"

if this chunk of code below is in the conditional that checks for that flag

        int r, c, i;

        // this loads the game settings into game
        FILE *fp = fopen("gameSave.txt", "r");
        fscanf(fp, "%d %d %d %c", &num_rows, &num_columns, &length_to_win, &player);
        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]);
                printf("%c ", aPtr[r][c]);
            }
            fscanf(fp, "\n");
            printf("\n");
        }

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

it works just fine and after this conditional I call the function printBoard() and I get the following output

Starting Game
0 1 0 9 9 9 9 
0 1 9 9 9 9 9 
0 1 9 9 9 9 9 
9 9 9 9 9 9 9 
9 9 9 9 9 9 9 
9 9 9 9 9 9 9 
9 9 9 9 9 9 9 
Game Loaded
*********************
   Starting Board   
*********************



------ Connect *Four ------
Connect X Command Line Game
&&===========================&&
||   |   |   |   |   |   |   ||
||   |   |   |   |   |   |   ||
||   |   |   |   |   |   |   ||
||   |   |   |   |   |   |   ||
|| X | O |   |   |   |   |   ||
|| X | O |   |   |   |   |   ||
|| X | O | X |   |   |   |   ||
&&===========================&&
   1   2   3   4   5   6   7  

Player: 2s Turn


Enter Column # To Place Token

I am then able to play the game as expected and everything works. However, if I put that same chunk of code into a function like the one below

void loadGame(int num_rows, int num_columns, int length_to_win, char player, char **aPtr){

        int r, c, i;

        // this loads the game settings into game
        FILE *fp = fopen("gameSave.txt", "r");
        fscanf(fp, "%d %d %d %c", &num_rows, &num_columns, &length_to_win, &player);
        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]);
                printf("%c ", aPtr[r][c]);
            }
            fscanf(fp, "\n");
            printf("\n");
        }

        printf("Game Loaded\n");
        printBoard(num_rows, num_columns, aPtr);
        fclose(fp);

}

and then comment out that same code in the conditional leaving only the following

        load = true;

        loadGame(num_rows, num_columns, length_to_win, player, aPtr);

it gives me this output

Starting Game
0 1 0 9 9 9 9 
0 1 9 9 9 9 9 
0 1 9 9 9 9 9 
9 9 9 9 9 9 9 
9 9 9 9 9 9 9 
9 9 9 9 9 9 9 
9 9 9 9 9 9 9 
Game Loaded
*********************
   Starting Board   
*********************



------ Connect *Four ------
Connect X Command Line Game
&&===========================&&
||   |   |   |   |   |   |   ||
|Segmentation fault: 11

and I'm not exactly sure why... this means that in the for-loop where the visualization of the board is created there is some kind of problem... could it be a scope issue perhaps (maybe with number of rows or columns)? the function that creates the board looks like this

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

    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 the board is less than 100 columns,
    // print the column number (for readability)
    if (col < 100){
        for(col = 0; col < num_columns; col++) {
            if (col < 10) {
                printf(" %d  ", col + 1);
            }
            else {
                printf("%d  ", col + 1);
            }
        }
        puts("\n");
    }
}

Any advice as to why this is happening would be much appreciated, if you want to run the code for yourself, all the code is below

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



// This is where the default board gets created
// The reasoning behind setting all values = '9'
// is that when a player places their piece on the board
// the location of that piece will then become their
// player number, which is used accordingly when
// saving and loading the game
void initialize(int num_rows, int num_cols, char **aPtr) {
    int r, c;

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



// this is where the board gets printed according to its
// state (whether default-starting, game in progress, or
// loaded game). It looks at the values of the 2D array
// and depending on whether or not they are default 9's or
// player numbers, prints out the appropriate illustration
void printBoard(int num_rows, int num_columns, char **aPtr) {
    int row, col; 

    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 the board is less than 100 columns,
    // print the column number (for readability)
    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



// This is a global variable used to signify the state of
// whether or not there is a winner
char winnerVal = '0';


// this checks if the board is full or not to check for a win or tie
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;
}


// this checks for the first available location within a column for players
// to place their pieces, if there is no available space in a column, it returns
// -1 which signals the player that they must adjust their placement
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;
}


// this is where a token is placed at an appropriate position if it is within the 
// allowances of the game
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;
        }
}


// this is where a check for a win occurs. Essentially until it finds a line long enough to win it'll return negative
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';
}
// end of checkForSeries


// checks for any horizontal wins
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;
}

// checks for any vertical wins
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;
}

// checks for any left-diagonal wins
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;
}

// checks for any right-diagonal wins
int checkDiagRight(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 = 0; c < num_columns - (length_to_win - 1); c++) {
            char winner = checkForSeries(3, num_rows, num_columns, length_to_win, r, c, aPtr);
            if(winner != '2') {
                winnerVal = winner;
                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) {
    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';
}


char **loadGame(int num_rows, int num_columns, int length_to_win, char player, char **aPtr){

            int r, c, i;

            // this loads the game settings into game
            FILE *fp = fopen("gameSave.txt", "r");
            fscanf(fp, "%d %d %d %c", &num_rows, &num_columns, &length_to_win, &player);
            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]);
                    printf("%c ", aPtr[r][c]);
                }
                fscanf(fp, "\n");
                printf("\n");
            }

            return aPtr;
            printf("Game Loaded\n");
            fclose(fp);

}


// END OF FUNCTIONS
// *******************************************************************************************************
// *******************************************************************************************************
// START OF MAIN METHOD


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, index, attmpt;
    char **aPtr;
    char player = '0';
    bool load = false;

    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]);
        }
        if ( strncmp( argv[index], "-l", 5) == 0 ) {

            load = true;

             aPtr = loadGame(num_rows, num_columns, length_to_win, player, aPtr);
            // int r, c;

            // this loads the game settings into game
            // FILE *fp = fopen("gameSave.txt", "r");
            // fscanf(fp, "%d %d %d %c", &num_rows, &num_columns, &length_to_win, &player);
            // 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]);
            //         printf("%c ", aPtr[r][c]);
            //     }
            //     fscanf(fp, "\n");
            //     printf("\n");
            // }

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



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


    // if the game isn't loaded upon execution, the load it normally
    if (load == false) {
        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);
    }


    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");



    // this is the loop that runs while the game is in progress 
    // it continually checks for winners, full boards, valid moves, etc. 
    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 called "gameSave.txt"
            // if the file doesn't exist it just creates it
            int *rows = &num_rows;
            int *cols = &num_columns;
            int *len = &length_to_win;
            char *play = &player;
            FILE *fp = fopen("gameSave.txt", "w+");
            fprintf(fp, "%d ", *rows);
            fprintf(fp, "%d ", *cols);
            fprintf(fp, "%d ", *len);
            fprintf(fp, "%c ", *play);
            fprintf(fp, "\n\n");

            // this loop saves the actual board state to the file
            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 %c", &num_rows, &num_columns, &length_to_win, &player);
            fscanf(fp, "\n\n");

            // this creates space for the loaded game
            aPtr = malloc(num_rows * sizeof(char*));
            for (i = 0; i < num_rows; i++){
                aPtr[i] = malloc(num_columns * sizeof (char));
            }

            // this fills the board with the saved game-data
            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");
            printf("Or load a previously saved game with 'load'\n");
            scanf("%s", temp2);

            // if the player wants to play again, clear the board for a new game
            // and start over
            if (strncmp (temp2, "y", 5) == 0){
                initialize(num_rows, num_columns, aPtr);
                printBoard(num_rows, num_columns, aPtr);
                puts("\n");
            }
            if (strncmp (temp2, "load", 5) == 0) {
                int r, c;

                // this loads the game settings into game
                FILE *fp = fopen("gameSave.txt", "r");
                fscanf(fp, "%d %d %d %c", &num_rows, &num_columns, &length_to_win, &player);
                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]);
                        printf("%c ", aPtr[r][c]);
                    }
                    fscanf(fp, "\n");
                    printf("\n");
                }

                printf("Game Loaded\n");
                puts("\n");
                printBoard(num_rows, num_columns, aPtr);
                fclose(fp);
            }

            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;
}

Solution

  • First, your question is a little bit clumsy, check out the guide @Michael provided in the comments.

    Now, from what I've understood, the problem is that you're passing the value of the variables when calling the function loadGame(...) instead of a reference to them. The variables num_rows, num_columns, length_to_win and the others are not being modified by the function.

    Check out this example:

    void wrong (int a, int b, int c) {
    
        a = 1;
        b = 2;
        c = 3;
    
    }
    
    void correct (int *a, int *b, int *c) {
    
        *a = 1;
        *b = 2;
        *c = 3;
    
    }
    
    int main(int argc, const char * argv[]) {
    
        int a, b, c;
    
        a = b = c = -1;
    
        /* 
         Not modified after function call.
         */
    
        wrong(a, b, c);
    
        printf("wrong a: %i b: %i c: %i\n", a, b, c);
    
        /*
         Modified after function call.
         */
    
        correct(&a, &b, &c);
    
        printf("correct a: %i b: %i c: %i\n", a, b, c);
    
        return 0;
    }