realloc():下一个大小无效,双重免费

时间:2016-01-10 21:27:21

标签: c malloc realloc

作为一项功课,我应该创建两个函数,使您能够将元素推送到一个充当队列的数组中。我们应该这样做动态分配内存。我的程序几乎正常工作,但有时添加和删除太多元素时,我会收到类似" realloc()的错误:无效的下一个尺寸",双倍免费(当我只调用一次自由函数时) )并且队列开头的一些元素设置为0.例如,如果我先添加100个元素,然后删除90并尝试添加另外20个,我得到" free():无效的下一个大小(快):0x0000000001ea6010"。 我在这里做错了什么?

根据下面的建议,我改变了我的函数,将双指针作为数组的输入。然而,这现在给我一个分段错误 - 这意味着现在我根本不知道该找什么......

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

void enqueue(int **arr, int* lastElementIdx, size_t* totalElements, int element) {
    if (*lastElementIdx >= *totalElements) {        // check if memorry is sufficient, otherwise double
        *totalElements *= 2;

        int* temp = realloc(arr, (*totalElements * sizeof(int)));

        if (temp == NULL) {         // just in case realloc fails
            printf("Allocation error\n");
        } else {
            *arr = temp;
        }
    }

    if (*lastElementIdx <= *totalElements) {
        *lastElementIdx += 1;       // once everything is done: add element
        *arr[*lastElementIdx] = element;
    }
}

int dequeue(int **arr, int* lastElementIdx, size_t* totalElements) {
    if (*lastElementIdx > -1) {     // if queue is not empty...
        int deleted = *arr[0];      // save deleted value first (in case it's still needed)
        for (int i = 0; i <= *lastElementIdx; i++) {    // shift all elements
            *arr[i] = *arr[i + 1];
        }
        *lastElementIdx -= 1;   // index is now decreased by 1

        if (((*totalElements / 2) >= 10) && ((*lastElementIdx + 1) < (*totalElements / 2))) {   // cut memory in half if not needed
            *totalElements /= 2;

            *arr = realloc(arr, (*totalElements * sizeof(int)));
            int* temp = realloc(arr, (*totalElements * sizeof(int)));
            if (temp == NULL) {     // in case realloc fails
                printf("Allocation error\n");
                return 0;
            } else {
                *arr = temp;
            }
        }

        return deleted;
    } else {        // if queue is empty, print that there's nothing to dequeue
        printf("There are no elements inside the queue\n");
        return 0;
    }
}

void printQueue(int arr[], int lastElementIdx) {
    for (int i = 0; i <= lastElementIdx; i++) {     // print entire queue
        printf("[%d] = %d\n", i, arr[i]);
    }
    printf("\n");
}

int main (void) {

    size_t totalElements = 10;      // number of needed elements at the time
    int lastElementIdx = -1;        // index of last element in queue at the time
    int *arr = calloc(totalElements, sizeof(int));
    int **arrpointer = &arr;

    for (int i = 1; i < 101; i++) {
        enqueue(arrpointer, &lastElementIdx, &totalElements, i);
    }

    printQueue(arr, lastElementIdx);

    for (int i = 0; i < 90; i++) {
        dequeue(arrpointer, &lastElementIdx, &totalElements);
    }

    printQueue(arr, lastElementIdx);

    for (int i = 1; i < 21; i++) {
        enqueue(arrpointer, &lastElementIdx, &totalElements, i);
    }

    printQueue(arr, lastElementIdx);

    free(arr);

    return EXIT_SUCCESS;

}

3 个答案:

答案 0 :(得分:1)

扩展或收缩队列的存储时,需要向存储器提供指向存储的指针。这是因为realloc()不一定就地调整内存块的大小 - 它可能在其他地方创建一个新的,大小不同的块。即使它调整到较小的块时也允许这样做,而不仅仅是当它调整到更大的块时。

您对变量temp的使用给出了您已意识到此问题的外观,但正如@DerkHermann首次观察到的那样,您错误地处理了结果指针。也许你打算按照

的方式写一些东西
arr = temp;

代替。然而,即使这还不够。 C只有按值传递,因此如果修改函数参数arr的值,则该修改仅在函数中可见(在arr中接收副本调用者传递的值。如果realloc()分配了一个新块,那么会给调用者留下无效的指针。

如果您希望enqueue()dequeue()函数能够调整队列的存储大小,则必须将指针传递给间接存储。在你现在的位置,最简单的方法是传递一个双指针,这样你就可以修改它的引用:

void enqueue(int **arr, int* lastElementIdx, size_t* totalElements, int element) {
    /* ... */
    *arr = temp;
    /* ... */
}

但是,请注意,您正在传递三个单独的指针,其中包含表示队列状态的指针。创建一个struct类型可以更清晰,它将这些细节组合在一个包中,并将指针传递给该类型的对象:

struct queue {
    int *arr;
    size_t capacity;
    size_t last_element_index;
};

void enqueue(struct queue *queue, int element) {
    /* ... */
    queue->arr = temp;
    /* ... */
}

答案 1 :(得分:0)

也许这不是唯一的问题,但至少以下几行并不是您所期望的:

*temp = *arr;

看起来好像要用realloc的结果替换arr,将其传递回调用函数(与其他的inout参数一样)。但是,arr不是一个inout参数:它是一个整数数组,而不是指向整数数组的指针。您实际使用上面的代码行是将arr的第一个元素复制到新分配的内存范围。然而,新分配的内存范围temp仍然被遗忘,造成内存泄漏。

答案 2 :(得分:0)

添加一个双指针来重新分配正确位置的空间,用size_t totalElements更改比较函数,并修复一些额外的错误,最终解决了这个问题。

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

void enqueue(int **arr, int* lastElementIdx, size_t* totalElements, int element) {
    if (*lastElementIdx + 1 >= (int)(*totalElements) - 1) {     // check if memorry is sufficient, otherwise double
        *totalElements *= 2;

        int* temp = realloc(*arr, (*totalElements * sizeof(int)));
        if (temp == NULL) {         // just in case realloc fails
            printf("Allocation error\n");
        } else {
            *arr = temp;
        }
    }

    if (*lastElementIdx + 1 <= (int)(*totalElements) - 1) {
        *lastElementIdx += 1;       // once everything is done and if there is now enough space: add element
        (*arr)[*lastElementIdx] = element;
    }
}

int dequeue(int **arr, int* lastElementIdx, size_t* totalElements) {
    if (*lastElementIdx > -1) {     // if queue is not empty...
        int deleted = (*arr)[0];        // save deleted value first (in case it's still needed)
        for (int i = 0; i <= *lastElementIdx; i++) {    // shift all elements
            (*arr)[i] = (*arr)[i + 1];
        }
        *lastElementIdx -= 1;   // index is now decreased by 1

        if (((*totalElements / 2) >= 10) && ((*lastElementIdx + 1) < (*totalElements / 2))) {   // cut memory in half if not needed
            *totalElements /= 2;

            int* temp = realloc(*arr, (*totalElements * sizeof(int)));
            if (temp == NULL) {     // in case realloc fails
                printf("Allocation error\n");
                return 0;
            } else {
                *arr = temp;
            }
        }

        return deleted;
    } else {        // if queue is empty, print that there's nothing to dequeue
        printf("There are no elements inside the queue\n");
        return 0;
    }
}

void printQueue(int arr[], int lastElementIdx) {
    for (int i = 0; i <= lastElementIdx; i++) {     // print entire queue
        printf("[%d] = %d\n", i, arr[i]);
    }
    printf("\n");
}

int main (void) {

    size_t totalElements = 10;      // number of needed elements at the time
    int lastElementIdx = -1;        // index of last element in queue at the time
    int *arr = calloc(totalElements, sizeof(int));
    int **arrpointer = &arr;

    for (int i = 1; i < 101; i++) {
        enqueue(arrpointer, &lastElementIdx, &totalElements, i);
    }

    printQueue(arr, lastElementIdx);

    for (int i = 0; i < 102; i++) {
        dequeue(arrpointer, &lastElementIdx, &totalElements);
    }

    printQueue(arr, lastElementIdx);

    for (int i = 1; i < 21; i++) {
        enqueue(arrpointer, &lastElementIdx, &totalElements, i);
    }

    printQueue(arr, lastElementIdx);

    free(arr);

    return EXIT_SUCCESS;

}