内部数组挂起并体验UB的struct的创建方法

Creation method for struct with internal array hanging and experiencing UB

提问人:Blundergat 提问时间:4/13/2023 最后编辑:Blundergat 更新时间:4/13/2023 访问量:42

问:

snakeGame * createSnakeGame()
{
    int row = 10;
    int col = 10;

    snakeGame * game = (snakeGame *) malloc(sizeof(snakeGame));

    game->rows = row;
    game->columns = col;

    for (int i = 0; i < row; i++)
    {
        if (i == 0 || i == row - 1)
        {
            for (int j = 0; j < col; j++)
            {
                game->board[i][j] = BORDER;    
            }
        }
        else
        {
            game->board[i][0] = BORDER;
            game->board[i][col - 1] = BORDER;
        }
    }

    return game;
}
typedef struct 
{
    snakeEntity ** board;
    int rows;
    int columns;
} snakeGame;
typedef enum 
{
    SNAKE_HORIZONTAL,
    SNAKE_VERTICAL,
    SNAKE_UP_RIGHT,
    SNAKE_UP_LEFT,
    SNAKE_DOWN_RIGHT,
    SNAKE_DOWN_LEFT,
    FOOD,
    BORDER,
    EMPTY
} snakeEntity;
int main()
{
    snakeGame * game = createSnakeGame();
    printf("Success");
    return 0;
}

无法到达 print 语句,因为程序挂起并在随机时间后突然退出。

我尝试以不同的方式分配结构,例如考虑数组大小和单独初始化板。但是,仍然存在相同的 UB。

// Adding the array size to the allocated memory
snakeGame * game = (snakeGame *) malloc(sizeof(snakeGame) + (row * col * sizeof(snakeEntity)));
// Allocating memory to each row individually to the size of the column
game->board = (snakeEntity **) malloc(row * sizeof(snakeEntity));
for (int i = 0; i < row; i++)
{
    game->board[i] = (snakeEntity *) malloc(col * sizeof(snakeEntity));
}
// Initialising the board separately
snakeGame * game = (snakeGame *) malloc(sizeof(snakeGame));
snakeEntity ** board = (snakeEntity **) malloc(row * col * sizeof(snakeEntity));
game->board = board;

我将不胜感激有关如何解决问题的一些建议。

c 内存 结构 malloc

评论

1赞 Some programmer dude 4/13/2023
该成员应更多地被视为指针数组(指向数组)。不是单个元素数组。您需要更多循环来初始化每个指针。boardsnakeEntity
0赞 Blundergat 4/13/2023
@Someprogrammerdude 在新添加的第二个示例中,我是否正确实现了您的建议?对我来说,这种行为没有改变。
0赞 Some programmer dude 4/13/2023
“将内存分别分配给每一行以适应列的大小”的代码是正确的。
0赞 Blundergat 4/13/2023
哦!我现在明白了我愚蠢的错误。非常感谢你:)请随时自己发布答案,或者我稍后会自己添加。
0赞 Some programmer dude 4/13/2023
顺便说一句,在 C 语言中,您不必(也真的不应该)转换 malloc 的结果

答:

2赞 Lundin 4/13/2023 #1

在循环中分配给成员之前,您不会为其分配空间:。boardgame->board[i][j]

至于,这也是错误的。A 不是二维数组,不能用作二维数组。但是,它可以用作指向指针数组中第一项的指针,以模拟 2D 数组。在这种情况下,正确的用法是:snakeEntity ** board = (snakeEntity **) malloc(row * col * sizeof(snakeEntity));board**

game->board = malloc(row * sizeof(snakeEntity));
for(size_t i=0; i<row; i++)
{
  game->board[i] = malloc(col * sizeof(snakeEntity));
}

然后以同样的方式。free()


然而,指针到指针的版本是幼稚的(很多坏书和坏老师都在宣扬它)。查看正确分配多维数组

有几种方法可以使用 2D 数组而不是指针到指针来实现这一点。例如,使用灵活的数组成员。不幸的是,灵活的数组成员仅支持声明的一维数组,但我们可以使用一个数组作为二维数组指针的占位符。然后永远不会实际访问它作为它被声明为的一维数组。

它以牺牲可读性为代价提供了非常高效的代码。例:

typedef struct 
{
  size_t rows;
  size_t cols;
  snakeEntity board[];
} snakeGame;

然后,您可以一次性分配所有内容:

snakeGame* game = malloc (sizeof(snakeGame) + sizeof(snakeEntity[ROWS][COLS]));
game->rows = ROWS;
game->cols = COLS;
...
free(game); // and later free on a single line

board未初始化,但现在指向分配的 2D 数组(充满垃圾)。

我们可以通过转换为适当的指针类型来填充它(这是难以阅读的部分):

snakeEntity (*board)[game->cols] = (snakeEntity(*)[game->cols]) game->board;

现在,我们可以使用此指针来填充或以其他方式访问 2D 数组,使用 2D 数组语法:

for(size_t i=0; i<game->rows; i++)
{
  for(size_t j=0; j<game->cols; j++)
  {
    board[i][j] = whatever;
  }
}