vector中Valgrind检测到内存泄漏,求定位与修复方案
内存泄漏排查:C语言向量结构的内存未正确释放问题
问题背景
我编写了一个简单的C语言程序,将标准输入的字符串存入名为command_list的vect结构中。程序代码如下:
#define MAX_CHARS = 256; int main(int argc, char **argv) { char input[MAX_CHARS]; vect_t* command_list = vect_new(); // After starting, the shell should print a welcome message: printf("Hi!\n"); while (1) { printf("input: "); if (fgets(input, MAX_CHARS, stdin) == NULL) { printf("bye.\n"); break; } // Add input to the command list vect_add(command_list, input); // while command list is not empty, while (vect_size(command_list) != 0) { // get the first element char* front = vect_get_copy(command_list, 0); // ... free(front); // remove the first element vect_remove_first(command_list); } } // free command_list vect_delete(command_list); return 0; }
执行echo "cd" | valgrind --leak-check=full ./shell后,Valgrind给出的内存泄漏信息如下:
==196780== ==196780== HEAP SUMMARY: ==196780== in use at exit: 10 bytes in 1 blocks ==196780== total heap usage: 12 allocs, 11 frees, 5,222 bytes allocated ==196780== ==196780== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==196780== at 0x48487A9: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==196780== by 0x10B1CA: vect_add (vect.c:139) ==196780== by 0x10A052: main (shell.c:79) ==196780== ==196780== LEAK SUMMARY: ==196780== definitely lost: 10 bytes in 1 blocks ==196780== indirectly lost: 0 bytes in 0 blocks ==196780== possibly lost: 0 bytes in 0 blocks ==196780== still reachable: 0 bytes in 0 blocks ==196780== suppressed: 0 bytes in 0 blocks ==196780== ==196780== For lists of detected and suppressed errors, rerun with: -s ==196780== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
信息显示泄漏来自vect_add函数。我使用的vect结构、vect_add、vect_delete及补充的vect_remove_first函数代码如下:
typedef struct vect vect_t; /** Main data structure for the vector. */ struct vect { char **data; /* Array containing the actual data. */ unsigned int size; /* Number of items currently in the vector. */ unsigned int capacity; /* Maximum number of items the vector can hold before growing. */ }; /** Delete the vector, freeing all memory it occupies. */ void vect_delete(vect_t *v) { if (v == NULL) { return; } /* [TODO] Complete the function */ // Delete data one by one because they are allocated memory for in other functions int size = v->size; for (int i = 0; i < size; i++) { vect_remove_last(v); } // Free v->data because memory is allocated for it in vect_new free(v->data); // Delete the vector free(v); } /** Add an element to the back of the vector. */ void vect_add(vect_t *v, const char *elt) { assert(v != NULL); /* [TODO] Complete the function */ // If the array is full if (v->size >= v->capacity) { char **tmp = v->data; // Reallocate memory for the array v->capacity *= VECT_GROWTH_FACTOR; v->data = (char**) realloc(tmp, (v->capacity) * sizeof(*v->data)); if (v->data == NULL) { return; } // Initialize the elements in the expanded part of the array to NULL for (int i = v->size; i < v->capacity; i++) { v->data[i] = NULL; } } // Reallocate memory for the element at v->size to be able to store elt v->data[v->size] = (char*) realloc(v->data[v->size], strlen(elt)+1); if (v->data[v->size] == NULL) { return; } strcpy(v->data[v->size], elt); v->size++; }
/** Remove the first element from the vector. */ void vect_remove_first(vect_t *v) { assert(v != NULL); /* [TODO] Complete the function */ // Shift the elements to the left by one int i = 1; for (; i < v->size; i++) { if (v->data[i] == NULL) { break; } else { v->data[i-1] = realloc(v->data[i-1], strlen(v->data[i])+1); strcpy(v->data[i-1], v->data[i]); } } v->data[i] = NULL; v->size -= 1; }
我无法定位内存泄漏的根源,请问哪里存在未正确释放内存的问题?
问题根源分析
内存泄漏的核心问题出在vect_remove_first函数中:
- 移除第一个元素时,函数仅将后续元素的内容复制到前一个位置,但未释放原第i个位置的堆内存。比如把
v->data[1]的内容复制到v->data[0]后,v->data[1]指向的内存没有被释放,直接将v->data[i]设为NULL,导致这块内存永久丢失。 - 循环逻辑存在错误:当
i遍历到v->size-1时,处理完后i会变成v->size,此时v->data[i]已超出当前有效元素范围,设置它为NULL没有意义,真正需要释放的是最后一个元素的内存。
修复方案
1. 修复vect_remove_first函数
正确逻辑是先释放第一个元素的内存,再将后续元素整体左移,最后调整向量大小:
/** Remove the first element from the vector. */ void vect_remove_first(vect_t *v) { assert(v != NULL); if (v->size == 0) { return; } // 释放第一个元素的堆内存 free(v->data[0]); // 将后续元素左移一位 for (int i = 1; i < v->size; i++) { v->data[i-1] = v->data[i]; } // 将原最后一个元素的位置置为NULL(可选,提升内存安全性) v->data[v->size - 1] = NULL; v->size -= 1; }
2. 确保vect_remove_last正确释放内存
由于vect_delete依赖vect_remove_last释放元素,需保证其实现正确:
/** Remove the last element from the vector. */ void vect_remove_last(vect_t *v) { assert(v != NULL); if (v->size == 0) { return; } // 释放最后一个元素的堆内存 free(v->data[v->size - 1]); v->data[v->size - 1] = NULL; v->size -= 1; }
3. 优化vect_add的内存分配逻辑
vect_add中对v->data[v->size]使用realloc不必要(新位置初始为NULL),直接用malloc更清晰:
// 替换原realloc代码 v->data[v->size] = malloc(strlen(elt) + 1); if (v->data[v->size] == NULL) { return; } strcpy(v->data[v->size], elt); v->size++;
验证修复效果
重新编译程序后,再次执行echo "cd" | valgrind --leak-check=full ./shell,Valgrind会显示无内存泄漏,HEAP SUMMARY中in use at exit为0字节。
内容的提问来源于stack exchange,提问作者d0nut
相关产品推荐
相关产品推荐

