glibc检测到-双重释放或损坏

3

这可能有点长,敬请谅解。 考虑以下代码(我已经省略了一些无关部分)。这段代码接收一个指向结构体(BoardP theBoard)的指针,x和y坐标以及一个值。 目标是将该值放入在结构体中找到的2D数组中。 如果坐标超出范围,我必须增加表格的大小,将旧数据复制到新数据中,并将该值放置在其位置。 好吧,这段代码第一次调用可以工作,但第二次调用会崩溃并写入:

*** glibc detected *** ./b: double free or corruption (top): 0x092ae138 ***  

我找不到答案,希望你能帮忙。
这些是来自main()函数的调用。

BoardP p = CreateNewBoard(10,10);
PutBoardSquare(p,10,5,'X');
PutBoardSquare(p,5,10,'O');

Boolean PutBoardSquare(BoardP theBoard, int X, int Y, char val) {

    if (inBounds(X,Y,theBoard->_rows,theBoard->_cols)) {
        theBoard->_board[X * theBoard->_cols + Y] = val;
        return TRUE;
    }
    else {
        int newRows = (X>=theBoard->_rows) ? (2*X) : theBoard->_rows;
        int newCols = (Y>=theBoard->_cols) ? (2*Y) : theBoard->_cols;
        BoardP newBoard = CreateNewBoard(newCols,newRows);  //this creates a new Board with the new dimensions
        if (newBoard == NULL) {
            //ReportError(MEM_OUT);
            return FALSE;
        }
        else {
            copyData(theBoard,newBoard);
            freeBoardArray(&theBoard->_board[0]); //free old array
            theBoard->_board = newBoard->_board;  //old array point to new array
            FreeBoard(newBoard);  //free the temp copy THIS CAUSES THE PROBLEM  
            PutBoardSquare(theBoard,X,Y,val);//recursion, will be in bounds now
            return TRUE;
        }
    }
}

这些是免费功能:
void FreeBoard(BoardP board) {
    if (board != NULL) {
        printf("FREE 1\n");
        //free the board array:
        if (board->_board != NULL) {
            printf("FREE 2\n");
            freeBoardArray(&board->_board[0]);
            printf("FREE 3\n");
        }
        free(board);
    }
}

static void freeBoardArray(char * arrP) {
    free(arrP);   //**PROGRAM CRASH HERE**
}

这是我创建新看板的步骤:

BoardP CreateNewBoard(int width, int high) {
    BoardP board = (BoardP) malloc(sizeof(Board));
    if (board != NULL) {
        board->_board = allocateBoardArray(high,width);
        if ( board->_board == NULL) {
            FreeBoard(board);
            //TODO make file ReportError(MEM_OUT);
            return NULL;
        }
        initializeBoard(board,high,width,X_SIGN,SPACE);
        return board;
    }
    else {
        FreeBoard(board);
        //TODO make file ReportError(MEM_OUT);
        return NULL;
    }
}

static char* allocateBoardArray(int row, int col) {
    char* newBoard = (char*) malloc(row * col * sizeof(char));

    if (newBoard == NULL) {
        return NULL;
    }
    return newBoard;
}

这是BoardP:

typedef struct Board* BoardP;

新的板块内存分配在哪里?是在CreateNewBoard函数中吗?如果是这样,没有看到它如何分配内存,就不可能确定你的free调用是否正确。如果glibc报告错误,那么它们很可能是不正确的。 - CB Bailey
哦,BoardP是什么?它是一个指针的typedef还是一个隐式可转换为指针的类类型? - CB Bailey
哎呀,我搞砸了...我会立刻修复它。 - Asher Saban
4个回答

6

您需要释放已分配但不再需要保留引用的内存。从您的代码中,我可以看到以下行。

theBoard->_board = newBoard->_board;

现在你维护一个分配的指针的引用,然后释放该指针本身。

示例代码:

char *foo()
{
char *ref1;
char *ref2;
ref1 = malloc(256);
ref2=ref1;// Holding reference to a pointer in another pointer
strcpy(ref1,"stackoverflow");
printf("%s %s",ref1,ref2); // This prints stackoverflow twice
free(ref1); // This is valid but you can access ref2 or ref1 after this point
return ref2; /// This will cause problems
}

我不认为正在发生的是这样的。newBoard->_board是从newBoard分配的单独块。 - karunski
我明白,但我的第一个问题是在函数退出后仍然保留对theBoard的更改。这是我解决它的唯一方法,但正如你所说,这是错误的。我该如何更改theBoard中的数据,以便在函数退出后仍然有效? - Asher Saban
哎呀,Praveen 是对的。FreeBoard(newBoard) 删除了 newBoard->_board。只需使用 free(newBoard) 而不是调用您的 FreeBoard 函数。 - karunski
这是我的结构体: typedef struct Board { int _rows; int _cols; char _board; } Board; 如果我执行free(newBoard),它将释放结构体但不会释放分配的char _board(数组)吗? - Asher Saban

1

试试这个:

copyData(theBoard, newBoard);
/* swap the _board pointers */
char * b = theBoard->_board;
theBoard->_board = newBoard->_board;
newBoard->_board = b;
FreeBoard(newBoard);  /* cleanup the temp struct and the old array */

谢谢您的回答。最后一个问题: 如果结构体还包含诸如int和char之类的基本类型,释放结构体时是否也会释放它们? - Asher Saban
1
是的,你只需要free()那些你malloc()的东西。 - karunski

0

这个错误意味着您试图释放已经被您释放的内存。我怀疑的是这段代码块

if (board != NULL) {
printf("FREE 1\n");
//free the board array:
if (board->_board != NULL) {
    printf("FREE 2\n");
    freeBoardArray(&board->_board[0]);
    printf("FREE 3\n");
}
free(board);

当你释放结构体的一部分时,使用freeBoardArray(&board->_board[0]);,然后你又释放整个结构体,使用free(board);,这看起来是导致问题的原因。为什么要传递_board指针的地址?我写的代码与你的代码在同一行,也出现了问题。

struct a{
    int * next;

}; int main() {

    struct a *aptr = (struct a *)malloc(sizeof(struct a));
    aptr->next=(int *)malloc(5*sizeof(int));
    free(&aptr->next);
    free(aptr);
    return 0;

}

这段代码会导致与您展示的相同的问题。现在尝试在从free(&aptr->next)语句中删除'&'后再次尝试此代码。它将正常工作。 所以我认为您已经得到了提示,需要在哪里进行修改。


&board->_board[0] 和 board->_board 是一样的意思。所以它比必要的更冗长,但并不是错误的。 - karunski

0

在valgrind下运行此代码将精确告诉您第一次释放内存的行以及再次尝试释放它的时间。

它还会告诉您是否尝试访问已释放块内部的任何地址。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接