C:无法在另一个函数中访问动态大小的数组

C: dynamically sized array cannot be accessed in another function

我正在用 C 开发一个四连线游戏模拟器。

https://en.wikipedia.org/wiki/Connect_Four

第一步是为游戏创建棋盘环境。我继续创建了一个数据类型 board_t,它是一个包含动态大小数组的结构,该数组将保存在一维数组中播放的动作。 Board_t 还包括板的高度和宽度信息,因此可以以正确的方式检索东西。

我在 board_create() 函数中初始化这个棋盘,并在 board_can_play() 函数中使用这个初始化的 board_t 变量来检查在给定的游戏中是否可以进行任何游戏.这是代码。

#include <stdlib.h>
#include <assert.h>
#define PLAYER_BLUE    2
#define PLAYER_YELLOW  1
#define PLAYER_EMPTY   0

typedef unsigned char player_t;

typedef struct board_t
{
    unsigned int width;
    unsigned int height;
    unsigned int run;
    player_t * moves;
} board_t;

bool board_create (board_t ** b, unsigned int height, unsigned int width, unsigned int run, const player_t * i)
{
    //Declare a board_t variable temp_b where parameters will be saved.
    board_t temp_b;
    //Create a pointer and malloc a memory location based on width and height.
    temp_b.moves = malloc(sizeof(unsigned char)*(height*width));
    //Itereate through the moves and initialize with the given player_t
    int j;
    for (j = 0; j < width*height; j++)
    {
        temp_b.moves[j] = PLAYER_EMPTY;
    }
    //Input all the values to temp_b
    temp_b.height = height;
    temp_b.width = width;
    temp_b.run = run;
    //Make a temporary pointer and assign that pointer to *b.
    board_t * temp_b_ptr = malloc(sizeof(board_t));
    temp_b_ptr = &temp_b;
    *b = temp_b_ptr;
    return true;
};

/// Return true if the specified player can make a move on the
/// board
bool board_can_play (const board_t * b, player_t p)
{
    unsigned int i;
    unsigned int height = board_get_height(b);
    unsigned int width = board_get_width(b);
    for(i = (height-1)*width; i < height*width; i++)
    {
        if (b->moves[i] == PLAYER_EMPTY)
        {
            return true;
        }
    }
    return false;
}

但是,每当我从 board_can_play() 调用 board_t *b 时,程序都会出现分段错误。更具体地说,

        if (b->moves[i] == PLAYER_EMPTY)

这一行给我一个分段错误。此外,在 main() 中运行良好的函数在 board_can_play() 中不起作用。例如,

    unsigned int height = board_get_height(b);
    unsigned int width = board_get_width(b);

本应得到3和3,却得到2和419678?我现在花了大约 7 个小时弄清楚,但无法弄清楚发生了什么。

在给你段错误的 if 语句中,

    if (b->moves[i] == PLAYER_EMPTY)

问题不是 moves 是如何分配的,而是 b 本身是如何分配的。在 board_create() 中,您将在此处返回一个临时对象:

    board_t * temp_b_ptr = malloc(sizeof(board_t));
    temp_b_ptr = &temp_b;
    *b = temp_b_ptr;

malloc'ed 指针丢失(您正在覆盖它)并且只是返回(通过 *b)一个指向局部变量的指针。

所以将分配移动到顶部并使用 temp_b_ptr 而不是 temp_b:

    board_t *temp_b_ptr = malloc(sizeof(board_t));
    if( !temp_b_ptr ) {
       /* error handling */
       }
    ....
    ....
    *b = temp_b_ptr;

我会通过以下方式解决您的问题。并不是说我存入了一些错误处理,以及添加了一种在完成后销毁电路板的方法。

以下代码在 Ubuntu 14.01 LTS 中编译时没有警告,使用 gcc-4.8.2。我使用以下命令行编译代码:

     gcc -g -std=c99 -pedantic -Wall connect4.c -o connect4 

现在,开始编写代码。你没有提供主线,所以我创建了一个快速存根主线:

    #include <stdlib.h>
    #include <stdbool.h>
    #include <stdio.h>
    #include <assert.h>
    #define PLAYER_BLUE    2
    #define PLAYER_YELLOW  1
    #define PLAYER_EMPTY   0

    typedef unsigned char player_t;

    typedef struct board_t
    {
        unsigned int width;
        unsigned int height;
        unsigned int run;
        player_t * moves;
    } board_t;


    bool board_create(board_t** b, unsigned int height, unsigned int width);
    void board_destroy(board_t** b);

    int board_get_height(const board_t* b);
    int board_get_width(const board_t* b);

    int main(int argc, char** argv)
    {
         board_t*   pBoard = NULL;
         if(board_create(&pBoard, 4, 4))
         {
             printf("board dimensions: %d by %d\n", board_get_height(pBoard), board_get_width(pBoard));
             // TODO : put game logic here...

            board_destroy(&pBoard);
         }
         else
         {
             fprintf(stderr, "failed to initialize the board structure\n");
         }

         return 0;
   }

主要景点并不多,正如您所期望的那样。接下来是 board_create 功能。请注意,我删除了 runplayer_t 参数,因为我没有看到您在代码中使用它们。

    bool board_create(board_t** b, unsigned int height, unsigned int width)
    {
        bool   bRet = false;

        if(*b != NULL)    // we already have a board struct laying about
        {
            board_destroy(b);
        }

        if(NULL != (*b = malloc(sizeof(board_t))))
        {
            (*b)->width = width;
            (*b)->height = height;

            if(NULL != ((*b)->moves = malloc(sizeof(unsigned char*)*(height * width))))
            {
                for(int j = 0; j < height * width; j++)
                    (*b)->moves[j] = PLAYER_EMPTY;

                bRet = true;
            }
            else
            {
                /* TODO : handle allocation error of moves array */
            }
        }
        else
        {
            /* TODO : handle allocation error of board struct */
        }

        return bRet;
    }

对这个函数的一些评论;

  • 首先是一些防御性编程,我检查了之前没有分配的棋盘结构。如果是的话,我会在创建新板之前先销毁之前的板。这可以防止我们泄漏内存,因为分配了一块板,然后我们调用了这个函数,我们将覆盖指向原始板的指针,这意味着我们将丢失我们的 'handle' 到第一块板。
  • 请注意,每次调用 malloc 都是检查以确保我们确实获得了我们想要的内存。我倾向于将支票放在与 malloc 相同的语句中,但这是个人偏好。
  • 我现在实际上有一个重要的 return 值。在您的原始代码中,无论所有分配是否成功,您都只会 return true 。请注意,在执行了两个分配并且它们都成功后,我才 return 为真。

好的,在我添加的新功能上,board_destroy:

    void board_destroy(board_t** b)
    {
        if(*b != NULL)   // no board struct, nothing to do..
        {
            if((*b)->moves != NULL)
            {
                free((*b)->moves);
            }
            free(*b);
           *b = NULL;
        }
    }

对该函数的一些评论;

  • 更多的防御性编程,我检查以确保我们在做任何工作之前确实有一个要摆脱的董事会结构。
  • 请记住,在您的电路板结构中,您有一个动态数组,因此您需要先 free 该数组。 (free-首先访问棋盘结构意味着您丢失了对 moves 数组的唯一引用,然后您将泄漏内存)。
  • free-ing moves 数组之前,我再次检查它是否存在。
  • 一旦 moves 数组被销毁,我继续销毁棋盘结构,并将指针设置回 NULL(以防我们想在 main 中重用棋盘指针)。

您没有提供 board_get_* 函数的实现细节,但从它们的用法来看,我怀疑您将它们实现为:

int board_get_height(const board_t* b)
{
    return (b->height);
}

int board_get_width(const board_t* b)
{
    return (b->width);
}   

由于不确定您打算如何使用它,我没有对您的 board_can_more 函数执行任何操作。

以上代码的快速 运行:

******@ubuntu:~/junk$ ./connect4
board dimensions: 4 by 4
******@ubuntu:~/junk$       

我个人的意见是,当在 C 或 C++ 中进行大量内存分配和释放时,您应该 运行 定期在 valgrind 下运行您的程序,以确保您没有泄漏内存或有其他与内存相关的错误。下面是 运行 在 valgrind 下使用此代码的示例:

    *****@ubuntu:~/junk$ valgrind --tool=memcheck --leak-check=full ./connect4
    ==4265== Memcheck, a memory error detector
    ==4265== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
    ==4265== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
    ==4265== Command: ./connect4
    ==4265== 
    board dimensions: 4 by 4
    ==4265== 
    ==4265== HEAP SUMMARY:
    ==4265==     in use at exit: 0 bytes in 0 blocks
    ==4265==   total heap usage: 2 allocs, 2 frees, 152 bytes allocated
    ==4265== 
    ==4265== All heap blocks were freed -- no leaks are possible
    ==4265== 
    ==4265== For counts of detected and suppressed errors, rerun with: -v
    ==4265== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

希望这对您有所帮助, T.