Пытаясь перераспределить структуру, с - PullRequest
1 голос
/ 16 августа 2011

Я уже некоторое время пытаюсь создать доску Gomoku .Вот пример платы:

typedef struct Board
{
    int width;
    int height;
    char **board;
} Board;

Вот конструктор, который возвращает указатель на Board (BoardP):

BoardP createNewBoard(int width, int high)
{

    BoardP board = (BoardP) malloc(sizeof(Board));

    if (board == NULL)
    {
        reportError(MEM_OUT);
        return NULL;
    }
    board->height = high;
    board->width = width;
    board->board = (char**) malloc(high * sizeof(char*));
    int i;
    for (i=0; i<high; i++)
    {
        board->board[i] = (char*) malloc(width * sizeof(char));
    }

    if (board->board == NULL)
    {
        //SHOULD I FREE EACH LINE INDIVIDUALLY??!!??!
        free(board);
        reportError(MEM_OUT);
        return NULL;
    }

    return board;
}

и вот моя попытка расширить доску:

static BoardP expandBoard(BoardP theBoard, int X, int Y)
{
    int newWidth = theBoard->width;
    int newHeight = theBoard->height;
    if (X>theBoard->height)
    {
        newHeight = (newHeight+1) * 2;
    }

    if (Y>theBoard->width)
    {
        newWidth = (newWidth+1) * 2;
    }

    BoardP result = createNewBoard(newWidth,newHeight);

    int i;
    for (i=0; i<newHeight; i++)
    {
        result->board[i] = realloc(theBoard->board[i],newWidth);
    }

    freeBoard(theBoard);
    return result;
}

но я продолжаю получать ошибку сегментации, и я не знаю почему.Правильна ли моя основная идея?Есть идеи, что я делаю не так?спасибо

Ответы [ 3 ]

2 голосов
/ 17 августа 2011

Здесь много возможных ошибок.Убедитесь, что при создании непрозрачной структуры данных, как у вас, вы пишете вспомогательные функции, которые облегчат вам жизнь, а затем используйте эти помощники.

Я изменил ваш код в небольшой пример программы с драйвером заглушки.Позвольте мне разбить это на части:

#include <stdio.h>
#include <stdlib.h>

#DEFINE BOARD_BLANK ' '

typedef struct Board 
{
    int width;
    int height;
    char **board;
} Board;

typedef Board *BoardP;

#define MEM_OUT 109

void reportError(int msg)
{
    // Stub
}

Эта часть просто образец.Теперь давайте создадим функцию freeBoard, которую за пределами createNewBoard мы будем использовать исключительно для освобождения досок:

void freeBoard(BoardP board)
{
    int i;
    for (i=0; i<board->height; i++) // Free each row
        free(board->board[i]);
    free(board->board); // Free the row pointers
    free(board); // Free the structure
}

Теперь мы напишем конструктор плат.Обратите внимание, что я изменил код обработки ошибок, чтобы уменьшить количество повторений и добавить ясности.Это также единственное распространенное использование goto в C:

BoardP createNewBoard(int width, int height)
{
    BoardP board = (BoardP) malloc(sizeof(Board));

    if (board == NULL) 
        goto err1; // Error, jump to alternative error handling exit

    board->height = height;
    board->width = width;
    board->board = (char**) malloc(height* sizeof(char*));

    if (board->board == NULL) 
        goto err2; // Error, jump to alternative error handling exit

    int i;
    for (i=0; i<height; i++)
    {
        // Allocate each row one at a time
        board->board[i] = (char*) malloc(width * sizeof(char));
        if (board == NULL)
            goto err3; 
        for (j=0; j<height; j++)
            board->board[i][j] = BOARD_BLANK; // Give all the data points an initial value
    }

    // Successfully allocated board -- return it.
    return board;

    // Error handling code
err3:
    while (i-- > 0) // Start freeing rows from where we left off
        free(board->board[i]);
    free(board->board); // Free the allocated board->board too

err2:
    free(board);
err1:
    reportError(MEM_OUT);
    return NULL;
}

Достаточно просто.Там нет большой разницы, но обратите внимание, что я инициализировал каждую точку на доске «пустым» значением, которое я определил в макросе.Это потому, что память malloc может содержать в себе что угодно.Теперь перейдем к функции expandBoard:

BoardP expandBoard(BoardP board, int X, int Y)
{
    int newWidth = board->width;
    int newHeight = board->height;
    if (X > board->height) // This seems backwards. Height is usually in terms of Y
        newHeight = (newHeight+1)*2; // If we're trying to ensure that the new board
                                     // can contain X as a row index, then it should be:
                                     // newHeight = (board->height > X) ? board->height : X;

    if (Y > board->width) // Same here; width is usually in terms of X
        newWidth = (newWidth+1)*2; // Likewise, should this be:
                                   // newWidth = (board->width > Y) ? board->width : Y; 

    // Create a new board using the constructor we already wrote
    BoardP newBoard = createNewBoard(newWidth, newHeight);

    int i, j;
    for (i=0; i<newHeight; i++) 
        for (j=0; j<board->width; j++)
            newBoard->board[i][j] = board->board[i][j]; // Simply copy data from old to new

    freeBoard(board); // Free the old board
    return newBoard;
}

Прочитайте комментарии.Подход, который я выбрал, состоял в том, чтобы просто скопировать старые данные платы на новую плату.Это потому, что мы уже выделили совершенно новую доску.Вы можете значительно ускорить этот процесс, используя memcpy вместо дважды вложенных циклов, но в любом случае он должен быть более чем достаточно быстрым.

Проблема, с которой вы сталкивались раньше, заключается в том, что вы пытались перераспределить старые строкина которую указывает предыдущая доска, но затем вы освободили эту доску и потеряли все указатели, которые вы перераспределили.Это означает, что ваша "доска объявлений", которую вы первоначально разместили, просто отбрасывает все старые данные о плате, оставляя вам совершенно новую доску.

В общем, старайтесь держаться подальше от realloc, пока вы не почувствуете себя более комфортно с указателями, и выВы разработали умение отслеживать их.

Теперь для использования:

int main()
{
    BoardP board = createNewBoard(50, 50);
    board = expandBoard(board, 3, 2); // Make sure you assign the board pointer to
                                      // the new expanded board!
    freeBoard(board);

    return 0;
}

Обратите внимание на этот комментарий!Когда вы вызываете функцию, которая изменяет и возвращает указатель, убедитесь, что вы делаете назначение.В противном случае вы все равно будете указывать на старый, свободный объект, который вам не нужен.

В качестве альтернативы, вы всегда можете передать указатель на указатель на функции, которые изменяют указатели.

В любом случае, надеюсь, это поможет.Береги себя!

1 голос
/ 16 августа 2011

Похоже, что вы получаете ошибку сегментации, потому что вы указываете строки новой доски на перераспределенную память вашей старой платы, а затем освобождаете свою старую плату. Я не уверен, почему вы делаете realloc, когда вы уже выделяете память для новой платы в функции createNewBoard.

0 голосов
/ 16 августа 2011

Во-первых, вам нужно проверить, что каждая строка (когда она выделяется malloc в цикле for) не завершается с ошибкой. Вы проверили это для board->board, но не для каждой строки, которая также malloc 'd и должна быть проверена, чтобы убедиться, что они были правильно выделены. в противном случае одна из строк доски может быть NULL, и при попытке сделать это с помощью realloc она получит нулевой указатель.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...