我正在尝试实现一个单元格网格,类似于康威的生命游戏。
虽然每个单独的网格在两个维度上都应该有固定的大小,但我想要一个允许在两个维度上任何大小的网格结构。
这类似于数组可以是任何大小的,但是一旦初始化的数组具有固定的大小。
这是我到目前为止所拥有的:
typedef struct Cell {
int data;
// stuff to be added later
} Cell;
typedef struct Grid {
unsigned width;
unsigned height;
Cell cell[][];
} Grid;
Grid initGrid(unsigned width, unsigned height) {
Grid g;
g.width = width;
g.height = height;
g.cell = malloc( sizeof(Cell)*width*height );
return g;
}
但是我收到以下编译时错误:
main.c|12|note: declaration of `‘cell’ as multidimensional array must have bounds for all dimensions except the first|
如何定义具有灵活大小的
Grid
数据类型?
后记:作为 C 新手,我认为以下内容会起作用:
typedef struct Grid {
unsigned width;
unsigned height;
Cell cell[width][height];
} Grid;
后记:每当我使用malloc
时,我总是感到不安。我在这里做(或试图做(任何可怕的错误吗?
C 中的双索引 ( cell[x][y]
( 来做到这一点,没有办法表达每行要跳转的字节数是动态的。
因此,最好的(在我看来(方法是使用一维数组手动进行索引。
放一个普通:
Cell *cell;
在struct
(保持width
和height
(,然后像这样索引:
set_cell(Grid *g, unsigned int x, unsigned int y, Cell value)
{
g->cell[y * g->width + x] = value;
}
编译器不太可能内联它,而且它会非常紧凑。可能比锯齿数组更快"方法,该方法使用更多的内存和另一层间接寻址。
分配很简单:
Grid initGrid(unsigned int width, unsigned int height)
{
Grid g;
g.width = width;
g.height = height;
g.cell = malloc(width * height * sizeof *g.cell);
// add assert or error here, can't return NULL for value type
return g;
}
如果你也想堆分配Grid
,你可以把它和它的元素共同分配。
是的,您需要在完成分配后free()
分配,以免泄漏内存。严格来说,在现代系统上,操作系统将在程序结束时释放所有资源,但无论如何释放都是很好的形式:
void destroyGrid(Grid g)
{
free(g.cell);
}
跟随这篇优秀的帖子:如何在 C 中使用动态多维数组?阅读@JensGustedt帖子并关注他的链接可变长度数组 (VLA(
实际上有一种方法 - 我关注了他的帖子并编写了一个小型测试程序来验证:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char ** argv)
{
unsigned int height = 100;
unsigned int width = 10;
int (*array)[width] = malloc (sizeof(int[height][width]));
array[90][2] = 1231;
printf("%d", array[90][2]);
}
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char ** argv)
{
unsigned int height;
unsigned int width;
int i,j;
printf("enter width: ");
scanf("%d", &width);
printf("enter height: ");
scanf("%d", &height);
int (*array)[width] = malloc (sizeof(int[height][width]));
for (i = 0; i < height; i++ )
for (j = 0; j < width; j++ )
array[i][j] = i;
for (i = 0; i < height; i++ ) {
for (j = 0; j < width; j++ )
printf("%d ", array[i][j]);
printf("n");
}
}
和控制台:
enter width: 10
enter height: 6
0 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2 2
3 3 3 3 3 3 3 3 3 3
4 4 4 4 4 4 4 4 4 4
5 5 5 5 5 5 5 5 5 5
我承认这令人惊讶 - 我不知道这存在......
编辑 - 使用结构:
#include <stdio.h>
#include <stdlib.h>
typedef struct Cell {
int data;
// stuff to be added later
} Cell;
typedef struct Grid {
unsigned width;
unsigned height;
Cell *cell;
} Grid;
Grid initGrid(unsigned width, unsigned height) {
Grid g;
g.width = width;
g.height = height;
g.cell = malloc( sizeof(Cell[height][width]) );
return g;
}
int main(int argc, char ** argv)
{
unsigned int height;
unsigned int width;
int i,j;
Grid test;
printf("enter width: ");
scanf("%d", &width);
printf("enter height: ");
scanf("%d", &height);
test = initGrid (width, height);
Cell (*array)[width] = test.cell;
for (i = 0; i < height; i++ )
for (j = 0; j < width; j++ )
array[i][j].data = i;
for (i = 0; i < height; i++ ) {
for (j = 0; j < width; j++ )
printf("%d ", array[i][j].data);
printf("n");
}
}
控制台输出:
enter width: 20
enter height: 10
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
有一个铸造警告,我没有时间解决,但可以实施这个想法 - 干净利落地做......再次,这是一个POC而不是一个实际的程序
你在这里几乎不走运,因为在 C 中没有办法在struct
定义中使用可变数组长度。你可以做的是:
typedef struct Grid {
unsigned width, height;
void* cell_internal; //Type: Cell(*)[height]
} Grid;
#define cell(myGrid) ((Cell(*)[(myGrid).height])(myGrid).cell_internal)
//in the constructor of Grid
newGrid->width = ...;
newGrid->height = ...;
cell(*newGrid) = malloc(newGrid->width*sizeof(*cell(*newGrid)));
for(unsigned x = 0; x < newGrid->width; x++) {
for(unsigned y = 0; y < newGrid->height; y++) {
cell(*newGrid)[x][y] = ...;
}
}
这是一个肮脏的小技巧,但它应该可以正常工作。很酷的部分是,您可以简单地用cell(aGrid)[x][y]
解决网格单元格。缺点是,它确实非常彻底地掩盖了实际发生的事情。而且没有多少人能真正读懂cell()
宏的作用。(提示:它只是将void*
强制转换为指向列数组Cell(*)[myGrid.height]
的指针,无论该时间点的myGrid.height
是什么。
当然,你可以像这样更明确:
typedef struct Grid {
unsigned width, height;
void* cell_internal; //Type: Cell(*)[height]
} Grid;
//in the constructor of Grid
newGrid->width = ...;
newGrid->height = ...;
Cell (*cells)[newGrid->height] = malloc(newGrid->width*sizeof(*cells));
newGrid->cell_internal = cells;
for(unsigned x = 0; x < newGrid->width; x++) {
for(unsigned y = 0; y < newGrid->height; y++) {
cells[x][y] = ...;
}
}
这种方法的缺点是,您需要为每个函数中的cell_internal
指针显式创建一个别名指针
Cell (*cells)[myGrid->height] = myGrid->cell_internal;
也许,这是更好的方法,因为它似乎可供更多人阅读。
使用灵活的数组。 使用两个malloc()
调用是微不足道的,如果您愿意突破对齐限制或严格混叠的限制,或者想要编写代码来强制对齐用于存储Cell
结构的malloc()
部分,则可以只使用一个调用。
typedef struct Grid {
unsigned width;
unsigned height;
Cell *cell[];
} Grid;
Grid *initGrid(unsigned width, unsigned height )
{
// the Grid structure itself
size_t bytesNeeded = sizeof( Grid );
// space for pointers
bytesNeeded += height * sizeof( Cell * );
Grid *g = malloc( bytesNeeded );
g->width = width;
g->height = height;
// get all the data needed with one malloc call
g->cell[ 0 ] = malloc( width * height * sizeof( Cell ) );
// fill in the pointers
for ( unsigned ii = 1; ii < height; ii++ )
{
g->cell[ ii ] = g->cell[ 0 ] + ii * width;
}
return g;
}
void freeGrid( Grid *g )
{
free( g->cell[ 0 ] );
free( g );
}
如果你不介意突破严格混叠的极限,你可以使用一个灵活的数组和一次调用malloc()
来完成(它留给读者强制对齐数据部分,这样就不会有潜在的对齐问题 - 这绝对是可能的(:
typedef struct Grid {
unsigned width;
unsigned height;
Cell *cell[];
} Grid;
Grid *initGrid(unsigned width, unsigned height )
{
// the Grid structure itself
size_t bytesNeeded = sizeof( Grid );
// space for pointers
bytesNeeded += height * sizeof( Cell * );
// space for data
bytesNeeded += width * height * sizeof( Cell );
Grid *g = malloc( bytesNeeded );
g->width = width;
g->height = height;
// fill in the pointers
// (technically a strict-aliasing/alignment violation as it assumes
// that &(g->cell[ height ]) is suitable to store a Cell...)
for ( unsigned ii = 0; ii < height; ii++ )
{
g->cell[ ii ] = ( Cell * ) &(g->cell[ height ]) +
ii * width;
}
return g;
}