Can't free memory from struct member variables [duplicate] - c

This question already has answers here:
Why doesn't free(p) set p to NULL?
(9 answers)
Closed 5 years ago.
After I call free on a struct variable in C, and then check if that variable is NULL, it shows me that it is NOT NULL. So free did not work? My code:
struct main_struct {
struct my_file *file;
};
struct my_file {
char *host;
int port; // this cannot be malloc'ed and free'ed
};
struct my_file* read_file() {
FILE *f;
struct my_file *file;
//open FILE for reading..
file = malloc(sizeof(struct my_file)); //allocate memory for my_file struct
memset(file, 0, sizeof(struct my_file));
// while loop to read file content..
// in the loop:
char *value = line;
file->host = malloc(sizeof(char) * strlen(value)); //allocate memory for host member variable
strncpy(file->host, value, strlen(value)); //assign value to host variable
return file;
}
int main() {
struct main_struct *mstr;
mstr = malloc(sizeof(struct main_struct)); //allocate memory to main_struct
memset(mstr, 0, sizeof(struct main_struct));
mstr->my_file = read_file(); //call to read file, allocate mem for my_file struct and the 'host' member variable
// some code
// call free here:
if(mstr->my_file->host != NULL) {
free(mstr->my_file->host);
}
// check if mem has been freed:
if(mstr->my_file->host == NULL) {
printf("mstr->my_file->host is NULL, good.\n");
} else {
printf("mstr->my_file->host is NOT NULL, bad.\n"); // I see this.
}
// I also try to free mstr->my_file:
if(mstr->my_file != NULL) {
free(mstr->my_file);
}
// check if mem has been freed:
if(mstr->my_file == NULL) {
printf("mstr->my_file is NULL, good.\n");
} else {
printf("mstr->my_file is NOT NULL, bad.\n"); // I see this.
}
// and also mstr itself..
}
Am I using the free function correctly, because I have seen examples where free has been called like this:
free(&mystruct->myfile->host); by sending the address of the pointer to free. But I think that the way I am calling free now, is correct..?

free(x) doesn't set x no NULL automatically, it just deallocates the memory and leaves x pointing to an invalid location. If you want to free x you can use a function like
void clear(void** ptr) { free(*ptr); *ptr = NULL; }
...
free(&(mstr->my_file->host));
Or you can do it manually each time. The comma operator can help here:
mstr->my_file->host = (free(mstr->my_file->host), NULL);
Edit: if you happen to be using glib (and its memory management wrappers), there is g_clear_pointer and g_clear_object to help with this.

free(&foo) is always wrong. You can only free pointer values that were returned from malloc / calloc / realloc (and wrappers such as strdup). &foo is the address of an existing variable (managed by the compiler).
free(ptr) will not set ptr to NULL. In general, a function call f(x) cannot modify x because C passes arguments by value. It will simply release the memory behind ptr, without touching ptr itself.
free(ptr) is a bit of a special case, because afterwards the value of ptr is indeterminate, which means your if(mstr->my_file->host != NULL) check actually has undefined behavior (looking at an indeterminate value is not allowed).
See also http://c-faq.com/malloc/ptrafterfree.html.
Random comments:
Never use strncpy. It is not a string function (in the sense that it doesn't work with or produce C strings), and its behavior will bite you at some point.
Multiplying by sizeof (char) is pointless: sizeof (char) is 1 by definition.
malloc + memset can be combined by using calloc to get zero-initialized memory. (In some cases calloc is also much faster than malloc/memset.)

Related

Segment Fualt when I pass a ptr to a function after I malloc a memory, but if I didn't malloc, it was fun?

this is a double circue link. dev_num, blk_num and status are not issued about my question.
typedef struct buf_node {
unsigned dev_num;
unsigned blk_num;
unsigned status:2;
struct buf_node *ptr_prev_free_q;
struct buf_node *ptr_next_free_q;
} stc_buf_header;
this is a buffer node which contented a identity buf_header and a data field
typedef struct {
stc_buf_header *buf_header;
stc_mm *data_area;
} stc_buffer;
this is the double circue link which holds the free buffer node waiting for use(like UNIX)
typedef struct free_lk_node {
stc_buffer *lk_header;
unsigned len;
} stc_free_lk;
AND, now I define a function to init the double circue link
int init(stc_buffer *lk_header, unsigned dev_num, unsigned blk_num){
printf("buf_header\t%p\n", lk_header->buf_header);
printf("lk_header\t%p\n", lk_header);
printf("dev_num\t%d\n", lk_header->buf_header->dev_num);
printf("blk_num\t%d\n", lk_header->buf_header->blk_num);
lk_header->buf_header->dev_num = dev_num;
lk_header->buf_header->blk_num = blk_num;
lk_header->buf_header->status = 0x0;
lk_header->buf_header->ptr_next_free_q = lk_header->buf_header;
lk_header->buf_header->ptr_prev_free_q = lk_header->buf_header;
lk_header->data_area->data = -1; // means that this node is header of the link
}
the printf() statement was used to debug when I got this bug.
OK, now,if I write like this in main()
int main(){
stc_free_lk *link = (stc_free_lk*)malloc(sizeof(stc_free_lk));
init(link->lk_header, (unsigned)0, (unsigned)0);
return 0;
}
when it runs to the point at printf() statement, there will raise a Segment Fualt error
However, if I write like this in main()
int main(){
stc_free_lk link;
init(link.lk_header, (unsigned)0, (unsigned)0);
return 0;
}
EVERYTHING IS OK. WHY??, it makes me confoused...(-o-)#
First of all, use %p for printing addresses, not %d.
And your problem is that malloc just allocates memory, it does not initialize it. And you malloc call only initialized the memory for a struct stc_free_lk object, it does not allocate memory the members that are pointers, you need to allocate memory for them as well.
So lk_header->buf_header is pointing to nowhere, you cannot dereference it, it's undefined behaviour.
What you are seeing is a classic case of undefined behaviour.
Like I said, you have to initialize the member of the struct before you can access them.
You could use calloc instead of malloc because calloc sets the allocated
memory to 0, this helps when initializing pointers in structs.
stc_free_lk *link = calloc(1, sizeof *link);
if(link == NULL)
{
fprintf(stderr, "Not enough memory\n");
return 1;
}
link->header = calloc(1, sizeof *link->header);
if(link->header == NULL)
{
fprintf(stderr, "Not enough memory\n");
free(link);
return 1;
}
init(link->lk_header, (unsigned)0, (unsigned)0);
...
Then in init you should also allocate memory for lk_header->buf_header and
lk_header->data_area and so on.
I suggest that you write a function that allocates all the memory for all the
pointers so that you don't have to allocate memory at different places. That
makes the code hard to follow and hard to find bugs. Also I'd write a destroy
function that frees all allocated memory, again all in one place.

How would I free a pointer malloc'd in a separate function?

I have a global variable called exam which is of type struct Exam:
typedef struct
{
Question* phead;
}Exam;
Exam exam;
In a function I malloc space for the pointer phead:
int initExam()
{
exam.phead = malloc(sizeof(Question*));
exam.phead = NULL;
return 1;
}
In a separate function I try to free this memory:
void CleanUp()
{
unsigned int i = 0;
Question* currentQuestion = exam.phead;
while (currentQuestion != NULL) {
// some other code
}
exam.phead = NULL;
}
I have also tried the following inside my function:
free(exam.phead);
My issue is it does not seem to free the memory allocated by malloc. I would like CleanUp() to free up the memory allocated by exam.phead and I cannot change the function signatures or move the free() calls to another function. Is there something I'm doing wrong? I'm fairly new to C programming. Thanks!
You have a memory leak, right from the off:
int initExam()
{
exam.phead = malloc(sizeof(Question*));//assign address of allocated memory
exam.phead = NULL;//reassign member, to a NULL-pointer
return 1;
}
the exam.phead member is assigned the address of the memory you allocated, only to become a null-pointer right after. A null pointer can be free'd safely, but it doesn't do anything.
Meanwhile, the malloc'ed memory will stay allocated, but you have no pointer to it, and therefore cannot manage it. You can't free the memory, nor can you use it. I take it the NULL assignment is an attempt to initialize the memory to a "clean" value. There are ways to to this, which I'll get to in a moment.
Anyway, because phead is NULL, the following statements:
Question* currentQuestion = exam.phead;//is the same as currentQuestion = NULL;
while (currentQuestion != NULL) //is the same as while(0)
don't make sense, at all.
To initialize newly allocated memory, either use memset, or calloc. The latter initializes the allocated block of memory to zero, memset can do this do (calloc is basically the same as calling malloc + memset), but allows you to initialize to any value you like:
char *foo = calloc(100, sizeof *foo);// or calloc(100, 1);
//is the same as writing:
char *bar = malloc(100);
memset(bar, '\0', 100);
You're setting exam.phead in initExam to NULL right after you allocate memory with malloc. free() doesn't do anything with a NULL pointer so you're leaking memory.

memory free throws segFault

I am implementing symbol table using link list, The code works fine but there is memory leak in code,
I have following structure
struct node
{
char* pcKey;
void* pvValue;
struct node *next;
};
struct _Sym
{
int totalBindings;
struct node *node;
};
add I have sym_new method to allocate memory for sym instance
sym Sym_new (void)
{
_Sym *m_SymTable_t = (_Sym*) malloc (sizeof(_Sym));
if(m_SymTable_t == NULL)
{
return NULL;
}
else
{
m_SymTable_t->totalBindings = 0;
m_SymTable_t->node = NULL;
return m_SymTable_t;
}//endif
}
I am allocating memory for key and value in other function based on the string length.
The free method is
typedef struct _Sym *Sym;
void Sym_free (Sym m_SymTable_t)
{
assert(m_SymTable_t != NULL);
struct node* temp = m_SymTable_t->node;
struct node *currentBinding = NULL;
while(temp != NULL)
{
currentBinding = temp;
temp = temp -> next;
//Removing comment for the below line throws segfault
//free(currentBinding -> pcKey);
//free(currentBinding -> pvValue);
free(currentBinding);
}
free(m_SymTable_t);
}
What is proper way to free the sym completely?
I have uploaded my symTable_Link.cpp file at link
The variables pcKey and pvValue should probably be initialised to null in the Sym_new() function. Otherwise they may contain any old value. This is because malloc doesn't necessarily zero the memory allocated: it just allocates a chunk of memory and the memory could therefore be filled with junk.
So, if for some reason sym_put() is not called for the newly created object these pointers could point to invalid memory and upon your call to free() segfault. If you initialise them to null free() will just ignore them and won't try to free the memory.
A "hacky" DEBUG-only technique you could use to check that the pcKey and pvValue variables are definitely allocated by a sym_put call would be to initialise them in sym_new with a dummy value, for example 0xCDCDCDCD (careful about pointer-widths here... this is why I'm calling this a hacky technique). Then in sym_free check for this magic constant before freeing pcKey and pvValue. If you find it, there's the problem...
Also of interest may be the thread Do I cast the result of malloc?
EDIT:
Looked at the code linked and you appear to be discarding const!
The function id defined as:
int SymTable_put (SymTable_t m_SymTable_t, const char *pcKey, const void *pvValue)
But then does this cast...
temp->pcKey = (char*)pcKey;
temp->pvValue = (char*)pvValue;
This is a bad idea. You're "fooling" the compiler into invalidating your const promise.
THE BUG:
Ok, so you allocate as follows
temp->pcKey = (char*) malloc (sizeof(char) * strlen (pcKey));
But then you overwrite this pointer using
temp->pcKey = (char*)pcKey;
So you a) have a memory leak and b) have just stashed the wrong pointer, which is probs why you get the segfault. You you probably meant to do instead is (strdup is useful here)...
temp->pcKey = strdup(pcKey);
This will allocate new memory for the string in pcKey and COPY the string into the new memory.
I would hazzard a guess you called the function like this...
SymTable_put (xxx, "KEY string", "VALUE string");
Then your code did this
temp->pcKey = (char*)malloc (sizeof(char) * strlen (pcKey));
...
temp->pcKey = (char*)pcKey;
So now temp->pcKey points to "KEY string" itself and not a copy of it. So when you try to free the string constant, your program complains. What you want to do is copy the string from pcKey into temp->pcKey instead of overwriting the pointer.
EDIT:
As per comments the mallocs need space + 1 to include the null terminator. Also sizeof(char) is always 1, so is redundant. Try strdup instread.

invalid realloc/realloc returns NULL

In one function I used malloc :
void name1(struct stos* s)
{
s = malloc (4 * sizeof (int));
}
And everything is ok. But later I used realloc
void name2(struct stos* s)
{
s->size = 2*(s->size);
s = realloc (s, (s->size + 1) * sizeof (int));
}
and I get invalid free/delete/realloc in valgrind and realloc returns NULL.
Declaration of Structure and rest of program is:
struct stos
{
int top;
int size;
int stk[];
};
void name1(struct stos* s);
void name2(struct stos* s);
int main()
{
struct stos stosik;
struct stos* s;
s = &stosik;
name1(s);
//some operations on the array and int top here
name2(s);
}
What did I do wrong here? I looked for what might have gone wrong for quite long, read quite a few articles about pointers, malloc/realloc etc. but with no result. I would be really grateful, if someone could help me.
The problem is slightly subtle and caused by a combination of two things. Let's start here:
struct stos stosik;
struct stos* s;
s = &stosik;
name1(s);
First, you make s point to an a valid chunk of memory that is allocated on the stack (stosik) and then you call name1 passing into s. Let's look at what name1 looks like:
void name1(struct stos* s)
{
s = malloc (4 * sizeof (int));
}
Hmm, we can see that name1 takes in a pointer to a struct stos called s; inside that function, we are allocating some memory and making s point to it. This is a problem.
First of all, note that s already points to a valid chunk of memory. So using a malloc here is suspicious. It will cause a subtle bug that will actually hide the real bug in your program, which is bad. So, let's remove stosik completely:
int main()
{
struct stos* s = NULL;
name1(s);
if(s == NULL)
return -1;
Now, if you run this program, you will see that after you call name1 the variable s still points to NULL. What's happening here?
Well, we are changing the function's LOCAL copy of s (i.e. the s that exists only inside name1)... but the s in main isn't changed! Remember, that we are passing a pointer into name1 but we are passing it by value.
To do what you seem to be trying to do you can do you would have to either pass a pointer to s into name1 (that is, to pass a double pointer) or you should return the result of the malloc from name1 as a return value. Let's look at each of these options:
Passing s in via a double pointer
void name1(struct stos **s)
{
/* sanity check */
if(s == NULL)
return;
/* now, allocate enough space for four integers and make
* whatever s points to, point to that newly allocated
* space.
*/
*s = malloc(4 * sizeof(int));
}
And calling it from main requires us to use the "address-of" operator:
struct stos *s = NULL;
/* we need to pass a pointer to s into name1, so get one. */
name1(&s);
/* malloc can fail; check the result! */
if(s == NULL)
return -1;
Returning a pointer to the allocated memory from name1
struct stos *name1()
{
return malloc(4 * sizeof(int));
}
Calling this from main is slightly easier:
struct stos *s = name1();
/* malloc can fail; check the result! */
if(s == NULL)
return -1;
Changing your code to what I show you here will fix this issue (but there may be others) but let me touch briefly upon something else:
The other bug
The crash you encounterd crops up partially because of the problem we just covered; another issue is that inside name2 you are calling realloc. The pointer you pass into realloc however, is not a pointer that you got back from malloc or realloc, which is what realloc expects. It points to stosik instead. So that code causes undefined behavior and after that anything can happen.
If you're lucky (it seems you were), it will just crash right then and there and if you're not... well, who knows what will happen?
if you want to dynamically allocate s in name1 you need it to be declared as name1(struct stos** s) and pass pointer to the pointer where the allocated memory should appear.
Your main allocates stosik staticaly, meaning you don't need to do any further dynamic allocation. Then when you try doing name1(statically allocated mem) it does … um, something. I don't know what, but certainly not what you expect.

weird memory error in C

I'm writing a program in C that checks for circular symbolic links. The strategy is to create a struct fileInfo:
typedef struct fileInfo fileInfo;
struct fileInfo {
ino_t inode;
dev_t devID;
};
that will store a file's inode and devID. We create an array of these structs and check every time before opening a new file whether the file already exists. If so, then it's a circular link.
void func1(...)
{
fileInfo **fileData = malloc(sizeof(struct fileInfo*));
int fileDataLen = 0;
char* path = "path of file";
/* some flags */
func2(path, fileData, &fileDataLen);
for (int i = 0; i < fileDataLen; i++)
free(fileData[i]);
free(fileData);
}
void func2(char* path, fileInfo ** fileData, int * fileDataLen)
{
//try to open file
struct stat buf;
if (openFile(file, &buf, followSymLinks) == -1)
exit(1);
fileData = checkForLoops(fileData, fileDataLen, &buf, file);
if (S_ISDIR(buf.st_mode))
{
char* newPath = /* modify path */
func2(newPath,fileData, fileDataLen);
}
/* other stuff */
}
int openFile(char* file, struct stat * buf, fileInfo ** fileData, int * fileDataLen)
{
if (lstat(path, buf) < 0)
{
fprintf(stderr, "lstat(%s) failed\n", path);
return -1;
}
return 0;
}
fileInfo** checkForLoops(fileInfo **fileData, int * fileDataLen,struct stat *buf,
char* path)
{
for (int i = 0; i < (*fileDataLen); i++)
{
if (fileData[i]->inode == buf->st_ino &&
fileData[i]->devID == buf->st_dev)
fprintf(stderr, "circular symbolic link at %s\n", path);
}
fileInfo *currFile = malloc(sizeof(struct fileInfo));
memcpy(&currFile->inode, &buf->st_ino, sizeof(buf->st_ino));
memcpy(&currFile->devID, &buf->st_dev, sizeof(buf->st_dev));
fileData[(*fileDataLen)] = currFile;
(*fileDataLen)++;
fileData = realloc(fileData, ((*fileDataLen)+1) * sizeof(struct fileInfo*));
return fileData;
}
I notice, however, that after a few calls to func2(), there is a memory leak and fileData points to nothing. I'm just not sure where the leak is coming from, since I don't free anything in func2(). I'm assuming there are some realloc shenanigans, but I don't understand why. Help would be greatly appreciated!
I'm noticing a couple oddities in the code.
First, the function signature of openFile has a return-type of void, yet you check for a return-value here:
if (openFile(file, &buf, fileData, fileDataLen) < 0)
Secondly, as Peter also points out, you're not allocating enough space when calling realloc:
fileData = realloc(fileData, (*fileDataLen) * sizeof(struct fileInfo*));
On the first iteration, where (*fileDataLen) == 0, after incrementing *fileDataLen, you now only have a value of 1, which means that you aren't reallocating anything (i.e, you're simply passing back the memory that fileData was already pointing to since it hasn't changed the size of the allocated array). Therefore the next time you call fileData[(*fileDataLen)] = currFile; during another recursive call, you are going to be copying the value of currFile into fileData[1], but that memory hasn't been allocated yet. Furthermore, the next-time that realloc is called, it may not longer reallocate memory at the same location, so fileData will be pointing to a completely different location, with only the first array entry copied over.
Third, you can't call free(fileData) in func1() since you, by calling realloc inside your func2() function, have changed the value of where the memory is pointing, and you are not passing the actual memory address for the original fileData variable by reference to your func2() function. In other words if the call to malloc() in func1() returned a value of let's say 0x10000, and you called realloc on that allocated memory somewhere else in the code, the memory that was allocated at 0x10000 has now moved somewhere else, but the value of fileData in the context of the local scope of func1() is still 0x10000. Thus when you effectively call free(0x10000), which is what's happening when you call free(fileData), you are going to get an error since the memory for the array is no longer allocated at 0x10000. In order to free the array, you are either going to have to return the updated pointer to the pointer array from all the recursive calls to func2(), or pass fileData by reference, meaning the function signature of func2() and openFile() will need to change to a fileInfo*** type, and you'll also need an extra layer of indirection whenever accessing fileData in func2() and openFile(). Then when you call realloc anywhere else, you are actually modifying the value of fileData as it was allocated in func1() as well, and can call free() on that pointer.
Finally, keep in mind that if you only free the memory allocated for fileData, you are going to have a big memory leak for all the allocated fileInfo nodes on the heap since fileData was only an array of pointers to the nodes, not the actual nodes themselves.
Your problem is that you aren't allocating enough memory for fileData:
fileInfo *fileData = malloc(sizeof(struct fileInfo));
Here you only allocate memory for a single pointer to fileInfo, instead of the array of fileInfo instance you seem to be using.
Sorry, my first idea was wrong... but your problem still seems to be that you aren't allocating enough memory for fileData - just in a different place:
fileData[(*fileDataLen)] = currFile; // 1
(*fileDataLen)++;
fileData = realloc(fileData, (*fileDataLen) * sizeof(struct fileInfo*)); // 2
Here you allocate one element less than needed. You start with fileDataLen of 0, and fileData containing 1 element. After opening the first file, you increment fileDataLen to 1, then reallocate the array to contain 1 element instead of 2! Thus, when opening the 2nd file, your buffer is overrun at // 1 above, and some memory is overwritten.
You should keep this invariant at all times, reallocating the array to a size fileDataLen + 1:
fileData = realloc(fileData, (*fileDataLen + 1) * sizeof(struct fileInfo*));
I don't know what kind of processing you're performing over path variable in func2, but you might be trying to modify a static string, which will lead you to another memory problem since those kind of strings are stored in a private memory zone reserved by the OS.

Resources