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

时间:2015-11-14 20:51:02

标签: c arrays function malloc

我正在使用C语言中的连接四游戏模拟器。

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

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

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

<ListView>
  <ListView.ItemContainerStyle>
    <Style TargetType="ListViewItem">
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
    </Style>
  </ListView.ItemContainerStyle>
      ...
</ListView>

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

#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;
}

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

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

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

2 个答案:

答案 0 :(得分:2)

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指针丢失(您正在覆盖它),只是返回(通过*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;

答案 1 :(得分:1)

我会用以下方式解决你的问题。并不是说我已经存在一些错误处理,并且在完成时添加了一种破坏电路板的方法。

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

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

现在,转到代码。你没有提供main,所以我创建了一个快速存根主体:

    #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;
    }

关于这个功能的几点评论;

  • 首先进行一些防御性编程,我检查一下是否已经分配了电路板结构。如果是这样的话,我会在创建新的电路板之前先销毁它。这可以防止我们泄漏内存,因为已经分配了一个板,然后我们回想起这个函数,我们会过度写入指向原始板的指针,这意味着我们将失去对第一块板的“句柄”。
  • 请注意,每次调用malloc都会检查以确保我们确实获得了我们想要的内存。我倾向于将支票放在与malloc相同的陈述中,但这是个人偏好。
  • 我现在实际上有一个重要的回报值。在原始代码中,无论所有分配是否成功,您都将返回true。请注意,我只在执行两次分配后才返回true,并且它们都成功了。

好的,我添加的新功能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 - 首先是板结构意味着你丢失了对移动数组的唯一引用,然后你就会泄漏内存。)
  • free移动数组之前,我再次检查它是否存在。
  • 一旦移动数组被销毁,我继续销毁板结构,并将指针设置回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)

希望这有帮助, 吨。