realloc():无效的下一个大小和双重释放
realloc(): invalid next size and double free
作为作业,我应该创建 2 个函数,使您能够将元素推入和弹出到充当队列的数组。我们应该动态分配内存。我的程序几乎可以正常工作,但有时在添加和删除太多元素时,会出现 "realloc(): invalid next size"、double free(当我只调用了一次 free 函数时)和一些元素在开头的错误队列设置为 0。例如,如果我先添加 100 个元素,然后删除 90 个并尝试添加另外 20 个,我得到 "free(): invalid next size (fast): 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;
}
也许这不是唯一的问题,但至少下面这行不是您所期望的:
*temp = *arr;
您似乎想用 realloc 的结果替换 arr,将其返回给调用函数(与其他 inout 参数一样)。但是,arr 不是 inout 参数:它是一个整数数组,而不是一个指向整数数组的指针。您实际上对上述代码行所做的是将 arr 的第一个元素复制到新分配的内存范围。然而,新分配的内存范围 temp 却被遗忘了,造成了内存泄漏。
当您扩展或收缩队列的存储空间时,您需要向调用方提供指向存储空间的指针。这是因为 realloc()
不一定会调整内存块的大小 in-place —— 它可能会在其他地方创建一个新的、大小不同的块。即使调整为较小的块,也允许这样做,而不仅仅是调整为较大的块。
您对变量 temp
的使用表明您已意识到此问题,但正如@DerkHermann 首次观察到的那样,您对结果指针的处理不当。也许你想写一些类似
的东西
arr = temp;
相反。然而,即使这样还不够。 C 只有 pass-by-value,所以如果你修改函数参数 arr
的值,该修改只在函数中可见(它在 arr
中接收一个 copy 调用者传递的值)。在 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;
/* ... */
}
添加双指针以在正确的位置重新分配 space,用 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;
}
作为作业,我应该创建 2 个函数,使您能够将元素推入和弹出到充当队列的数组。我们应该动态分配内存。我的程序几乎可以正常工作,但有时在添加和删除太多元素时,会出现 "realloc(): invalid next size"、double free(当我只调用了一次 free 函数时)和一些元素在开头的错误队列设置为 0。例如,如果我先添加 100 个元素,然后删除 90 个并尝试添加另外 20 个,我得到 "free(): invalid next size (fast): 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;
}
也许这不是唯一的问题,但至少下面这行不是您所期望的:
*temp = *arr;
您似乎想用 realloc 的结果替换 arr,将其返回给调用函数(与其他 inout 参数一样)。但是,arr 不是 inout 参数:它是一个整数数组,而不是一个指向整数数组的指针。您实际上对上述代码行所做的是将 arr 的第一个元素复制到新分配的内存范围。然而,新分配的内存范围 temp 却被遗忘了,造成了内存泄漏。
当您扩展或收缩队列的存储空间时,您需要向调用方提供指向存储空间的指针。这是因为 realloc()
不一定会调整内存块的大小 in-place —— 它可能会在其他地方创建一个新的、大小不同的块。即使调整为较小的块,也允许这样做,而不仅仅是调整为较大的块。
您对变量 temp
的使用表明您已意识到此问题,但正如@DerkHermann 首次观察到的那样,您对结果指针的处理不当。也许你想写一些类似
arr = temp;
相反。然而,即使这样还不够。 C 只有 pass-by-value,所以如果你修改函数参数 arr
的值,该修改只在函数中可见(它在 arr
中接收一个 copy 调用者传递的值)。在 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;
/* ... */
}
添加双指针以在正确的位置重新分配 space,用 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;
}