-1

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?

Chris
  • 26,361
  • 5
  • 21
  • 42
hzaheer48
  • 1
  • 1
  • Please provide a [mre]. – kaylum Sep 09 '22 at 21:40
  • 3
    "*When i print strlen of actual_state->board[0]*". The `board` entries are not strings. Strings in C need to be NUL terminated and it looks like your entries are not. That's also why we need to see your complete code as a [mre] including the exact test/debug code that you are drawing conclusions from. – kaylum Sep 09 '22 at 21:43
  • Key here: *minimal*. – tadman Sep 09 '22 at 22:12
  • 1
    Note: You have a very peculiar habit of adding a space before the `;` at the end of the line. While the compiler doesn't care, this makes your code stand out for no reason and can draw attention away from actual problems. Remember, C programmers are **highly** sensitive to even the tiniest things out of the ordinary, as this language does not protect you from little mistakes such as using `=` instead of `==`. – tadman Sep 09 '22 at 22:14

2 Answers2

1

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.

NoDakker
  • 3,390
  • 1
  • 10
  • 11
  • 1
    Good catch. Thought I had revised the malloc size to 21. I need another cup of coffee. – NoDakker Sep 10 '22 at 00:16
  • "Grind coffee or grind to a halt..." `:-)` Another alternative would be to introduce OP to "format specifier" for `printf( )`, of course... `printf( "%.*s", 20, buffer );` No need for extra byte on each string, then... All good. `:-)` – Fe2O3 Sep 10 '22 at 00:27
  • Thank you for your effort. But this is still not working for me. Maybe it's environment issue? – hzaheer48 Sep 10 '22 at 07:18
  • [Death to Magic Numbers!](https://stackoverflow.com/questions/47882/what-is-a-magic-number-and-why-is-it-bad) Death I say! – user4581301 Sep 10 '22 at 22:10
0

I have your answer, although it might be a bit too late!

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]=' ';
        }
    }
}

So you have the above code section in your code. All you need to do to fix it is add defaultState->board[i][20] = '\0'; to the block like below:

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->board[i][20] = '\0';
}

This essentially follows from what was said above. Faced the same issue and this fixed it for me. This is a short answer please reply if you would like more explanation