Why calling function second time is increasing column count by 5 times?

Viewed 90

I am new to C coding and don't know much about dynamic allocation.I was working on an assignment.I am developing snake game in c.Below is the implementation of my code:

typedef struct snake_t {   
    unsigned int tail_row;
    unsigned int tail_col;
    unsigned int head_row;
    unsigned int head_col;            
    bool live;
} snake_t;
                
typedef struct game_state_t {
    unsigned int num_rows;
    char** board;
    unsigned int num_snakes;
    snake_t* snakes;
} game_state_t;
                
game_state_t* create_default_state() {
    // TODO: Implement this function.
    game_state_t *defaultState = (game_state_t*)malloc(sizeof(game_state_t));
    defaultState->num_rows = 18;
    defaultState->board = malloc(18 * sizeof(char *));
    
    for (int i = 0; i < 18; i++) {
        defaultState->board[i] = malloc(20 * sizeof(char));
    }
                  
    for (int i = 0; i < 18; i++) {
        for (int j = 0; j < 20; j++) {
            if (i == 0 || j == 0 || i == 18 - 1 || j == 20 - 1) {
                defaultState->board[i][j] = '#';
            }
            else {
                defaultState->board[i][j]=' ';
            }
        }
    }
                  
    defaultState->num_snakes = 1;
    defaultState->snakes = (snake_t*)malloc(sizeof(snake_t));
    defaultState->board[2][9] = '*';
    defaultState->snakes->tail_row = 2 ;
    defaultState->snakes->tail_col = 2 ;
    defaultState->snakes->head_row = 2 ;
    defaultState->snakes->head_col = 4 ;
    defaultState->board[2][2] = 'd';
    defaultState->board[2][3] = '>';
    defaultState->board[2][4] = 'D';
    defaultState->snakes->live = true;

    return defaultState;
}
                
game_state_t* actual_state = create_default_state();
game_state_t* expected_state = create_default_state();

actual_state is having correct number of rows and columns i.e. 18 and 20 respectively, but expected_state increase column by 5.When i print strlen of actual_state->board[0], it gives 20 but when i print strlen of expected_state->board[0] gives 25. Also when i tried to debug through gdb call function print on expected_state->board[0] it said repeated " 0x00577784 <repeated 20 times> , ' #\n#!' ". I cannot spot the bug. Why calling function second time increases column size by 5?

1 Answers

As noted, you probably are experiencing undefined behavior in your string lengths due to the fact that there may or may not be a null terminator at the end of the "board" character arrays.

When I copied your code into my Linux virtual machine and placed the creation of the "actual" and "expected" game state structures into the "main" function, I had no discrepancy between the size of the "board" character arrays between the two structures. That might be just due to my environment, and "getting lucky" with the string lengths (which could be one of the outcomes from undefined behavior).

With that, I added in some character array initialization to your function to ensure that the character arrays have a null terminator ('\0') at the end of the character array. Following is a copy of your code with those tweaks.

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

typedef struct snake_t
{
    unsigned int tail_row;
    unsigned int tail_col;
    unsigned int head_row;
    unsigned int head_col;

    bool live;
} snake_t;


typedef struct game_state_t
{
    unsigned int num_rows;
    char** board;

    unsigned int num_snakes;
    snake_t* snakes;
} game_state_t;

game_state_t* create_default_state()
{
    // TODO: Implement this function.
    game_state_t *defaultState = (game_state_t*)malloc(sizeof(game_state_t));
    defaultState->num_rows = 18;
    defaultState->board = malloc(18 * sizeof(char *));
    for(int i = 0; i < 18; i++)
    {
        defaultState->board[i] = malloc(21 * sizeof(char));
        /* Added this block of code to initialize the board array so that it will behave like a string */
        for (int j = 0; j < 20; j++)
        {
            defaultState->board[i][j] = 0;
        }
        /* End of additional code */
    }
    for (int i = 0; i < 18; i++)
    {
        for (int j = 0; j < 20; j++)
        {
            if (i == 0 || j == 0 || i == 18 - 1 || j == 20 - 1)
            {
                defaultState->board[i][j]='#';
            }
            else
            {
                defaultState->board[i][j]=' ';
            }
        }
    }
    defaultState->num_snakes=1;
    defaultState->snakes = (snake_t*)malloc(sizeof(snake_t));
    defaultState->board[2][9]='*';
    defaultState->snakes->tail_row =2 ;
    defaultState->snakes->tail_col=2 ;
    defaultState->snakes->head_row =2 ;
    defaultState->snakes->head_col=4 ;
    defaultState->board[2][2]='d';
    defaultState->board[2][3]='>';
    defaultState->board[2][4]='D';
    defaultState->snakes->live=true;
    return defaultState;
}


int main()
{
    /* Added the actual and expected state creation statements into the "main" function for testing */
    game_state_t* actual_state      = create_default_state();
    game_state_t* expected_state    = create_default_state();

    printf("Size actual_state: %d\n", (int)strlen(actual_state->board[0]));
    printf("Size expected_state: %d\n\n", (int)strlen(expected_state->board[0]));

    for (int i = 0; i < 18; i++)
    {
        printf("%s\n", actual_state->board[i]);
    }

    printf("\n");

    for (int i = 0; i < 18; i++)
    {
        printf("%s\n", expected_state->board[i]);
    }

    printf("\n");

    return 0;
}

Adding in some "printf" statements to see what each "board" would look like I ran the program which produced the following terminal output.

@Una:~/C_Programs/Console/T_Snake/bin/Release$ ./T_Snake 
Size actual_state: 20
Size expected_state: 20

####################
#                  #
# d>D    *         #
#                  #
#                  #
#                  #
#                  #
#                  #
#                  #
#                  #
#                  #
#                  #
#                  #
#                  #
#                  #
#                  #
#                  #
####################

####################
#                  #
# d>D    *         #
#                  #
#                  #
#                  #
#                  #
#                  #
#                  #
#                  #
#                  #
#                  #
#                  #
#                  #
#                  #
#                  #
#                  #
####################

Both "boards" are alike. So, the main take-away from this is if you are utilizing character arrays like strings, be sure that they are properly sized to contain a terminator character at the end.

Give that a try.

Related