typedef struct
{
char path[MAX_FILENAME*MAX_FOLDERS];
char filename[MAX_FILENAME];
time_t date;
off_t size;
} FILES;
This code works
FILES *fls = (FILES*)malloc(sizeof(FILES));
strcpy(fls[0].filename, "asd");
printf("%s\n", fls[0].filename);
fls = (FILES*)realloc(fls, 2);
strcpy(fls[1].filename, "asdfgh");
printf("%s\n", fls[0].filename);
printf("%s\n", fls[1].filename);
But here:
void allocateFileTree(FILES *tree,int i)
{
if(i==0)
tree = (FILES*)malloc(sizeof(FILES));
else
tree = (FILES*)realloc(tree, sizeof(FILES)*i);
}
in a loop
allocateFileTree(tree, i);
struct stat buff;
stat(entry -> d_name, &buff);
strcpy(tree[i].path, "whatever");//it gives segfault
i++;//this is never executed so realloc isn't the problem (yet)
Why and how can I solve this? What is so different that it crashes?
The code you say works really doesn't. One major problem is this line here
fls = (FILES*)realloc(fls, 2);
This reallocate the pointer to be two bytes. There's also a problem with this if the realloc call fails, as then you overwrite the only pointer you have with NULL, and therefore loose the original pointer and have a memory leak (besides the obvious problem of dereferencing a NULL pointer).
Your exact cause of the crash is because you don't allocate memory for the path member, so you're using an uninitialized pointer.
Both of the above leads to undefined behavior, which is a common cause of crashes.
And finally, in C you should not cast the return of malloc (and family).
While you allocate space for an array of FILES, you do not allocate storage for path in the code shown.
In the code
strcpy(tree[i].path, "whatever")
The value of tree[i].path is undefined. It might happen to point to space you can write to, or not.
This statement:
(FILES*)realloc(tree, sizeof(tree)*i);
allocates enough space for i pointers since tree is a FILES*. I think you want:
(FILES*)realloc(tree, sizeof(*tree)*i);
Your other problem is that you never actually update your tree pointer. The allocateFileTree() function only every updates it's local copy of the pointer to the new allocation.
You may want to try something like
FILES* allocateFileTree(FILES *tree,int i)
{
if(i==0)
tree = (FILES*)malloc(sizeof(FILES));
else
tree = (FILES*)realloc(tree, sizeof(FILES)*i);
return tree;
}
and call it like so:
tree = allocateFileTree(tree, i);
Related
I want to store pointers that have been allocated using malloc() in an array and then free all of them after. However even though the program doesn't complain it doesn't work. Below cleanMemManager() won't actually free the memory as when tested inside main() the char* pointer is not NULL and it will print ???.
code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void **ptrList = NULL;
void tfree(void** ptr)
{
free(*ptr);
*ptr = NULL;
}
void* talloc(int size)
{
void* ptr = malloc(size);
ptrList[0] = ptr; ///No clue if this actually does what I think it does
return ptrList[0];
}
void initMemManager()
{
ptrList = (void**)malloc(sizeof(void**) * 3);
memset(ptrList, 0, sizeof(void**) * 3);
}
void cleanMemManager()
{
tfree(&ptrList[0]); //Doesn't free the right pointer it seems
}
int main()
{
initMemManager();
char* ptr = (char*)talloc(3);
cleanMemManager();
if (ptr != NULL) //This will trigger and I'm not expecting it to
printf("???");
getchar();
return 0;
}
I don't understand the syntax to use for this, does the pointer not actually get touched at all? What is it freeing then since it doesn't throw any errors?
In main, char *ptr = (char*)talloc(3); declares ptr to be a local variable. It contains a copy of the value returned by talloc, and none of your subroutines know about ptr or where it is. So none of them change the value of ptr. Thus, when you reach if (ptr != NULL), the value of ptr has not changed.
Additionally:
In initMemManager, you should use sizeof(void *) in two places where you have sizeof(void**). In these places, you are allocating and copying void * objects, not void ** objects.
It looks like you are trying to implement a sort of smart memory manager that automatically sets pointers to NULL when they are freed. To do that in C, you would have to give up having copies of pointers. For example, ptr is a copy of ptrList[0], but tfree only sets whichever copy it is passed to NULL. We could give advice on building such a system, but it would quickly become cumbersome—your memory manager needs to keep a database of pointers and their copies (and pointers derived from them, as by doing array arithmetic). Or you have to refer to everything indirectly through that ptrList array, which adds some mess to your source code. Unfortunately, C is not a good language for this.
Freeing doesn't guarantee that pointers pointing to the allocated block will be set to NULL. If you actually try doing
if (ptrList[0] != NULL)
printf("ptrList[0] != NULL");
you will see that the program won't output and if you remove the cleanMemManager() function call, it will output. This means tfree function is working as intended, it's freeing the memory that was allocated.
Now as to why ptr variable being not set to NULL, it's simply because ptr is still storing the old address. cleanMemManager() has no way of mutating the variable ptr. This is commonly called dangling pointer or use after free.
Also free() doesn't clean/zero out the the allocated space, the block is simply marked as "free". The data will most likely remain in the memory for a moment until the free block is overwritten by another malloc request.
Recently, I'm learning C. I found a question on the internet. The question is:
What is the problem with this function in terms of memory allocation?
What is a good solution? You may assume that a struct item type has
been declared. The purpose of this function is to allocate an array of
struct item, which you may assume has been declared prior to this
function.
BOOLEAN allocate_items(struct item * items, size_t howmany)
{
size_t count;
items = malloc(sizeof(struct item) * howmany);
if(!items) {
perror("failed to allocate memory");
return FALSE;
}
return TRUE;
}
So, I think that the 4th line is wrong. It should be like:
items = malloc(sizeof(struct item));
And also the 6th line is wrong. It should be like:
if(items == NULL){
Is it correct?
First of all, both line 4 and 6, as you mentioned seems to be OK.
That said, the basic problem with this function is, you're allocating memory to a local scope of variable. This way
as you don't return the pointer to allocated memory, after the function returns, there would be no way to access the allocated memory.
by not freeing up the allocated memory, you will face memory leak.
If you have to allocate memory to a pointer, you need to pass the address of that pointer to the function and allocate memory. You can also return the pointer but then you need to change the function signature.
Finally, arrays are not pointers and vice-versa. They may appear or beahave similar sometimes, but they are not the same.
The 4th line is not wrong as they are trying to declare an array of the structs.
You should add a line inside the function that declares a new pointer, temp, to hold the current value of items, then after allocating the memory,
the 6th line should be
if(items == temp)
to check whether the value has changed(because that is the closest we can get to checking whether malloc worked)
this is because the ! operator is used to check if a condition is true or not(at least at a basic level in most languages) and as a pointer isn't a condition or an int that can be used as true or false, the operator won't work.
Here a fixed version, as it would probably be written in the "industry".
bool allocate_items(struct item ** pitems, size_t howmany)
{
// argument validation
assert(NULL != pitems); // some also add release version checks...
if(NULL == pitems ) return false;
// We can also spot memory leak sources here.
// If *pItems != NULL - does that mean we have to free first to prevent
// a leak? What if it is just some random value and not something we can
// free? So contract usually is: *pitems has to be NULL...
assert(NULL == *pitems);
if(NULL != *pitems) return false;
// implementation
*pitems = malloc(sizeof(struct item) * howmany);
if(NULL == *pitems) {
perror("failed to allocate memory");
}
return NULL != *pitems;
}
While the bool defined in stdbool.h sometimes causes trouble with C++ interop (same symbols on both sides, but sometimes sizeof(bool) differs), it is still the better option compared to inventing yet another bool type.
The pitems is a pointer to the location where the pointer to the new chunk of memory shall be written to. A caller of this function might have written:
int main(int argc, const char*[] argv) {
struct item *myBunchOfStuff = NULL;
if(false != allocate_items( &myBunchOfStuff, 20) ) {
// ...
free(myBunchOfStuff);
myBunchOfStuff = NULL;
}
return 0;
}
Defensive programming states: Your function cannot claim "Heh - my function only crashed because I was given a bad value!". Instead, it has to verify itself. It is responsible not to crash. The pointer could still be != NULL but otherwise bad. That is impossible for the function to catch, usually.
In C, everyone is proud of not requiring the cast of malloc()'s return value. You can be proud of that until you compile your code with a C++ compiler. Then you have to change your code and fix that. Well, I guess it is a matter of preference...
While parameter checking is often seen as a separate part of the functions implementation, after that, you should try to stick to "single point of exit". Main reason for that is maintainability. With multiple exit points, if the function gets bigger later on, it gets harder to spot if some early exit forgets to free some memory or cleanup other forms of state.
I have a problem with this code, I tried to understand what's going on, but I cannot understand why it just crushes. the functions works like I expect for the node->left.
But when the last call of the function ends in the case node->left; the function just crushes, I think because of the reallocated array, I don't know if I'm trying to access a forbidden memory.
I'll explain a little more where I think the problem comes from:we are in the last call of helper(node->left,table,len) before printing the array : consider len = N and node->left!=NULL ==> reallocating table len = N+1, assigning node->data to table[len] and everything is fine, node->left == NULL ==> printing the table and we are done with the helper(node->left,table,N) case; now we are in the helper(node->right,table,N) case; in this case the program just crushes ; it's supposed to reallocate table; and assign node->data to table[N]; and so one ...
By the way : this function tries to print all the Root-leaf paths in a binary tree.
struct node {
int data;
struct node* left;
struct node* right;
};
void helper(struct node* node,int *table,int len)
{
if(node == NULL){
return;
}
else{
table = realloc(table,(len+1)*sizeof(int));
table[len]=node->data;
len = len +1;
if(node->left == NULL && node->right == NULL)
{
int cmt=0;
for(cmt = 0; cmt < len ; cmt++ ){
printf("%d ->",table[cmt]);
}
}
else{
helper(node->left,table,len);
helper(node->right,table,len);
}
}
}
void print_rtl(struct node* node) {
if(NULL == node) return;
else{
int *t=malloc(sizeof(int));
t[0]=node->data;
int lenght = 1;
helper(node->left,t,1);
helper(node->right,t,1);
}
}
Here is the thing about realloc: it has the freedom to change not just the size of the allocated memory block, but also its location. In general, realloc can break any pointers you have that are pointing to objects in the block of memory that was realloced. When you try to use those pointers later, you will get undefined behavior.
You should either allocate enough memory at the beginning of your program that you won't need to call realloc, or you should redesign your program so you don't need to allocate that memory block in the first place, or you should make your table pointer be a global variable so that when you change its value, it gets updated for every function that is using the table.
Right now the problem with your code is that when you call helper, it might call realloc and change the location of your table, but it doesn't communicate that change to the caller in any way, so the caller doesn't know where the table is located.
There might be other problems with your code. If you continue to have trouble, I strongly urge you to produce an MCVE and post that in your next question. Doing that makes it much easier for someone else to reproduce your problem and find a solution to it that actually works.
It's hard to tell exactly what going on because it quite a big mess, but generally... The first thing helper() does (after validating node!=NULL) is reallocate table. That means when you get to the recursion point, helper(node->left.. will reallocate, and then immediately after helper(node->right.. will also try to do so, but on an invalid pointer. From there on, it's a wild ride to exceptions...
After running this function many (not sure exactly how many) times, it seg faults on a simple memory allocation. Why would this suddenly happen? I did notice something strange in GDB. In the function that calls it, normally there's 6-digit long hex value for wrd (wrd = 0x605140 for example), however on the call where it crashes, the hex value is only two digits long. (wrd=0x21). I also checked the wrd->length, and it's 3.
The line that it crashes on is...
char *word_temp = malloc(wrd->length * sizeof(char));
EDIT:
Here's the code that creates the wrd...
while(fgets(input, 100, src) != 0)
{
int i = 0;
while(input[i] != '\0')
{
i++;
}
struct word *wrd = malloc(sizeof(struct word));
wrd->letters = input;
wrd->length = i;
If I'm getting an overflow, how do I fix that?
Looks like wrd->length does not include the terminating '\0'.
Fix 1, allocate word_temp like this:
char *word_temp = malloc( wrd->length + 1 );
Fix 2, include the '\0' by modifying you length count loop:
int i = 0;
while(input[i++] != '\0') {}
This will increase i one more time than code in the question, which is easy to see if you consider case of input being empty.
Note that you need to do either fix 1 or fix 2, not both. Choose which ever works with rest of your code.
You probably have a second issue with this line:
wrd->letters = input;
It does not copy input, it copies the pointer. If you change contents of input, contents of wrd->letters changes too, because they point to same memory location. Also if input is a local char array, then once it goes out of scope, wrd->letters becomes a dangling pointer, which will be overwritten by other data, and modifying it after that will result in memory corruption.
Possible fix (depending on rest of your code) is to use strdup:
wrd->letters = strdup(input);
Remember that it is now allocated from heap, so when done, you must remember to do
free(wrd->letters);
About wrd being 0x21, that indicates either memory corruption, or that you actually have two separate wrd variables, and one one is left uninitialized.
For example, maybe wrd is a function parameter struct word *wrd, in which case you only modify the local value in function, it does not get passed back to the caller. To modify the pointer of caller, you need to have pointer to pointer: struct word **wrd and then do (*wrd) = malloc... and (*wrd)->letters... etc.
I'm having some trouble with the following:
void BuildList(cs460hwp hw)
{
FILE* fp;
fp = fopen("HW2input.dat", "r");
if(fp == NULL)
{
printf("Couldn't open the file.");
return;
}
int numStudents;
int i;
bool success;
char* dueDate = malloc(9*sizeof(char));
char* course = malloc(7*sizeof(char));
char* wsuid = malloc(9*sizeof(char));
char* subDate = malloc(9*sizeof(char));
double points1 = 0;
double points2 = 0;
cs460hwp stuInsert = NULL;
fscanf(fp, "%d", &numStudents);
fscanf(fp, "%s", dueDate);
for(i = 0; i < numStudents; i++)
{
stuInsert = malloc(sizeof(cs460hwp));
fscanf(fp, "%s %s %s %lf", course, wsuid, subDate, &points1);
strcpy(stuInsert->course, course);
strcpy(stuInsert->wsuid, wsuid);
strcpy(stuInsert->subdate, subDate);
stuInsert->points1 = points1;
stuInsert->points2 = CalculatePoints(dueDate, subDate, points1);
stuInsert->nextPtr = NULL;
if(hw == NULL)
{
hw = stuInsert;
}
else
{
stuInsert->nextPtr = hw;
hw = stuInsert;
}
}
free(course);
free(wsuid);
free(subDate);
free(dueDate);
PrintGrades(hw);
fclose(fp);
}
struct hwpoints
{
char course[7];
char wsuid[9];
char subdate[9];
double points1;
double points2;
struct hwpoints *nextPtr;
};
typedef struct hwpoints *cs460hwp;
My goal here is to insert every entry to the top of the list. However, whenever I try to assign anything to nextPtr (such as in the else clause), it gets filled with garbage values. They're mostly truncated versions of old data, which leads me to believe they're being taken from the heap. I've been reading (a lot), but I'm having trouble finding advice on this particular problem.
nextPtr always becomes junk, and nextPtr->nextPtr causes a segfault. For every iteration of the loop. hw remains fine, but its pointer value never gets updated properly.
Even when I've attempted to move the memory allocation for the struct into a function, I've had the same (or similar) issues.
Can anyone point me in the right direction?
Two problems.
1) As pb2q mentioned, you are passing a pointer to a struct and trying to assign what the arg points to. That's allowed by the compiler, but it doesn't do anything for you outside the function. It might be OK in your case if:
void main()
{
cs460hwp hw = NULL;
BuildList(hw);
return;
}
Is the whole of your function. I don't know the assignment so you need to figure out if that's acceptable to you or not.
2) The much bigger problem:
stuInsert = malloc(sizeof(cs460hwp));
Did you check what sizeof(cs460hwp) comes out to be? it's 4. You're allocating enough memory for the size of a pointer, not the size of your structure. I'm pretty sure this is not what you want to do and this is what is killing you. Just for kicks, replace it with malloc(100) and see if your problem goes away. If so you just need to figure out what size you really want. ;)
A problem with your BuildList function is that you're passing a pointer to a struct hwpoints, and you're trying to re-assign what the argument points to. Since function arguments in C are pass-by-value you're only changing the copy of the pointer that your function receives, and those changes won't be reflected in the caller.
You can use this pointer to modify the contents of the list: you can change e.g. hw->course or hw->nextPtr, you can move elements around in your list. But you can't change what the head, hw points to, so you can't insert elements at the beginning of the list.
If you want to change your head pointer, as in these statements:
hw = stuInsert;
// ...
hw = stuInsert;
Then you'll need to pass a pointer to the pointer:
void BuildList(cs460hwp *hw)
And de-reference it as necessary in the body of the function.
I can't be sure that this is the cause of the output that you're observing, which may be due to other problems. But if, after some number of calls to BuildList, beginning with a head pointer equal to NULL, you're trying to print your list assuming that it has valid nodes, you could see garbage data.
Thanks to #Mike's answer, we see also that you're not allocating enough space for your list nodes:
stuInsert = malloc(sizeof(cs460hwp));
Will only allocate enough space for a pointer, since cs460hwp is typedef'd to be a pointer to struct hwpoints. You need to allocate enough space for the structure, not a pointer to it:
stuInsert = malloc(sizeof(struct hwpoints));