Most convenient way to deallocate multiple memory pointers? - c

I'm looking for most convenient/best/smartest way for multiple memory deallocation. Not quite sure if that is best explanation out there, but here is an example that will show what i want to do:
void func()
{
int* ptr1 = malloc(1);
if(ptr1 == NULL)
{
//...
}
int* ptr2 = malloc(1);
if(ptr2 == NULL)
{
//...
free(ptr1);
}
int* ptr3 = malloc(1);
if(ptr3 == NULL)
{
//...
free(ptr2);
free(ptr1);
}
//code...
}
Only thing that comes up to my mind is array filled with flags, if the flag is raised particular memory has to be freed. Is there any other more convenient way to do this? You can imagine how many times i need to repeat free() if there is need for more malloc()-ing.

What you posted is a common practice for error handling and resource release in a function, you acquire several resources and in case any error occurred, you need to release those that have been acquired prior, there is nothing wrong, just do it one by one.
void func(void) {
void *ptr1 = NULL;
void *ptr2 = NULL;
void *ptr3 = NULL;
ptr1 = malloc(SIZE);
if (!ptr1) goto end;
ptr2 = malloc(SIZE);
if (!ptr2) goto end;
ptr3 = malloc(SIZE);
if (!ptr3) goto end;
// do your work with the buffers
end:
free(ptr1);
free(ptr2);
free(ptr3);
}

You can use an array of pointers and keep count of the number of mallocs done. And use a common free function to free them all. Like,
void func()
{
char* ptr[10];
int n = 0, i;
for(i = 0; i < 10; i++)
ptr[i] = NULL;
ptr[n] = malloc(1);
if(ptr[n] == NULL)
{
//...
}
n++;
ptr[n] = malloc(1);
if(ptr[n] == NULL)
{
//...
custom_free(ptr1, n);
}
n++;
ptr[n] = malloc(1);
if(ptr[n] == NULL)
{
//...
custom_free(ptr, n);
}
n++;
//code...
}
And the custom_free() can be something like,
void custom_free(char* ptr[], int n)
{
int i;
for(i = 0; i <= n; i++)
free(ptr[i]);
}

An alternative way is to allocate all needed memory in one big chunk,
and treat parts of that as p0,p1,p2:
void worker(void)
{
#define N_ELEM 123
int *work_mem;
int *p0,*p1,*p2;
work_mem = malloc ( 3* N_ELEM * sizeof *work_mem);
if (!work_mem) { OMG(); return; }
p0 = work_mem, p1= work_mem + N_ELEM, p2 = work_mem + 2 * N_ELEM;
/* do useful stuff here with work_mem
** , possibly via p0,p1,p2
*/
free(work_mem);
}

Related

Problem freeing memory from multidimensional array in C, free() crashes program

I am creating a deque to store stings in C, and when I call the free() function, the program crashes. I have implemented a similar structure but only storing integers, and encountered no problems, but this seems to be causing me a few. I created a struct containing a multidimensional array or characters, and i think maybe I am not using the pointers correctly? I have searched far and wide and cannot solve it The main area of concern is when i call clear() from the ain body. That in turn calls free(), and the program just stalls. :-( Any help would be extremely useful.
#include <stdio.h>
#define MAX 20 // number of characters for word
typedef struct {
char **deque;
int size;
int pFront;
int pRear;
} deque;
typedef int bool;
enum { false, true };
void initDeque(deque *d, int initialSize)
{
d->size = initialSize;
d->pFront = -1;
d->pRear = -1;
d->deque = (char **)malloc(sizeof(char*)*initialSize);
int idx;
for(int idx = 0; idx < d->size; idx++)
{
d->deque[idx] = (char *)malloc((MAX+1) * sizeof(char));
d->deque[idx] = "";
}
printf("d->size: %zu\n", d->size);
}
void clear(deque *d) {
if(d->pFront == -1)
{
printf("Queue is empty\n");
}
else
{
printf("Attempting to clear...\n");
for(int idx = 0; idx < d->size; idx++)
{
printf("Attempting to clear columns...");
free(d->deque[idx]);
}
printf("Attempting to clear rows...");
free(d->deque);
printf("Freed!!!!\n");
d->deque = NULL;
d->size = 0;
d->pFront = -1;
d->pRear = -1;
}
}
bool isEmpty(deque *d)
{
if(d->pFront == -1){
return true;
}
else
{
return false;
}
}
bool isFull(deque *d)
{
if(d->size == d->pRear+1)
{
return true;
}
else
{
return false;
}
}
void display(deque *d)
{
if(isEmpty(d)){
printf("empty\n");
}
else{
printf("Deque Values:\n");
int idx;
for(int idx = 0; idx <= d->pRear; idx++)
{
printf("Index: %zu\tValue: %s\n", idx, d->deque[idx]);
}
printf("Size: %zu\n", d->size);
}
}
void rAppend(deque *d, char item[]) // as in rear append - same enqueue for queue structure.
{
if(isFull(d))
{
printf("Is Full\n");
int idx;
deque dTemp;
initDeque(&dTemp, d->size);
printf("dTemp Initialised\n");
for(idx = 0; idx < d->size; idx++)
{
dTemp.deque[idx] = d->deque[idx];
}
printf("deque copied to dTemp:\n");
for(idx = 0; idx < d->size; idx++)
{
printf("dTemp[%zu]: %s\n", idx, dTemp.deque[idx]);
}
clear(&d);
printf("d cleared\n");
initDeque(&d, dTemp.size*2);
printf("New deque of double length initialised\n");
for(idx = 0; idx < dTemp.size; idx++)
{
d->deque[idx] = d->deque[idx];
}
printf("dTemp Copied to new deque\n");
clear(&dTemp);
printf("dTemp Cleared\n");
char **tmp = realloc( d->deque, sizeof (d->deque) * (d->size*2) );
if (tmp)
{
d->deque = tmp;
for (int i = 0; i < d->size; i++)
{
d->deque[d->size + i] = malloc( sizeof(char) * MAX );
}
}
}
printf("Appending to rear.. %s\n", item);
d->pRear++;
d->deque[d->pRear] = item;
if(d->pFront == -1)
d->pFront = 0;
}
int main(void)
{
deque d;
initDeque(&d, 5);
rAppend(&d, "when");
rAppend(&d, "will");
rAppend(&d, "wendy");
rAppend(&d, "walk");
rAppend(&d, "with");
display(&d);
clear(&d);
return 0;
}
The problem is your are calling free() on static chain "when", "will",...
You can replace insertion in the function void rAppend(deque *d, char item[]) :
d->deque[d->pRear] = item;
with:
d->deque[d->pRear] = strdup(item);
Doing like this chains are allocated in the heap and free from the heap.
After there is others problems in the code, but it run without crash.
The main problem seems to be that you don't appreciate the difference between copying / assigning pointers and copying / assigning the data to which they point. Secondarily, it seems you may not appreciate the utility of pointers that don't point to anything, especially null pointers. Some details follow.
You are dynamically allocating space for a bunch of strings ...
for(int idx = 0; idx < d->size; idx++)
{
d->deque[idx] = (char *)malloc((MAX+1) * sizeof(char));
... and then leaking all of that space by replacing the pointer to each with a pointer to an empty string literal:
d->deque[idx] = "";
}
As if the leak were not bad enough, you are not permitted to free a string literal or modify its content, which you nevertheless try to do to any of those pointers that remain in the dequeue whenever you clear() it. This is likely the cause of some of your errors.
If you want to set each allocated string to an empty one then modify its content instead of replacing the pointer to it. For example:
d->deque[idx][0] = '\0';
In fact, however, you probably don't need to do even that. You are already performing bookkeeping to know which arrays contain valid (string) data and which don't, and that should be sufficient to do the right thing. Supposing you maintain copies of the strings in the first place.
But that's not all. When you rAppend() elements to your deque you have a similar problem. You create a temporary deque, and then copy the string pointers from your original deque into the temporary:
dTemp.deque[idx] = d->deque[idx];
Not only does this leak the original (empty) data in the temporary deque, it aliases that deque's contents with the main deque's. When you later clear the temporary deque, therefore, you free all the string pointers in the original. Subsequently using or freeing them produces undefined behavior.
Perhaps you instead want to strcpy() all the elements of the main deque into the temp and back, but I suggest instead skipping the temp deque altogether with something along these lines:
void rAppend(deque *d, char item[]) // as in rear append - same enqueue for queue structure.
{
if(isFull(d))
{
printf("Is Full\n");
char **tmp = realloc(d.deque, d->size * 2);
if (tmp)
{
d->deque = tmp;
for (int i = 0; i < d->size; i++)
{
// Copied from the original, but see below
d->deque[d->size + i] = malloc( sizeof(char) * MAX );
}
d->size * 2;
} // else?
}
printf("Appending to rear.. %s\n", item);
d->pRear++;
// Oops, this is another leak / aliasing issue:
d->deque[d->pRear] = item;
if(d->pFront == -1)
d->pFront = 0;
}
The whole point of the temporary deque is lost on me, since the realloc() you need to do preserves the original data anyway (as long as it succeeds, anyway).
Note too, however, that this still has an aliasing issue: you have aliased a deque element with the appended string, and leaked the memory allocated for that element. Furthermore, when you clear the deque, you free that string for everyone holding a pointer to it. Or at least you attempt to do so. You're not permitted to do that to string literals.
I suggest not allocating space in your deque for the individual strings at all, and not freeing it. Continue to use assignment to store elements in your deque, understanding and embracing that these are aliases. This will be more analogous to your implementation for ints.
#include<memory>
#include<iostream>
using namespace std;
struct S {
S() { cout << "make an S\n"; }
~S() { cout << "destroy an S\n"; }
S(const S&) { cout << "copy initialize an S\n"; }
S& operator=(const S&) { cout << "copy assign an S\n"; }
};
S* f()
{
return new S; // who is responsible for deleting this S?
};
unique_ptr<S> g()
{
return make_unique<S>(); // explicitly transfer responsibility for deleting this S
}
int main()
{
cout << "start main\n";
S* p = f();
cout << "after f() before g()\n";
// S* q = g(); // this error would be caught by the compiler
unique_ptr<S> q = g();
cout << "exit main\n";
// leaks *p
// implicitly deletes *q
}

C Memory leak issue

Program is leaking memory and not able to fix it... This Program is reading data from text files and after reading data, it perform certain operation on data during this it leaks memory. Device has very limited memory & flash drive due to this I cannot run memory leaks checking tools.
Please advise to fix the memory leak issue
Please find code snippet below
int LanguageRequiredData(void)
{
char *data=NULL;
int retValue = 0 ;
retValue = GetString_English(&data);
if(retValue>0 && strlen(data)>0)
{
// Do Some Operation
}
if (data!=NULL)
{
Mem_free(data);
data = NULL;
}
}
int GetString_English(char **data)
{
int retValue = 0 ;
retValue = File_LoadContent(LANGSENGFILE,&(*data));
return retValue;
}
int File_LoadContent (char *file, char **content)
{
long size = File_Size(file);
char buf[256]={};
memset(buf,0x00,sizeof(buf));
if (*content)
{
Mem_free(*content);
}
*content = (char*) Mem_alloc((size+1) * sizeof(char));
TFILE * fd; fd=File_Open(file,"r"); if (fd==NULL) return 0;
while (File_Gets(buf,sizeof(buf),fd)!=NULL)
{
strcat(*content,buf);
memset(buf,0x00,sizeof(buf));
}
File_Close(fd); return 1;
}
void * Mem_alloc(size_t size)
{
int i;
void * ptr = NULL;
for (i = 0; i < 2; i++)
{
ptr = malloc(size);
if (ptr)
{
break;
}
}
if (ptr)
{
memset(ptr, 0, size);
}
return ptr;
}
void Mem_free(void * ptr)
{
if (ptr != NULL)
{
free(ptr);
}
ptr = NULL;
}
This part seems suspect:
while (File_Gets(buf,sizeof(buf),fd)!=NULL)
{
strcat(*content,buf);
memset(buf,0x00,sizeof(buf));
}
Specifically the strcat(). Is File_Gets() null-terminating what it writes to buf[]? If not then the strcat() may be reading/copying beyond the bounds of buf[], since it requires null-termination to know when to stop.
EDIT: I should point out that this is one of the reasons to recommend strncat() over strcat(). Using the "n" variants of the string functions (i.e. strncat(), strncpy(), strncmp()) helps prevent buffer overruns and is generally a good practice.

Another way to do cleanup in C?

Consider this program:
int main(void)
{
int* i = malloc(sizeof(int));
int* j = malloc(sizeof(int));
}
However this is a naive approach, because malloc may fail and the pointers are not free'd.
So you can do this:
int main(void)
{
int* i;
int* j;
if ((i = malloc(sizeof(int)) < 0)
{
return -1;
}
if ((j = malloc(sizeof(int)) < 0)
{
free(i);
return -1;
}
free(i);
free(j);
}
However I consider this very error-prone. Consider having to assign 20 pointers, in the last malloc error case, you have to free 19 variables and then return -1.
I also know atexit, which can help me to write it like this:
int* i;
int* j;
void del_i(void)
{
free(i);
}
void del_j(void)
{
free(j);
}
int main(void)
{
if ((i = malloc(sizeof(int)) < 0)
{
return -1;
}
else
{
atexit(del_i);
}
if ((j = malloc(sizeof(int)) < 0)
{
return -1;
}
else
{
atexit(del_j);
}
}
Which is better, but I dislike having to declare all pointers as global. Is there some way to combine these two approaches, basically:
Having destructors for pointers, which can be either executed directly or be used with atexit.
Having pointers local to functions.
free on NULL is defined to be a safe no-op. So a non-jumping variation could be:
int *i = malloc(sizeof(int));
int *j = malloc(sizeof(int));
if(i && j)
{
// do some work
}
free(i);
free(j);
First, this will not detect malloc failure:
if ((i = malloc(sizeof(int)) < 0)
{
return -1;
}
malloc returns NULL on failure, not a negative number.
Second, atexit is good for cleaning up static and global objects. It is not a good idea to make local objects global only to use them inside atexit.
A better approach is to make a struct for all related pointers that you need to allocate in an all-or-nothing unit, define a function for freeing them all at once, and write a function that allocates them one by one with memory checking on each allocation:
typedef struct AllOrNothing {
double *dPtr;
int *iPtr;
float *fPtr;
size_t n;
} AllOrNothing;
void freeAllOrNothing(AllOrNothing *ptr) {
free(ptr->dPtr);
free(ptr->iPtr);
free(ptr->fPtr);
free(ptr);
}
int allocateAllOrNothing(size_t n, AllOrNothing **res) {
*res = malloc(sizeof(AllOrNothing));
if (*res == NULL) {
return -1;
}
// Freeing NULL is allowed by the standard.
// Set all pointers to NULL upfront, so we can free them
// regardless of the stage at which the allocation fails
(*res)->dPtr = NULL;
(*res)->iPtr = NULL;
(*res)->fPtr = NULL;
(*res)->n = n;
(*res)->dPtr = malloc(n*sizeof(double));
if ((*res)->dPtr == NULL) {
free(*res);
*res = NULL;
return -1;
}
(*res)->fPtr = malloc(n*sizeof(float));
if ((*res)->fPtr == NULL) {
free(*res);
*res = NULL;
return -1;
}
(*res)->iPtr = malloc(n*sizeof(int));
if ((*res)->iPtr == NULL) {
free(*res);
*res = NULL;
return -1;
}
return 0;
}
int main(void)
{
int* i = NULL; // Init with NULL otherwise free on none NULL possible
int* j = NULLL;
if (!(i = malloc(sizeof(int)))
{
goto exit;
}
if (!(j = malloc(sizeof(int)))
{
goto exit;
}
...
exit:
free(i);
free(j);
...
return err;
}
This is something you can solve with goto statements.
int main(void)
{
int* i = NULL;
int* j = NULL;
bool success = false;
do {
i = malloc(sizeof(int));
if (NULL == i) break;
j = malloc(sizeof(int));
if (NULL == j) break;
success = true;
} while (0);
if (!success)
{
printf("Something failed!");
}
else
{
printf("All succeeded!");
// Do more work
}
free(i);
free(j);
return (success? 0 : 1);
}
Avoid multiple exit points. Avoid interlacing allocation and error handling. Follows a clean order of operation:
Declare, allocate and initialize resources..
If all successful, do the task.
Clean-up.
Return status.
// Do all allocations first, test their `NULL`-ness, then free them all.
int main(void) {
// Allocate resources
// declare and allocate in one step
int* i = malloc(sizeof *i);
double* j = malloc(sizeof *j);
// Test for acceptability
bool ok = i && j;
// Perform the main body of code
if (ok) {
; // do normal process in the code;
}
// free resources
free(i);
free(j);
// return status
return ok ? 0 : -1;
}
int *i=NULL,*j=NULL;
if(!(i=malloc(sizeof(int))))
goto EXIT;
if(!(j=malloc(sizeof(int))))
goto EXIT;
/* do some work */
return 0;
EXIT:
free(i);
free(j);
exit(EXIT_FAILURE);
Although goto is considered a bad programming practice
but here we can use it to get our task done with ease and simplicity

Segfault when returning an int value;

I know this question might sound quite stupid, but I've tried my best and I can't seem to have solved issues with this code:
struct bacon_statement* statms;// = (struct bacon_statement*)malloc(3000 * sizeof(struct bacon_statement));
int ind = 0;
char** yay = strsplit(code, ";");
char* a = *yay;
while (a != NULL) {
puts(a);
if (strncmp(a, "#", 1)) {
struct bacon_statement statm;
int valid = bacon_make_statement(a, statm);
if (valid != 0) { return valid; }
statms[sta] = statm;
}
sta++;
*(yay)++;
a = *yay;
}
puts("Running BACON INTERNAL MAKE");
int ret = bacon_internal_make(statms, internal);
printf("%d\n", ret);
return ret;
It segfaults when returning, since it the printf is executed alright., and another printf call /after/ the function is called (on int main()) doesn't print anything at all.
I'm sorry if this sounds too specific, but I don't know where else to get help.
Apparently you are corrupting the stack somehow.
your line does not initialize the pointer:
struct bacon_statement* statms;// = (struct bacon_statement*)malloc(3000 * sizeof(struct bacon_statement));
but you write to it like this, which will have undefined behavior:
statms[sta] = statm;
Easiest way to fix is probably to restore the malloc (provided the size is correct)
struct bacon_statement* statms = (struct bacon_statement*)malloc(3000 * sizeof(struct bacon_statement));
Seems like a stack overrun, you keep assigning data to the buffer (statms) without checking for boundaries. Sooner or later, the return address (which is on the stack) will be overridden and when return is reached, the address to return to will be corrupted. and there is your segmentation fault.
Try doing something like:
int count = 0;
char *ptr = strchr(code, ';');
while (ptr) {
count++;
ptr = strchr(ptr + 1, ';');
}
to predict the number of statements, and then:
struct bacon_statement* statms = (struct bacon_statement*) malloc(
count * sizeof(struct bacon_statement));
to allocate enough bacon_statement slots. Alternatives are probably more difficult: a linked list or other mutable structure; or using realloc to increase the size of the array, keeping a note of how many sots you have left.
The mistake can still be in the other functions though!
The code does too much. Let me summarise:
struct bacon_statement* statms;// = (struct bacon_statement*)malloc(3000 * sizeof(struct bacon_statement));
int ind = 0;
char** yay = strsplit(code, ";");
char* a;
while ((a = *yay)) {
puts(a);
if (strncmp(a, "#", 1)) {
struct bacon_statement statm;
int valid = bacon_make_statement(a, statm);
if (valid != 0) { return valid; }
statms[sta] = statm;
}
sta++;
*(yay)++;
}
puts("Running BACON INTERNAL MAKE");
int ret = bacon_internal_make(statms, internal);
printf("%d\n", ret);
return ret;
But we can get more compact than that:
struct bacon_statement* statms;// = (struct bacon_statement*)malloc(3000 * sizeof(struct bacon_statement));
int ind = 0;
char** yay;;
char* a;
for (yay = strsplit(code, ";"); (a = *yay); *(yay)++ ) {
puts(a);
if (strncmp(a, "#", 1)) {
struct bacon_statement statm;
int valid = bacon_make_statement(a, statm);
if (valid != 0) { return valid; }
statms[sta] = statm;
}
sta++;
}
puts("Running BACON INTERNAL MAKE");
int ret = bacon_internal_make(statms, internal);
printf("%d\n", ret);
return ret;
Now let's remove the silly stringcompare:
struct bacon_statement* statms;// = (struct bacon_statement*)malloc(3000 * sizeof(struct bacon_statement));
int ind = 0;
char** yay;
char* a;
for (yay = strsplit(code, ";"); (a = *yay); *(yay)++ ) {
puts(a);
if (*a != '#') {
struct bacon_statement statm;
int valid = bacon_make_statement(a, statm);
if (valid != 0) { return valid; }
statms[sta] = statm;
}
sta++;
}
puts("Running BACON INTERNAL MAKE");
int ret = bacon_internal_make(statms, internal);
printf("%d\n", ret);
return ret;
Still does not make sense. IMO the OP wants to loop trough an array of string-pointers (yay) and process each string in it. Especially the *(yay)++ looks awkward.
Maybe with the '#' he wants to skip comments. I'd expect something like:
sta=0;
for (yay = strsplit(code, ";"); (a = *yay); yay++ ) {
int err;
if (*a == '#') continue;
/* make bacon from a */
err = bacon_make_statements(a, statms[sta] );
if (err) return err;
sta++; /* could overflow ... */
}
/* you need the number of assigned struct members ("sta") to this function */
return bacon_internal_make(statms,sta internal);
On second thought, my guess is that the strsplit() function return a pointer to an automatic ("stack") variable. Or the *yay variable is incremented beyond recognition. Or the statms[] array is indexed out of bounds.
Is it possible that you meant to use ind instead of sta as the array index variable and sta is uninitialized?

C Linked List Memory Usage (Possible memory leak)

I'm having trouble with what should be a simple program.
I've written a single linked list implementation in C using void* pointers. However, I have a problem, as there is a possible memory leak somewhere, however I checked the code using valgrind and it detected no such errors.
But when all the memory is free'd there is still some memory un-freed (see comments)... I tried passing everything to the add function by reference too, but this didn't fix the issue either.
I just wondered if anyone here had any comments from looking at the code. (This should be simple!, right?)
/*
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;
}
Thanks!
You don't say how you are checking memory usage, but I'm going to guess that you are using ps or something similar to see how much memory the OS has given the process.
Depending on your memory allocator, calling free may or may not return the memory to the OS. So even though you are calling free, you will not see the memory footprint decrease from the OS's point of view.
The allocator may keep a cache of memory that is given to it by the OS. A call to malloc will first look in this cache to see if it can find a big enough block and if so, malloc can return without asking the OS for more memory. If it can't find a big enough block, malloc will ask the OS for more memory and add it to it's cache.
But free may simply add the memory back to the cache and never return it to the OS.
So, what you may be doing is seeing the allocators cache and not any memory leak.
As was mentioned, I would not trust the memory usage reported by the task manager as there are other factors beyond your control that impact it (how malloc/free are implemented, etc).
One way you can test for memory leaks is by writing your own wrapper functions around the existing malloc and free functions similar to:
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);
}
Now, you will get a log of all memory that is dynamically allocated or freed. From here, it should be fairly obvious if you leak a block of memory (the more complex your program is, the longer your log will be and the more difficult this task will be).
Your program contains incorrect argv in main, incorrect usage of strncat, and strange memory allocation. Some of these should of shown up as warnings. The argv is a non-issue, but if the others showed up as warning, you needed to heed them. Don't ignore warnings.
These changes clean it up. The biggest thing was that you don't seem to have a good grasp on the NUL ('\0') character (different than NULL pointer) used to terminate C strings, and how that effects str(n)cat.
The mixed usage of str* functions with memory functions (*alloc/free) was likely part of the confusion. Be careful.
#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);
}

Resources