im trying to make a sort of minesweeper clone in c, but i cant figure out how to create a variable length array member of a struct and initialize it.
my struct definitions
typedef struct Cell
{
Rectangle area; // The area the cell covers
bool hasMine; // If the cell covers a mine
bool covered; // If the cell is currently covered
int touching; // How many mines the cell is touching
} Cell;
typedef struct Minefield
{
int columns; // The amount of columns
int rows; // The amount of rows
int mines; // The amount of mines
Cell field[]; // An array of columns*rows Cells
} Minefield;
how im creating new instances of a Minefield
Minefield NewGame(int columns, int rows, int mines)
{
int i = 0;
Cell cells[columns*rows];
for (i=0; i < columns*rows; i++) {
int x = 0;
int y = 0;
if (i == 0) {
Rectangle area = (Rectangle){0, 0, cellsize, cellsize};
cells[i] = (Cell){area, false, true, 0};
}
else {
Cell previous = cells[i-1];
x = previous.area.x + cellsize;
y = previous.area.y + cellsize;
Rectangle area = (Rectangle){x, y, cellsize, cellsize};
cells[i] = (Cell){area, false, true, 0};
}
}
Minefield field = (Minefield){columns, rows, mines, cells};
return field;
}
im new to c, ive done some googling and ive come across some very similar questions, but i cant really understand them or the examples they give. i think you can do what my errendous code is trying to do (or something very similar) with a gcc extension, but i want my code to be portable. how can i have a struct have an unknown size array member?
Yes, it's called flexible array member
. It's usually used with dynamically allocated objects. However, you cannot return Minefield
by value. The compiler will copy only sizeof(Minefield)
data what is the size of object if length of array Minefield::field
was 0
.
You can do:
Minefield* NewGame(int columns, int rows, int mines)
{
Cell cells[columns*rows];
...
// allocate memory for Minefield and cells array just after it
Minefield* minefield = malloc(sizeof(*minefield) + sizeof cells);
if (!minefield) { ... error handling ... }
// set normal fields using compund literal
*minefield = (Minefield){columns, rows, mines};
// copy cells `array` to `minefield->field` array
memcpy(minefield->field, cells, sizeof cells);
return minefield;
}
The memcpy
can be avoided by first allocating Minefield
and writing directly to field
member.
Minefield* NewGame(int columns, int rows, int mines)
{
Minefield* minefield = malloc(sizeof(Minefield) + rows * columns * sizeof(Cell));
if (!minefield) { ... error handling ... }
*minefield = (Minefield){columns, rows, mines};
Cell *cells = minefield->field;
...
return minefield;
}