现在我有一个非常简单的程序,它将来自 stdin 的输入字符串添加到一个名为 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
时,它给了我以下内存泄漏信息,
==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,
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++;
}
我真的很难找出内存泄漏的来源。我在哪里没有正确释放任何东西?请帮助。
编辑:
添加 vect_remove_first
/** 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;
}
编辑: 发布第 1 部分后,我发现了另一个更有可能的内存泄漏。第 1 部分仍然相关,但也许先阅读第 2 部分。
第 1 部分:
发布的代码不完整,因此无法完全检查代码,但这是一个潜在的问题。
如果
realloc
失败,使用realloc
可能会导致内存泄漏。
当/如果
realloc
无法分配请求的内存,它返回NULL并保持原始内存不变,包括“仍然分配”。所以使用realloc
的安全方法是:
void * tmp = realloc(ptr, size);
if (tmp != NULL)
{
ptr = tmp;
}
else
{
// Error handler....
}
在代码后面的某个地方做
free(ptr);
贴出的代码中有一些
realloc
将返回值直接保存到原始指针中,e.g.
v->data[i-1] = realloc(v->data[i-1], strlen(v->data[i])+1);
这可能会导致内存泄漏,除非您在调用
v->data[i-1]
时知道realloc
为NULL。但是,如果已知,您应该使用 malloc
来使代码更清晰。
在此代码块中:
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;
}
您似乎尝试对
tmp
使用realloc
指针。但这是错误的方式。返回值存储在 v->data
中,如果为 NULL,则函数返回而不释放 tmp
。再次是潜在的内存泄漏。你打算做吗if (v->data == NULL) { free(tmp); return; }
除此之外,在这种情况下使用 return 似乎是错误的。稍后执行的代码可能会取消引用 NULL 指针并崩溃。也许你想要
exit(FAILURE)
而不是return
第 2 部分:
快速浏览
vect_remove_first
让我怀疑...
移除元素的函数怎么可能没有调用
free
?
这似乎是一个导致泄漏的错误。它是...
如果向量已满,即
data
中的所有指针都是非空的,您需要在最后一个元素上调用free
。
此外,使用“完整”向量,您可以在执行
v->data[i] = NULL;
时越界访问,因为 i
将等于 v->size
就是说......你为什么要做那些
realloc
和strcpy
?这是复杂而缓慢的方式。你不需要它!
你有一个指针数组,所以你可以移动指针值。
原则上是这样的:
free(v->data[0]);
v->data[0] = v->data[1];
v->data[1] = v->data[2];
...
v->data[v->size - 2] = v->data[v->size - 1];
v->data[v->size - 1] = NULL;
真正的实现将是一个循环或者一个
memmove
。使用 memmove
代码将非常简单,但当填充级别较低时,性能很可能会变差。