1

Trying to make a tic-tac-toe game and having trouble with the grid, I'm new to C and have looked everywhere but nothing seems to work.

int main(void) {
    char grid[GRID_HEIGHT][GRID_WIDTH];
    grid = make_grid((char **)grid);
    print_grid((char **)grid);
}

char ** make_grid(char **grid) {
    char grid[GRID_HEIGHT][GRID_WIDTH] = {
        { '\n', ' ', '1', ' ', '|', ' ', '2', ' ', '|', ' ', '3', ' ' } ,
        { '\n', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-' } ,
        { '\n', ' ', '4', ' ', '|', ' ', '5', ' ', '|', ' ', '6', ' ' } ,
        { '\n', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-' } ,
        { '\n', ' ', '7', ' ', '|', ' ', '8', ' ', '|', ' ', '9', ' ' }
    };
    return grid;
}

void print_grid(char **grid) {
    for (int row = 0; row < GRID_HEIGHT; row++) {
        for (int column = 0; column < GRID_WIDTH; column++) {
            printf("%c", grid[row][column]);
        }
    }
    printf("\n");
}

How would I parse the grid into a function and also return it without the program crashing?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
aLongBoat
  • 59
  • 9

2 Answers2

1

You already declared the array grid in main.

char grid[GRID_HEIGHT][GRID_WIDTH];

So for example this function definition

char ** make_grid(char **grid) {
    char grid[GRID_HEIGHT][GRID_WIDTH] = {
        { '\n', ' ', '1', ' ', '|', ' ', '2', ' ', '|', ' ', '3', ' ' } ,
        { '\n', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-' } ,
        { '\n', ' ', '4', ' ', '|', ' ', '5', ' ', '|', ' ', '6', ' ' } ,
        { '\n', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-' } ,
        { '\n', ' ', '7', ' ', '|', ' ', '8', ' ', '|', ' ', '9', ' ' }
    };
    return grid;
}

does not make sense at least because the parameter is redeclared in the outer block scope of the function.

The function could look the following way

void make_grid( char ( *grid )[GRID_WIDTH] )
{
    static const char *rows[] = 
    {
        { "\n 1 | 2 | 3 " } ,
        { "\n-----------" } ,
        { "\n 4 | 5 | 6 " } ,
        { "\n-----------" } ,
        { "\n 7 | 8 | 9 " }
    };
    const size_t N = sizeof( rows ) / sizeof( *rows );

    for ( size_t i = 0; i < N; i++ )
    {
        strncpy( grid[i], rows[i], GRID_WIDTH );
    }
}

and called like

make_grid( grid );

The function print_grid can be defined like

void print_grid( char ( *grid )[GRID_WIDTH], size_t height ) 
{
    for ( size_t row = 0; row < height; row++) {
        for ( size_t column = 0; column < GRID_WIDTH; column++) {
            printf("%c", grid[row][column]);
        }
        putchar( '\n' );
    }
    putchar( '\n' );
}

and called like

print_grid( grid, GRID_HEIGHT );
Federico klez Culloca
  • 26,308
  • 17
  • 56
  • 95
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Do not use `strncpy`, it is a dangerous function that causes bugs such as `strncpy( grid[i], rows[i], GRID_WIDTH );`. The resulting arrays are not null terminated because `GRID_WIDTH` doesn't contain space for the null terminator, as the code is written. Use `memcpy` instead. – Lundin Dec 03 '19 at 11:45
  • @Lundin It is not a bug. It is a design solution when the character array does not contain the terminating zero. Why did you decide that the element of grid shall be zero=terminated?! – Vlad from Moscow Dec 03 '19 at 11:48
  • Because `strncpy` is just the lazy version of `memcpy`. You know the size, you know that the strings aren't null terminated, so there's no point to call a function slowly searching for the null termination that isn't there. – Lundin Dec 03 '19 at 12:05
  • @Lundin I think that in this case it is better to use strncpy instead of memcpy because there is nothing can be said about the initializers. – Vlad from Moscow Dec 03 '19 at 12:21
0

Fundamental problems:

  • You need to #include relevant headers.
  • You need to declare functions before calling them.
  • You cannot return arrays from functions in C. You can return pointers, but that might be the wrong solution. If you wish to copy an array, you must use memcpy.
  • You cannot have two variables with the same name grid in the same scope.
  • Pointer-to-pointers cannot point at arrays and they are not compatible with arrays. So you cannot use pointer-to-pointer here.

Fixed code:

#define GRID_HEIGHT 5
#define GRID_WIDTH 12

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

void make_grid (char grid[GRID_HEIGHT][GRID_WIDTH]);
void print_grid (char grid[GRID_HEIGHT][GRID_WIDTH]);

int main(void) {
    char grid[GRID_HEIGHT][GRID_WIDTH];
    make_grid(grid);
    print_grid(grid);
}

void make_grid (char grid[GRID_HEIGHT][GRID_WIDTH]) {
    const char GRID [GRID_HEIGHT][GRID_WIDTH] = {
        { '\n', ' ', '1', ' ', '|', ' ', '2', ' ', '|', ' ', '3', ' ' } ,
        { '\n', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-' } ,
        { '\n', ' ', '4', ' ', '|', ' ', '5', ' ', '|', ' ', '6', ' ' } ,
        { '\n', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-' } ,
        { '\n', ' ', '7', ' ', '|', ' ', '8', ' ', '|', ' ', '9', ' ' }
    };
    memcpy( grid, GRID, sizeof(char[GRID_HEIGHT][GRID_WIDTH]) );
}

void print_grid (char grid[GRID_HEIGHT][GRID_WIDTH]) {
    for (int row = 0; row < GRID_HEIGHT; row++) {
        for (int column = 0; column < GRID_WIDTH; column++) {
            printf("%c", grid[row][column]);
        }
    }
    printf("\n");
}

In this example, the parameter grid in each function is an array, which "decays" into a pointer to the first element. The first element of a 2D array is a 1D array, so you end up with an array pointer. If you don't want the compiler to "decay" the array you could write the array pointer syntax manually: char (*grid)[GRID_WIDTH], but it is harder to read so I wouldn't recommend it.

Lundin
  • 195,001
  • 40
  • 254
  • 396