realloc and the ghosts of mallocs past - c

I know that realloc will free memory when necessary, and I know the third rule of C - "for every malloc there must be an equal and opposite free"... but how do these two work together?
The situation is best described in code:
int main()
{
myStruct **myStructArray;
int i, num_elements;
num_elements = getnumber(); // gets value for num_elements
myStructArray = (myStruct **) malloc(num_elements * sizeof(myStruct*));
for (i=0; i<num_elements; i++)
myStructArray[i] = (myStruct *) malloc(sizeof(myStruct));
// so far so good...
num_elements = getnumber(); // gets new, LOWER value
myStructArray = realloc(myStructArrary, num_elements * sizeof(myStruct*));
// rest_of_code, and necessary free loop for myStructArray etc...
}
Obviously the above is nothing more than a snippet, but a snippet paints a thousand words.
Would this create a memory leak? I know the call to realloc will free the pointers' memory, but I can see arguments for and against the possibility that there is still going to be a bunch of memory forgotten about.
A leak can be circumvented by incorporating int number_elements_new into the code and loop free-ing the surplus myStructs before calling realloc to free the (now NULL) pointers.
If realloc does the donkeywork and frees up ALL the associated memory that's great, otherwise I've got to trawl through to make sure nothing has been missed - myStruct itself contains allocated memory and so on.
Thank you for your recommendations...

malloc, realloc and free have no idea about what the memory is being used for. If you're using the memory to store pointers to other dynamically-allocated memory, then that's for you to tidy up!
Also, note that the way you're using realloc in your code snippet is potentially unsafe. If realloc fails, it leaves the original memory un-freed, and returns NULL. Best practice is to assign the return value to a temporary pointer, and then check. See e.g. http://www.c-faq.com/malloc/realloc.html.

If you're shrinking the size of your array, you would first need to free each myStructArray[i] where i >= num_elements, otherwise you will have a memory leak.
Put another way, shrinking the size of the pointer array does not affect the memory that each array element was pointing to.
Also, in the realloc call, you will want to assign the result to a temporary pointer; if realloc cannot extend or shrink the buffer, it will return NULL, and you'll lose your reference to that block, which will also introduce a leak:
myStruct **tmp = realloc(myStructArray, ...);
if (tmp)
{
myStructArray = tmp;
...
}

If the number is lower than the existing number of elements, you will introduce a memory leak as the reallocated block is not related to the content and you are losing your reference to the blocks allocated in the for-loop.
Therefore you'll have to loop through your array and free all elements that will be subject to removed.
If the number is higher than the existing number, it won't create a memory leak as realloc copies the existing data if a new memory block has been allocated. Therefore your other dynamically allocated blocks (malloc in the for loop) are still referenced by the resized array. The only gotcha here is, that the newly allocated array spaces are uninitialized and can therefore contain invalid pointers.

Related

Malloc doesn't doesn't add new memory

I'm having trouble when I try to use malloc to allocate new space for floats inside my array.
My goal is to dynamically create array, and malloc to add a space for new float each time I want to add a new float.
Here is the code I am trying to run, but each time it only allocates an array with sizeof(float), even though the variables keep increasing.
float *funkcia_e(FILE **subor, int *pocet_cien) {
float *pole_cien;
*pocet_cien = 1;
while (fgets(nazov, sizeof nazov, *subor) != NULL)
{
pole_cien = (float*) malloc((*pocet_cien) * 4);
fscanf(*subor, "%f", &pole_cien[pozicia++]); //This causes problems
*pocet_cien = *pocet_cien + 1;
}
}
int main() {
int pocet_cien = 1;
float *pole_cien = NULL;
funkcia_r(&subor, pole_cien, &pocet_cien);
}
Here is the recorded debugging: https://s.put.re/RR6wqRk.mp4
It appears the malloc actually corrupts the array, instead of exntending it. Any ideas?
You need to use realloc. It does what you wanted malloc to do, i.e. lets you extend a previously allocated block. In contrast, malloc is a one-shot deal: you get your memory block, and that's what you have to work with.
(from the comment) cant afford to realloc, as I have to allocate the array dynamically and realloc would purge the contents of array when reallocating it.
That is not true: when realloc extends the amount of memory allocated to your program, and decides that it must re-allocate the block, it copies the content of the current block into the new block, up to the allocated size of the old block, so the new block is ready to use. Hence, your current program results in a memory leak.
In order to let main use results of allocation you must pass the pointer pole_cien by pointer, i.e. as float**. Otherwise the results of assigning it inside funkcia_e are not propagated to main.
A few other points to consider:
Do not cast malloc results
Do not hard-code size of float as 4; use sizeof(float)
Make sure pocet_cien starts at zero, and use it in place of pozicia; add 1 on the call to realloc.
It is quite obvious as malloc does not "add" any memory, but allocates a new chunk of it - every time fresh. So eventually you access not allocated memory and you get the memory fault as index increments all the time but you allocate the space for 4 elements.

How should I free all memory created using multiple realloc?

I created array with 10 integer size using malloc. I added values to the elements. Then, I reallocated it to 200 bytes into newArr. And then I reallocated newArr into newArr2 with size of 10 integers again. Code:
void main(){
int i, *arr = (int *)malloc(10* sizeof(int));
for(i=0; i<10; i++){
arr[i] = i;
}
int *newArr = (int *)realloc(arr, 200);
int *newArr2 = (int *)realloc(newArr, 10* sizeof(int));
}
How should I use free to remove all the allocated memory here? I'm getting error while clearing all of them.
Edit: As per the accepted answer the old memory should've been cleared but it didn't. I was able to access memory and was able to change value on old address.
From my point of view, when you use malloc or realloc you're changing the memory reference, so, if you call realloc on a variable you are freeing the old space used and allocate new space, copying the old data to the new memory position, so, in your example, arr doesn't hold a valid memory address after first realloc. The same thing happen on newArr
realloc is basically malloc with the new size, memmove the data to the new block and free the old one. (But implementation can optimize this process because they've got more information they can use, like just extending the current allocated block, producing the same pointer)
So the pointers arr and newArr are invalid and shouldn't be accessed anymore because they might have been freed, so the pointer in newArr2 is the current one and valid, if the previous allocations didn't fail. So free(newArr2) is the correct answer.
Sure, you might access the memory from the old pointers, but it isn't guaranteed because it might've been allocated and overwritten for a different purpose or you might just be lucky to get the same pointer back from realloc (because from eg. the optimization above). It's just undefined behavior when accessing freed memory.
Source on reddit

pointer to a pointer, which is pointing to a memory block, which pointer should be freed?

At the end of the code below, which pointer would I need to plug into free(), array or temp_array? Does it matter which one or would either free the memory block?
int *array = 0;
int *temp_array = 0;
int count = 0;
array = malloc(sizeof(int));
// skipping code where count is calculated...
temp_array = realloc(array, count * sizeof(int));
if (temp_array == NULL) {
free(array);
// some error message
return;
}
array = temp_array;
// skipping section of code, which reads numbers from a file and loads them into an array
// amount of numbers read can be 0 to whatever
free (array); // free array or temp_array?
Also, is it possible to allocate a block of memory with realloc if the pointer it's trying to allocate memory for is NULL (in other words, do I need to allocate memory first with malloc and later resize it with realloc, or can I skip the malloc)?
It doesn't matter - both temp_array and array point to the same memory block. I would prefer temp_array as then the realloc and free pointers match. Depending on your working code, for protection you could consider assigning both pointers to NULL to prevent free-ing the memory twice. free(NULL) is safe - no operation is performed.
Regarding the initial alloc of one integer - is that necessary? From the code shown, an int defined on the stack would be preferable.
EDIT:
After more info from OP (in comments) it appears the code can be simplified using an header value which holds the number of records in the file. This eliminates the need for realloc and permits memory allocation prior to reading the file values in.

Memory is not reallocating

I'm in the middle of a project and I'm trying to use malloc() and realloc(). I know when I malloc, it works, but when I use realloc, it doesn't change the amount of alloced memory at all. I've always though that realloc will re-allocate your already malloced memory.
Here is what I have:
This include:
#include <stdlib.h>
I have a struct:
struct student {
int age;
int numOfClasses;
int gender; //0 male; 1 female
} student;
When I want to make 7 of those structs using malloc, I will use this line of code:
student stud* = (structure*) malloc(7*sizeof(student));
This line works. That line of code takes the size of the structure and multiples that by 7. In short, this will grab enough memory to make an array of 7 structures.
Now, if I want to change that to 8, I would do this where A is the previous malloced memory, and B is the new malloced (or realloced) memory:
Here is how I have it in code:
stud = (student*)realloc(stud, 8*sizeof(student));
From what I know, realloc takes the variable in the second parameter and mallocs that amount of memory. Then, it takes the pointer (or previous malloced), and fills in the just malloced memory with as much as it can from the given pointer. Of course, the second parameter must be bigger than the previous malloced size, or stud will lose some memory on the end. Now this is where my problem is. When I call the line above, it doesn't change anything. The malloced array is still length of 7. I'm pretty sure, also, that I have enough memory to realloc.
Am I doing this right? Where could my problem be?
Your understanding of realloc's behaviour is nearly correct. It doesn't have to return a different pointer; it may be that there was enough unused memory after the initial block, so the heap manager can just return the same pointer back to you (but adjust its own internal state such that it knows the block is now bigger).
You have made one small mistake, though.
stud = (student*)realloc(stud, 8*sizeof(student));
Here you are replacing your stud pointer with the return value from realloc. If it happens to return NULL due to memory starvation, then you have lost your original stud pointer and the memory is leaked. You should use a temporary pointer instead.
tmp = realloc(stud, 8*sizeof(student));
if (tmp)
stud = tmp;
Also note that you still have to actually put something in the eighth slot. After the realloc the eighth slot is valid allocated memory, but contains garbage until you store something meaningful in it.
This should work, although I'd have these recommendations:
Don't cast the return from malloc. It's useless in C and may hide that you have forgotten to include <stdlib.h>.
Do not use ptr = realloc (ptr, ...) as this creates a memory leak in the case realloc returns NULL. Instead, use
if ((new_ptr = realloc (stud, 8 * sizeof (*stud))) != NULL) {
stud = new_ptr;
} else {
scream_and_die("out of memory");
}
And use sizeof (*stud), i.e. reference an expression using the pointer, not the type being pointed to (to be independent of the particular type of pointer you allocate). This way, when you rename the typedef, the malloc/realloc line needs no modification. In other words, Best Practice for Dynamic Memory Allocation in C is
#include <stdlib.h>
sometype *ptr;
...
ptr = malloc (N * sizeof *ptr);
for an array of N sometypes.

How to realloc() in the middle of an array?

I have an array of pointers
char *wordlist[9];
and then I malloc() a block of memory on every of this pointers
for(int i=0; i<9; i++)
wordList[i] = (char*)malloc(someLength);
Lets suppose that every time the someLength is different.
And the problem is now, that I want to realloc() ie. 4th elemet of wordList to a larger size than it is now.
wordList[3] = (char*) realloc(&wordList[3], someBiggerSize);
Since malloc allocates a consistent block of memory, is that operation even possible without colliding with wordList[4]?
There's nothing to worry about this in principle. You just have an array of pointers and each element of the array points to a distinct memory block. Each element of the array, each pointer, can be therefore be reallocated independent of the other elements.
Now, I say in principle because your code does have an error. You should pass wordList[3] rather than &wordList[3] to the realloc.
Just remove the & . wordList[3] = (char*) realloc(wordList[3], someBiggerSize);
wordList[3] is a pointer, and realloc expected to get a pointer that allocated by malloc. not pointer to it.
About your last question: every time you call malloc, it return a consistent block of memory. there is not guarantee that memory, allocated by two calls for malloc, will be consistent. In other words, wordList[3] and wordList[4] are not must be consistent, and you can do whatever you want two one of them (as long as you care about the buffers size) without thinking about the other.
Why should it be colliding? You have declared an array of pointers, each of which points to memory that is allocated elsewhere. When you reallocate you are just changing the size/position of this memory, the pointer returned by realloc is as big as it was.
By the way, you shouldn't be using realloc that way, since, if it fails, you'd be leaking memory; see e.g. here.
---edit---
And, as #asaelr noted, you should remove that &, just reallocing the block pointed by wordList[3], not the memory of wordList.
You have a misunderstanding about what realloc does. It will return a whole new block of memory (if the new size is larger than the old size) instead of increasing the size of the block that was passed into it.
malloc allocates a trunk of memory from heap and that trunk of memory can't be allocated for other malloc until you free them. In other words, malloc succeeds only if there are enough continuous free space in the heap. So this makes sure that the memory allocated would not collide with any others in your words.
Each of your pointers points to a separate and independent block of memory. Inside your array of pointers, each element is simply an address and overwriting one won't affect the others. So, what you are doing is fine and won't cause any problems with other elements of the array. As others mentioned, you should be passing wordList[3] and not &wordList[3]

Resources