You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 02:20:01