Здесь много возможных ошибок.Убедитесь, что при создании непрозрачной структуры данных, как у вас, вы пишете вспомогательные функции, которые облегчат вам жизнь, а затем используйте эти помощники.
Я изменил ваш код в небольшой пример программы с драйвером заглушки.Позвольте мне разбить это на части:
#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;
}
Обратите внимание на этот комментарий!Когда вы вызываете функцию, которая изменяет и возвращает указатель, убедитесь, что вы делаете назначение.В противном случае вы все равно будете указывать на старый, свободный объект, который вам не нужен.
В качестве альтернативы, вы всегда можете передать указатель на указатель на функции, которые изменяют указатели.
В любом случае, надеюсь, это поможет.Береги себя!