我在应该是一个简单的程序时遇到麻烦。
我使用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;
}
谢谢!
答案 0 :(得分:3)
您没有说明如何检查内存使用情况,但我猜您正在使用ps
或类似内容来查看操作系统为此过程提供了多少内存。
根据您的内存分配器,调用free
可能会也可能不会将内存返回给操作系统。因此,即使您正在调用free
,从操作系统的角度来看,您也不会看到内存占用减少。
分配器可以保留OS提供给它的内存缓存。对malloc
的调用将首先查看此缓存以查看它是否可以找到足够大的块,如果是这样,malloc
可以返回而无需向OS请求更多内存。如果找不到足够大的块,malloc
会向操作系统询问更多内存并将其添加到缓存中。
但free
可能只是将内存添加回缓存,并且永远不会将其返回给操作系统。
所以,你可能正在做的是看到分配器缓存而不是任何内存泄漏。
答案 1 :(得分: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);
}
现在,您将获得动态分配或释放的所有内存的日志。从这里开始,如果泄漏一块内存(程序越复杂,日志越长,这项任务就越困难)就应该相当明显。
答案 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}}