C链接列表内存使用(可能的内存泄漏)

时间:2010-03-05 19:58:32

标签: c free malloc linked-list

我在应该是一个简单的程序时遇到麻烦。

我使用void*指针在C中编写了一个链表实现。但是,我有一个问题,因为某处可能存在内存泄漏,但是我使用valgrind检查了代码并且没有检测到这样的错误。

但是当所有内存都是free时,仍有一些内存未释放(请参阅注释)...我尝试通过引用将所有内容传递给add函数,但是这并没有修复问题。

我只是想知道这里是否有人通过查看代码有任何意见。 (这应该很简单!,对吗?)

/*
 Wrapping up singley linked list inside a struct
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* Needed for: memcpy */

void waitKey(){
 printf("Press any key to continue...");
 getchar();
}

/* Define a structure for a list entry */
struct ListEntry {
 void* data;
 struct ListEntry* pNext;
};

/* Struct for list properties */
struct ListProperties {
 struct ListEntry* g_pLast;
 struct ListEntry* g_pHead;
 struct ListEntry* pCurrent;
 unsigned int size;
 int getHead;
};

/* Add:
 args: list, data, dyn (0 if not, else size of dynamic data)
*/
void add(struct ListProperties* l, void* d, unsigned long dyn) {
 struct ListEntry* pNew = malloc(sizeof(struct ListEntry));

 /* Set the data */
 if (dyn > 0){
  /* Allocate and copy array */
  pNew->data = malloc(dyn);
  pNew->data = memcpy(pNew->data,d,dyn);
 } else {
  pNew->data = d;
 }

 /* Set last element to point to new element */
 if (l->g_pLast != NULL){
  l->g_pLast->pNext = pNew;

  /* Get head of list */
  if (l->g_pHead == NULL && l->getHead == 0){
   l->g_pHead = l->g_pLast;
   l->getHead = 1;
  }
 } else {
  /* 1 elem case */
  l->g_pHead = pNew;
  l->pCurrent = pNew;
 }

 /* New element points to NULL */
 pNew->pNext = NULL;

 /* Save last element for setting 
    pointer to next element */
 l->g_pLast = pNew;

 /* Inc size */
 l->size++;
}

/* Create new list and return a pointer to it */
struct ListProperties* newList(){
 struct ListProperties* nList = malloc (sizeof(struct ListProperties));
 nList->g_pHead = NULL;
 nList->g_pLast = NULL;
 nList->getHead = 0;
 nList->size = 0;
 return nList;
}

/* Reset pointer */
int reset(struct ListProperties *l){
 if (l->g_pHead != NULL){
  l->pCurrent = l->g_pHead;
  return 0;
 }
 return -1;
}

/* Get element at pointer */
void* get(struct ListProperties *l) {
 if (l->size > 0){
  if (l->pCurrent != NULL){
   return l->pCurrent->data;
  }
 }
 return NULL;
}

/* Increment pointer */
int next(struct ListProperties *l){
 if (l->pCurrent->pNext != NULL){
  l->pCurrent = l->pCurrent->pNext;
  return 1;
 }
 return 0;
}

/* Get element at n */
void* getatn(struct ListProperties *l, int n) {
 if (l->size > 0){
  int count = 0;
  reset(l);
  while (count <= n){
   if (count == n){
    return l->pCurrent->data;
    break;
   }
   next(l);
   count++; 
  }
 }
 return NULL;
}

/* Free list contents */
void freeList(struct ListProperties *l){
 struct ListEntry* tmp;

 /* Reset pointer */
 if (l->size > 0){
  if (reset(l) == 0){
   /* Free list if elements remain */
   while (l->pCurrent != NULL){
    if (l->pCurrent->data != NULL)
     free(l->pCurrent->data);
    tmp = l->pCurrent->pNext;
    free(l->pCurrent);
    l->pCurrent = tmp;
   }
  }
 }

 l->g_pHead = NULL;
 l->g_pLast = NULL;

 l->size = 0;
 l->getHead = 0;

 free(l);
}

void deleteElem(struct ListProperties *l, int index){
 struct ListEntry* tmp;
 int count = 0;
 if (index != 0)
  index--;
 reset(l);
 while (count <= index){
  if (count == index){ // Prev element
   if (l->pCurrent != NULL){
    if (l->pCurrent->pNext != NULL){
     free(l->pCurrent->pNext->data); // Free payload
     tmp = l->pCurrent->pNext;
     l->pCurrent->pNext = l->pCurrent->pNext->pNext;
     free(tmp);
     if (l->size > 0)
      l->size--;
    } else {
     // Last element
     free(l->pCurrent->data);
     free(l->pCurrent);
     l->g_pHead = NULL;
     l->g_pLast = NULL;
     l->getHead = 0;
     l->size = 0;
    }
   }
   break;
  }
  if (next(l) != 1)
   break;
  count++;
 }
}

int size(struct ListProperties *l){
 return l->size;
}

int main( int argc, char* argv )
{
 int j = 0;
 unsigned long sz = 0;

 /*=====| Test 1: Dynamic strings |=====*/

 /* Create new list */
 struct ListProperties* list = newList();

 if (list == NULL)
  return 1;

 char *str;
 str = malloc(2);
 str = strncat(str,"A",1);
 sz = 2;

 printf("Dynamic Strings\n===============\n");

 /* Check memory usage here (pre-allocation) */
 waitKey();

 /* Add to list */
 for (j = 0; j < 10000; j++){
  add(list,(char*)str, sz);
  str = realloc(str, sz+2);

  if (str != NULL){
   str = strncat(str,"a",1);
   sz++;
  }
 }

 /* Allocated strings */
 waitKey();

 /* TESTING */
 freeList(list);
 free(str);

 /* Check memory usage here (Not original size!?) */
 waitKey();
 return 0;
}

谢谢!

3 个答案:

答案 0 :(得分:3)

您没有说明如何检查内存使用情况,但我猜您正在使用ps或类似内容来查看操作系统为此过程提供了多少内存。

根据您的内存分配器,调用free可能会也可能不会将内存返回给操作系统。因此,即使您正在调用free,从操作系统的角度来看,您也不会看到内存占用减少。

分配器可以保留OS提供给它的内存缓存。对malloc的调用将首先查看此缓存以查看它是否可以找到足够大的块,如果是这样,malloc可以返回而无需向OS请求更多内存。如果找不到足够大的块,malloc会向操作系统询问更多内存并将其添加到缓存中。

free可能只是将内存添加回缓存,并且永远不会将其返回给操作系统。

所以,你可能正在做的是看到分配器缓存而不是任何内存泄漏。

答案 1 :(得分:0)

如上所述,我不相信任务管理器报告的内存使用情况,因为您无法控制的其他因素会影响它(如何实现malloc / free等)。

您可以测试内存泄漏的一种方法是在现有mallocfree函数周围编写自己的包装函数,类似于:

void* my_malloc(size_t len) {
    void* ptr = malloc(len);
    printf("Allocated %u bytes at %p\n", len, ptr);
    return ptr;
}

void my_free(void* ptr) {
    printf("Freeing memory at %p\n", ptr);
    free(ptr);
}

现在,您将获得动态分配或释放的所有内存的日志。从这里开始,如果泄漏一块内存(程序越复杂,日志越长,这项任务就越困难)就应该相当明显。

答案 2 :(得分:0)

您的程序包含argv主要错误strncat,错误使用NULL以及奇怪的内存分配。其中一些应该显示为警告。 argv不是问题,但如果其他人出现警告,你需要注意它们。不要忽视警告。

这些更改会将其清理干净。最重要的是你似乎没有很好地掌握用于终止C字符串的NUL('\ 0')字符(不同于str(n)cat 指针),以及如何影响 #include <assert.h> ... int main( int argc, char* argv[] ) /* or int main(void) */ ... sz = 2; str = (char*) malloc(sz); /* allocate 2 bytes, shortest non-trivial C string */ assert(str != NULL); strncpy(str, "A", sz); /* copy 'A' and '\0' into the memory that str points to */ ... /* Add to list */ for (j = 0; j < 10000; j++){ add(list, str, sz); str = realloc(str, ++sz); /* realloc str to be one (1) byte larger */ assert(str != NULL); strncat(str, "a", sz - strlen(str)); /* now insert an 'a' between last 'A' or 'a' and '\0' */ assert(str != NULL); }

str *函数与内存函数(* alloc / free)的混合使用可能是混淆的一部分。小心。

{{1}}