0

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

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;
}

谢谢!

4

3 回答 3

3

你没有说你是如何检查内存使用情况的,但我会猜测你正在使用ps或类似的东西来查看操作系统为进程提供了多少内存。

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

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

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

因此,您可能正在做的是查看分配器缓存而不是任何内存泄漏。

于 2010-03-05T20:23:50.540 回答
0

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

malloc测试内存泄漏的一种方法是围绕现有的和free类似于以下的函数编写自己的包装函数:

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);
}

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

于 2010-03-05T21:31:03.020 回答
0

您的程序包含不正确argv的 main、不正确的使用strncat和奇怪的内存分配。其中一些应该显示为警告。argv 不是问题,但如果其他人显示为警告,您需要注意他们。不要忽视警告。

这些更改将其清理干净。最重要的是,您似乎没有很好地掌握用于终止 C 字符串的 NUL ('\0') 字符(不同于NULL 指针),以及它的影响str(n)cat

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

 #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);
 }
于 2010-03-05T21:53:29.407 回答