向量中的 Valgrind 内存泄漏

问题描述 投票:0回答:1

现在我有一个非常简单的程序,它将来自 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;
}
c vector valgrind
1个回答
0
投票

编辑: 发布第 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
代码将非常简单,但当填充级别较低时,性能很可能会变差。

© www.soinside.com 2019 - 2024. All rights reserved.