Safer way to realloc - c

I am implementing a function to safely realloc a structure in order not to lost the information if any allocation error occurs, something like this:
int foo (someStruct_t *ptr, int size)
{
someStruct_t *tmp_ptr;
tmp_ptr = realloc(ptr, size);
if (tmp_ptr == NULL)
return -1;
ptr = tmp_ptr;
return 0;
}
My doubt resides in the following: Am I not duplicating the allocated memory for the structure everytime I run this function? In my line of thought I should free one of the pointers before exiting, correct?

The primary and major problem here is, after a call to foo(), in the caller, the passed argument for ptr will not be changed, as it itself is passed by value.
You need to pass a pointer to the pointer which you want to be reallocated, in case you don't want to return the new pointer.
That said, there is no "duplication" of memory here.
From realloc() point of view
realloc(), if successful, returns the pointer to the new memory and handles the job of de-allocating the older one. You don't need to worry about any duplication or memory leaks.
Quoting C11, chapter §7.22.3.5 (emphasis mine)
The realloc function deallocates the old object pointed to by ptr and returns a
pointer to a new object that has the size specified by size. [....]
[....] If memory for the new object cannot be
allocated, the old object is not deallocated and its value is unchanged.
From the assignment point of view
A statement like ptr = tmp_ptr; does not duplicate the memory or memory contents the pointer points to, it is just having two copies of the same pointer. For example, You can pass either of them to free().
So, bottom line, to answer the "question" in the question,
In my line of thought I should free one of the pointers before exiting, correct?
No, you should not. You need to have the newly allocated pointer to be useful in the caller, free()-in it inside the called functions makes the whole function pointless. You should free the pointer from the caller, though.

int foo (someStruct_t **ptr, int size)
{
someStruct_t *tmp_ptr;
tmp_ptr = realloc(*ptr, size);
if (tmp_ptr == NULL)
return -1;
*ptr = tmp_ptr;
return 0;
}

the posted code contains several problems:
cannot change where a caller's pointer points without the address of that pointer, so use ** in the parameter list and call the function using: foo( &ptr, size);
each reference to the callers' pointer must now be dereferenced via a *
usually the size refers to the number entries room to be allocated, so size needs to be multiplied by sizeof( someStruct_t )
note the use of size_t for the size parameter because realloc() is expecting the second parameter to have the type: size_t
and now the proposed code:
#include <stdlib.h> // realloc()
int foo (someStruct_t **ptr, size_t size)
{
someStruct_t *tmp_ptr;
tmp_ptr = realloc( *ptr, sizeof( someStruct_t) * size );
if (tmp_ptr == NULL)
return -1;
*ptr = tmp_ptr;
return 0;
} // end function: foo

Related

Using 'realloc()' during runtime gives crash, why? - C Language

I have an array that I want to increase the size of during runtime. I assign values to the elements of the array by using a loop, and when the index of the loop hits the number of elements of the array, I want to increase the size of the array.
What I did, actually works; I can assign values to elements of the array that I would normally not be able to assign any values to without increasing the array's size. The bad side is, it gives me a crash after the program runs and finishes smoothly. What is wrong here? Is it that maybe the memory that I try to allocate for the array is already filled?
int main()
{
int arr[3];
int num_of_elements = sizeof(arr)/sizeof(arr[0]); // This gives '3', I checked
for(i = 0; i < 10; i++)
{
if(i == num_of_elements)
{
num_of_elements = num_of_elements + 10;
realloc(arr, num_of_elements);
}
arr[i] = i+10;
printf("%d\n", arr[i]);
}
return 0;
}
Well you are invoking undefined behavior. From standard §7.22.3.5
void *realloc(void *ptr, size_t size);
If ptr is a null pointer, the realloc function behaves like the malloc
function for the specified size. Otherwise, if ptr does not match a
pointer earlier returned by a memory management function, or if the
space has been deallocated by a call to the free or realloc function,
the behavior is undefined. If memory for the new object cannot be
allocated, the old object is not deallocated and its value is
unchanged.
By memory management function - it means malloc etc. arr is not dynamically allocated memory. So passing this to realloc is undefined behavior - in your case that behavior leads you to crash in program.
It would work if you do this
int *arr = malloc(sizeof(int)*3);
if( arr == NULL){
perror("Malloc failed");
exit(EXIT_FAILURE);
}
...
int *p = realloc(arr,num_of_elements*sizeof(int));
^^^^^
if(p == NULL ){
perror("realloc failed");
exit(EXIT_FAILURE);
}
arr = p;
Check how realloc is used.
The takeaways will be:-
Check the return value of realloc, malloc.
You were trying to reallocate extra 10 elements for which you need 10*sizeof(int) amount of memory.
Don't do arr = realloc(arr,SIZE) in case realloc fails you will have memory leak.
Why realloc to p after all you do arr=p annyway?
Two reasons so far
The answer to this is when realloc fails then it returns NULL now if you assign arr to NULL then you may have a situation where you lose the only reference to the previously allocated memory - leading to a memory leak. That's why we do it like this.
Note this from standard
The realloc function returns a pointer to the new object (which may
have the same value as a pointer to the old object), or a null pointer
if the new object could not be allocated.
Notice that may part - it might be the same address as before as pointed by arr or it might be different one. That explains why we should store it in some temporary pointer and then we assign it later.
You should have done this:
arr = realloc(arr, num_of_elements);
^^^^^
realloc() does not necessarily extend or shrink the allocated memory in-place, it invalidates the dynamic memory that its first argument points to and allocates new memory, while preserving the content of the previous memory.
One possible implementation is :
void* realloc(void* ptr, size_t size) {
void* ret = malloc(size);
if (ret == NULL) return ret;
if (ptr != NULL) {
memcpy(ret, ptr, /* previous size from system */);
free(ptr);
}
return ret;
}

C - Storing pointers with malloc() in an array, can't free() them afterwards

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.

Proper usage of realloc()

From man realloc:The realloc() function returns a pointer to the newly allocated memory, which is suitably aligned for any kind of variable and may be different from ptr, or NULL if the request fails.
So in this code snippet:
ptr = (int *) malloc(sizeof(int));
ptr1 = (int *) realloc(ptr, count * sizeof(int));
if(ptr1 == NULL){ //reallocated pointer ptr1
printf("Exiting!!\n");
free(ptr);
exit(0);
}else{
free(ptr); //to deallocate the previous memory block pointed by ptr so as not to leave orphaned blocks of memory when ptr=ptr1 executes and ptr moves on to another block
ptr = ptr1; //deallocation using free has been done assuming that ptr and ptr1 do not point to the same address
}
Is it sufficient to just assume that the reallocated pointer points to a different block of memeory and not to the same block.Because if the assumption becomes false and realloc returns the address of the original memory block pointed to by ptr and then free(ptr) executes(for the reason given in the comments) then the memory block would be erased and the program would go nuts.
Should I put in another condition which will compare the equality of ptr and ptr1 and exclude the execution of the free(ptr) statement?
Just don't call free() on your original ptr in the happy path. Essentially realloc() has done that for you.
ptr = malloc(sizeof(int));
ptr1 = realloc(ptr, count * sizeof(int));
if (ptr1 == NULL) // reallocated pointer ptr1
{
printf("\nExiting!!");
free(ptr);
exit(0);
}
else
{
ptr = ptr1; // the reallocation succeeded, we can overwrite our original pointer now
}
Applying fixes as edits, based on the good comments below.
Reading this comp.lang.c question, reveals 3 cases:
"When it is able to, it simply gives you back the same pointer you handed it."
"But if it must go to some other part of memory to find enough contiguous space, it will return a different pointer (and the previous pointer value will become unusable)."
"If realloc cannot find enough space at all, it returns a null pointer, and leaves the previous region allocated."
This can be translated directly to code:
int* ptr = (int*)malloc(sizeof(int));
int* tmp = (int*)realloc(ptr, count * sizeof(int));
if(tmp == NULL)
{
// Case 3, clean up then terminate.
free(ptr);
exit(0);
}
else if(tmp == ptr)
{
// Case 1: They point to the same place, so technically we can get away with
// doing nothing.
// Just to be safe, I'll assign NULL to tmp to avoid a dangling pointer.
tmp = NULL;
}
else
{
// Case 2: Now tmp is a different chunk of memory.
ptr = tmp;
tmp = NULL;
}
So, if you think about it, the code you posted is fine (almost). The above code simplifies to:
int* ptr = (int*)malloc(sizeof(int));
int* tmp = (int*)realloc(ptr, count * sizeof(int));
if(tmp == NULL)
{
// Case 3.
free(ptr);
exit(0);
}
else if(ptr != tmp)
{
ptr = tmp;
}
// Eliminate dangling pointer.
tmp = NULL;
Note the extra else if(ptr != tmp), which excludes Case 1, where you wouldn't want to call free(ptr) because ptr and tmp refer to the same location. Also, just for safety, I make sure to assign NULL to tmp to avoid any dangling pointer issues while tmp is in scope.
OP: ... may be different from ptr, or NULL if the request fails.
A: Not always. NULL may be legitimately returned (not a failure), if count is 0.
OP: Is it sufficient to just assume that the reallocated pointer points to a different block of memory and not to the same block.
A: No
OP: Should I put in another condition which will compare the equality of ptr and ptr1 and exclude the execution of the free(ptr) statement?
A: No.
If realloc() returns NULL (and count is not 0), the value of ptr is still valid, pointing to the un-resized data. free(ptr) or not depends on your goals.
If realloc() returns not NULL, do not free(ptr), it is all ready freed.
Example: https://codereview.stackexchange.com/questions/36662/critique-of-realloc-wrapper
#include <assert.h>
#include <stdlib.h>
int ReallocAndTest(char **Buf, size_t NewSize) {
assert(Buf);
void *NewBuf = realloc(*Buf, NewSize);
if ((NewBuf == NULL) && (NewSize > 0)) {
return 1; // return failure
}
*Buf = NewBuf;
return 0;
}
realloc will return the same address to ptr if it have enough space to extend the actual chunk of memory pointed by ptr. Otherwise, it will move the data to the new chunk and free the old chunk. You can not rely on ptr1 being different to ptr. Your program behaves undefined.
If realloc returns another address, it first deallocates the old one so you don't have to do it yourself.
By the way, never cast the return of malloc/realloc :). Your code should be like this:
ptr=malloc(sizeof(int));
ptr=realloc(ptr,count*sizeof(int));
if(ptr==NULL)
{
// error!
printf("\nExiting!!");
// no need to free, the process is exiting :)
exit(0);
}
If realloc moves your data, it will free the old pointer for you behind the scenes. I don't have a copy of the C11 standard, but it is guaranteed in the C99 standard.
You should not free your original pointer if the realloc succeeds. Whether you free that pointer if the realloc fails depends on the needs of your particular application; if you absolutely cannot continue without that additional memory, then this would be a fatal error and you would deallocate any held storage and exit. If, OTOH, you can still continue (perhaps execute a different operation and hope that memory will come available later), the you'd probably want to hold on to that memory and a attempt a another realloc later.
Chapter and verse:
7.22.3.5 The realloc function
Synopsis
1 #include <stdlib.h>
void *realloc(void *ptr, size_t size);
Description
2 The realloc function deallocates the old object pointed to by ptr and returns a
pointer to a new object that has the size specified by size. The contents of the new
object shall be the same as that of the old object prior to deallocation, up to the lesser of
the new and old sizes. Any bytes in the new object beyond the size of the old object have
indeterminate values.
3 If ptr is a null pointer, the realloc function behaves like the malloc function for the
specified size. Otherwise, if ptr does not match a pointer earlier returned by a memory
management function, or if the space has been deallocated by a call to the free or
realloc function, the behavior is undefined. If memory for the new object cannot be
allocated, the old object is not deallocated and its value is unchanged.
Returns
4 The realloc function returns a pointer to the new object (which may have the same
value as a pointer to the old object), or a null pointer if the new object could not be
allocated.
Emphasis added. Note clause 4; the returned pointer may be the same as your original pointer.

free malloc pointer locality issue

I am trying to implement a function set_it_free as shown below, but it would only play with the pointer locally, which does not do what I want, i.e. free and set to null for a pointer. Could you help me to modify my set_it_free to make it work?
int set_it_free(void* pointer, unsigned long level, char* message){
logMessage(level, message);
assert (pointer != NULL); //Never free a NULL pointer
free (pointer); //Free a malloc/calloc/realloc pointer
pointer = NULL; //Set it to NULL for prevention
return 0;}
I would call it like this in main()
int* a = (int* )malloc(sizeof(int));
set_it_free(a);
int set_it_free(void** ppointer, unsigned long level, char* message)
{
logMessage(level, message);
assert ( ppointer != NULL); //Check valid parameter
assert (*ppointer != NULL); //Never free a NULL pointer
free (*ppointer); //Free a malloc/calloc/realloc pointer
*ppointer = NULL; //Set it to NULL for prevention
return 0;
}
And example usage:
sometype* pMyVar = malloc(somesize);
set_it_free(&pMyVar, 5, "freeing myVar");
First of all, in C you should not cast the return values of functions returning void * (like malloc). It can cause subtle runtime problems if you forget to include the correct header files.
Secondly, when you pass the pointer pointer to the function, the pointer itself is passed by value, meaning the pointer is copied and any assignment to the local copy inside set_it_free will be only local. This means the assignment to NULL inside the function is only done to the local pointer variable, the pointer a will still point to the now unallocated memory once the function returns.
For the last problem there are four ways of solving this: The first is to pass the pointer by reference (as in the answer by abelenky), the other is to return the new pointer (similar to what realloc does). The third way is to assign to a after the function call, either NULL or make it point to something else. The fourth way is to simply not use the variable a again.

How can I allocate memory and return it (via a pointer-parameter) to the calling function?

I have some code in a couple of different functions that looks something like this:
void someFunction (int *data) {
data = (int *) malloc (sizeof (data));
}
void useData (int *data) {
printf ("%p", data);
}
int main () {
int *data = NULL;
someFunction (data);
useData (data);
return 0;
}
someFunction () and useData () are defined in separate modules (*.c files).
The problem is that, while malloc works fine, and the allocated memory is usable in someFunction, the same memory is not available once the function has returned.
An example run of the program can be seen here, with output showing the various memory addresses.
Can someone please explain to me what I am doing wrong here, and how I can get this code to work?
EDIT: So it seems like I need to use double pointers to do this - how would I go about doing the same thing when I actually need to use double pointers? So e.g. data is
int **data = NULL; //used for 2D array
Do I then need to use triple pointers in function calls?
You want to use a pointer-to-pointer:
void someFunction (int **data) {
*data = malloc (sizeof (int));
}
void useData (int *data) {
printf ("%p", data);
}
int main () {
int *data = NULL;
someFunction (&data);
useData (data);
return 0;
}
Why? Well, you want to change your pointer data in the main function. In C, if you want to change something that's passed in as a parameter (and have that change show up in the caller's version), you have to pass in a pointer to whatever you want to change. In this case, that "something you want to change" is a pointer -- so to be able to change that pointer, you have to use a pointer-to-pointer...
Note that on top of your main problem, there was another bug in the code: sizeof(data) gives you the number of bytes required to store the pointer (4 bytes on a 32-bit OS or 8 bytes on a 64-bit OS), whereas you really want the number of bytes required to store what the pointer points to (an int, i.e. 4 bytes on most OSes). Because typically sizeof(int *)>=sizeof(int), this probably wouldn't have caused a problem, but it's something to be aware of. I've corrected this in the code above.
Here are some useful questions on pointers-to-pointers:
How do pointer to pointers work in C?
Uses for multiple levels of pointer dereferences?
A common pitfall especially if you moved form Java to C/C++
Remember when you passing a pointer, it's pass by value i.e the value of the pointer is copied. It's good for making changes to data pointed by the pointer but any changes to the pointer itself is just local since it a copy!!
The trick is to use pass the pointer by reference since you wanna change it i.e malloc it etc.
**pointer --> will scare a noobie C programmer ;)
You have to pass a pointer to the pointer if you want to modify the pointer.
ie. :
void someFunction (int **data) {
*data = malloc (sizeof (int)*ARRAY_SIZE);
}
edit :
Added ARRAY_SIZE, at some point you have to know how many integers you want to allocate.
That is because pointer data is passed by value to someFunction.
int *data = NULL;
//data is passed by value here.
someFunction (data);
//the memory allocated inside someFunction is not available.
Pointer to pointer or return the allocated pointer would solve the problem.
void someFunction (int **data) {
*data = (int *) malloc (sizeof (data));
}
int* someFunction (int *data) {
data = (int *) malloc (sizeof (data));
return data;
}
someFunction() takes its parameter as int*. So when you call it from main(), a copy of the value you passed created. Whatever you are modifying inside the function is this copy and hence the changes will not be reflected outside. As others suggested, you can use int** to get the changes reflected in data. Otherway of doing it is to return int* from someFunction().
Apart from using the doublepointer technique, if there's only 1 return param needed rewrite is as following:
int *someFunction () {
return (int *) malloc (sizeof (int *));
}
and use it:
int *data = someFunction ();
Here's the general pattern for allocating memory in a function and returning the pointer via parameter:
void myAllocator (T **p, size_t count)
{
*p = malloc(sizeof **p * count);
}
...
void foo(void)
{
T *p = NULL;
myAllocator(&p, 100);
...
}
Another method is to make the pointer the function's return value (my preferred method):
T *myAllocator (size_t count)
{
T *p = malloc(sizeof *p * count);
return p;
}
...
void foo(void)
{
T *p = myAllocator(100);
...
}
Some notes on memory management:
The best way to avoid problems with memory management is to avoid memory management; don't muck with dynamic memory unless you really need it.
Do not cast the result of malloc() unless you're using an implementation that predates the 1989 ANSI standard or you intend to compile the code as C++. If you forget to include stdlib.h or otherwise don't have a prototype for malloc() in scope, casting the return value will supress a valuable compiler diagnostic.
Use the size of the object being allocated instead of the size of the data type (i.e., sizeof *p instead of sizeof (T)); this will save you some heartburn if the data type has to change (say from int to long or float to double). It also makes the code read a little better IMO.
Isolate memory management functions behind higher-level allocate and deallocate functions; these can handle not only allocation but also initialization and errors.
Here you are trying to modifying the pointer i.e. from "data == Null" to "data == 0xabcd"some other memory you allocated. So to modify data that you need pass the address of data i.e. &data.
void someFunction (int **data) {
*data = (int *) malloc (sizeof (int));
}
Replying to your additional question you edited in:
'*' denotes a pointer to something. So '**' would be a pointer to a pointer to something, '***' a pointer to a pointer to a pointer to something, etc.
The usual interpretation of 'int **data' (if data is not a function parameter) would be a pointer to list of int arrays (e.g. 'int a [100][100]').
So you'd need to first allocate your int arrays (I am using a direct call to malloc() for the sake of simplicity):
data = (int**) malloc(arrayCount); //allocate a list of int pointers
for (int i = 0; i < arrayCount; i++) //assign a list of ints to each int pointer
data [i] = (int*) malloc(arrayElemCount);
Rather than using double pointer we can just allocate a new pointer and just return it, no need to pass double pointer because it is not used anywhere in the function.
Return void * so can be used for any type of allocation.
void *someFunction (size_t size) {
return malloc (size);
}
and use it as:
int *data = someFunction (sizeof(int));
For simplicity, let me call the above single pointer parameter p
and the double pointer pp (pointing to p).
In a function, the object that p points to can be changed and the change goes out of
the function. However, if p itself is changed, the change does not
leave the function.
Unfortunately, malloc by its own nature, typically
changes p. That is why the original code does not work.
The correction (58) uses the pointer pp pointing to p. in the corrected
function, p is changed but pp is not. Thus it worked.

Resources