我正在为Conway的人生游戏开发一个C实现,我被要求使用以下标题:
#ifndef game_of_life_h
#define game_of_life_h
#include <stdio.h>
#include <stdlib.h>
// a structure containing a square board for the game and its size
typedef struct gol{
int **board;
size_t size;
} gol;
// dynamically creates a struct gol of size 20 and returns a pointer to it
gol* create_default_gol();
// creates dynamically a struct gol of a specified size and returns a pointer to it.
gol* create_gol(size_t size);
// destroy gol structures
void destroy_gol(gol* g);
// the board of 'g' is set to 'b'. You do not need to check if 'b' has a proper size and values
void set_pattern(gol* g, int** b);
// using rules of the game of life, the function sets next pattern to the g->board
void next_pattern(gol* g);
/* returns sum of all the neighbours of the cell g->board[i][j]. The function is an auxiliary
function and should be used in the following function. */
int neighbour_sum(gol* g, int i, int j);
// prints the current pattern of the g-board on the screen
void print(gol* g);
#endif
我添加了评论来帮助解释每一位是什么。
gol.board
是一个2级整数数组,包含x和y坐标,即board[x][y]
,每个坐标可以是1(有效)或0(无效)。
这都是一些背景信息,我正在尝试编写我的第一个函数create_default_gol()
,它将返回一个指向gol
实例的指针,带有20x20板。
然后,我尝试通过20x20板遍历每个坐标,并将其设置为0
,运行此程序时,我得到了Segmentation fault (core dumped)
。
下面的代码是我的c文件,其中包含核心代码和main()
函数:
#include "game_of_life.h"
int main()
{
// Create a 20x20 game
gol* g_temp = create_default_gol();
int x,y;
for (x = 0; x < 20; x++)
{
for (y = 0; y < 20; y++)
{
g_temp->board[x][y] = 0;
}
}
free(g_temp);
}
// return a pointer to a 20x20 game of life
gol* create_default_gol()
{
gol* g_rtn = malloc(sizeof(*g_rtn) + (sizeof(int) * 20 * 20));
return g_rtn;
}
这是我想实现的第一个功能,能够为每个坐标生成一个20x20板,其状态为0(死)。
请随意批评我的代码,我想确定为什么我会出现分段错误,以及我是否在create_default_gol()
函数中正确分配了内存。
谢谢!
类型int **board;
意味着board
必须包含一个指针数组,每个指针指向每行的开头。您现有的分配忽略了这一点,只分配*g_rtn
加上板中的int
。
假设你必须坚持int **board;
类型,那么分配你的董事会的典型方式是:
gol* g_rtn = malloc(sizeof *g_rtn);
g_rtn->size = size;
g_rtn->board = malloc(size * sizeof *g_rtn->board);
for (int i = 0; i < size; ++i)
g_rtn->board[i] = malloc(size * sizeof **g_rtn->board);
这段代码涉及很多malloc小块。您可以将板上的行和列压缩为一个单独的分配,但也需要设置指向每行开头的指针,因为board
必须是指向int的指针数组。
这种方法的另一个问题是对齐。保证malloc
结果对任何类型都是对齐的;但是CCD_ 16可能具有比CCD_ 17更严格的对准要求。下面的代码假设它没有;如果你想移植,那么你可以添加一些编译时检查(或者运行它,看看它是否中止!)。
所需的内存量是最后两个malloc:的总和
g_rtn->board = malloc( size * size * sizeof **g_rtn->board
+ size * sizeof *g_rtn->board );
然后第一行将在行指针结束后开始(强制转换是必要的,因为我们将int **
转换为int *
,使用void *
意味着我们不必重复单词int
):
g_rtn->board[0] = (void *) (g_rtn->board + size);
其他行中的每一行都有size
int:
for (int i = 1; i < size; ++i)
g_rtn->board[i] = g_rtn->board[i-1] + size;
请注意,这比只使用一维数组和计算偏移量要复杂得多,但它规定必须有两个间接级别才能访问板。
此外,这比"规范"版本更复杂。在这个版本中,我们用代码复杂性来换取malloc数量减少的好处。如果你的程序通常只分配一个板或少量板,那么这种权衡可能不值得,而且规范版本会让你更少头疼。
最后,可以在单个malloc中同时分配*g_rtn
和板,正如您在问题中所尝试的那样。然而,我的建议(基于经验)是,将董事会分开更简单。如果棋盘是对游戏对象的单独分配,它会使代码更清晰,使对象更容易使用和更改。
create_default_gol()
未能初始化board
,因此对其应用[]
运算符(在main()
中),程序访问"invaid"内存,并使用ethis引发未定义的行为。
虽然分配了足够的内存,但代码仍然需要通过使板指向内存
gol->board = ((char*) gol) + sizeof(*gol);
更新
正如Matt McNabb所指出的,board
的注释指向指向int
的指针数组,因此初始化更为复杂:
gol * g_rtn = malloc(sizeof(*g_rtn) + 20 * sizeof(*gol->board));
g_rtn->board = ((char*) gol) + sizeof(*gol);
for (size_t i = 0; i<20; ++i)
{
g_rtn->board[i] = malloc(20 * sizeof(*g_rtn->board[i])
}
此外,该代码未设置gol
的成员size
。根据您告诉我们的内容,尚不清楚它是否应包含字节、行/列或字段的核心。
另外,^2编码像20
这样的"幻数"是个坏习惯。
此外,^3 create_default_gol
没有指定任何参数,这明确地允许任何数字m,而不是像您可能预期的那样不允许任何数字。
总而言之,我会把create_default_gol()
编码成这样:
gol * create_default_gol(const size_t rows, const size_t columns)
{
size_t size_rows = rows * sizeof(*g_rtn->board));
size_t size_column = columns * sizeof(**g_rtn->board));
gol * g_rtn = malloc(sizeof(*g_rtn) + size_rows);
g_rtn->board = ((char*) gol) + sizeof(*gol);
if (NULL ! = g_rtn)
{
for (size_t i = 0; i<columns; ++i)
{
g_rtn->board[i] = malloc(size_columns); /* TODO: Add error checking here. */
}
g_rtn->size = size_rows * size_columns; /* Or what ever this attribute is meant for. */
}
return g_rtn;
}
gol* create_default_gol()
{
int **a,i;
a = (int**)malloc(20 * sizeof(int *));
for (i = 0; i < 20; i++)
a[i] = (int*)malloc(20 * sizeof(int));
gol* g_rtn = (gol*)malloc(sizeof(*g_rtn));
g_rtn->board = a;
return g_rtn;
}
int main()
{
// Create a 20x20 game
gol* g_temp = create_default_gol();
int x,y;
for (x = 0; x < 20; x++)
{
for (y = 0; y < 20; y++)
{
g_temp->board[x][y] = 10;
}
}
for(x=0;x<20;x++)
free(g_temp->board[x]);
free(g_temp->board);
free(g_temp);
}
main (void)
{
gol* gameOfLife;
gameOfLife = create_default_gol();
free(gameOfLife);
}
gol* create_default_gol()
{
int size = 20;
gol* g_rtn = malloc(sizeof *g_rtn);
g_rtn = malloc(sizeof g_rtn);
g_rtn->size = size;
g_rtn->board = malloc(size * sizeof *g_rtn->board);
int i, b;
for (i = 0; i < size; ++i){
g_rtn->board[i] = malloc(sizeof (int) * size);
for(b=0;b<size;b++){
g_rtn->board[i][b] = 0;
}
}
return g_rtn;
}
或者,由于您还需要添加一个自定义大小的create_gol(size_tnew_size),因此您也可以将其编写如下。
main (void)
{
gol* gameOfLife;
gameOfLife = create_default_gol();
free(gameOfLife);
}
gol* create_default_gol()
{
size_t size = 20;
return create_gol(size);
}
gol* create_gol(size_t new_size)
{
gol* g_rtn = malloc(sizeof *g_rtn);
g_rtn = malloc(sizeof g_rtn);
g_rtn->size = new_size;
g_rtn->board = malloc(size * sizeof *g_rtn->board);
int i, b;
for (i = 0; i < size; ++i){
g_rtn->board[i] = malloc(sizeof (int) * size);
for(b=0;b<size;b++){
g_rtn->board[i][b] = 0;
}
}
return g_rtn;
}
这样做只会最大限度地减少所需的代码量。