Proper usage of realloc()
Asked Answered
M

6

28

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?

Michale answered 8/1, 2014 at 21:16 Comment(0)
S
38

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
}
Screwball answered 8/1, 2014 at 21:22 Comment(7)
In the man page of realloc i couldnt find the info that it would itself call upon free()...thnx anywayMichale
@Michale Really? On my Mac man realloc: "... If there is not enough room to enlarge the memory allocation pointed to by ptr, realloc() creates a new allocation, copies as much of the old data pointed to by ptr as will fit to the new allocation, frees the old allocation, and returns a pointer to the allocated memory. ..."Absence
Don't cast the result of malloc and realloc.Bottoms
I feel the need to second zenith's suggestion. If it weren't for the casts, you'd probably have two more +1s.Herdsman
I was attempting to limit the number of changes to the original code as possible. I completely agree on the no-casting part.Screwball
heh... the typecasts aren't just an eyesore, but could cause issues on some obscure systems and serve no purpose... but you kept them to keep the code original. Now spaces have been introduced to make it less of an eyesore, which also serve no functional purpose yet won't break anything (unlike typecasts, which will). My point is, if you would allow spaces to be added to make the code stylistically appealing, would you also allow unnecessary typecasts to be removed to make the code more portable in addition to stylistically appealing? I'm not going to edit, but would you revert if I did?Herdsman
/me cries "Uncle!". Casts removed.Screwball
F
5

Applying fixes as edits, based on the good comments below.

Reading this comp.lang.c question, reveals 3 cases:

  1. "When it is able to, it simply gives you back the same pointer you handed it."
  2. "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)."
  3. "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.

Footstep answered 8/1, 2014 at 21:55 Comment(5)
You didn't obey the "and the previous pointer value will become unusable" part. In the ptr != tmp case, the free(ptr) is wrong.Heteronomous
Agree with @Heteronomous that this answer is dangerous, since you must not call free(ptr) in case 2.Aliaalias
Good call, applied as an edit, and comment upvotes for the both of you.Footstep
@Footstep +1 for setting the tmp pointer to NULL. I just have been badly bitten when I changed a realloc'd pointer within a function to static. That caused a later segfault with subsequent realloc calls (memory was free'd between function calls), as now the pointer retained its old (dangling) value. Took me while to figure it out... I have a habit of using realloc over malloc often, but one has to be vigilant to make sure the first realloc call (without a prior malloc) does get a NULL pointer.Cough
I think exit(1) would be more appropriate here.Fusilier
P
2

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;
}
Pore answered 8/1, 2014 at 21:21 Comment(0)
C
1

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);
}
Contrapuntist answered 8/1, 2014 at 21:23 Comment(6)
There is a problem there: if ptr is NULL, free(ptr) does not make sense.Matchbook
Well, that actually make sense. Besides, the program is about to exit so no problem not freeing it :) Fixing it :)Contrapuntist
This is an implementation detail and not true in general: "realloc will return the same address to ptr if it have enough space to extend the actual chunk of memory pointed by ptr." Implementations that segregate allocations by size (e.g. OpenBSD's omalloc) are unlikely to ever return the original pointer except when the original and new sizes match.Biquadrate
For adding note on casting return of malloc() etc. +1Feigin
ptr=realloc(ptr,count*sizeof(int)); is broken; when realloc returns NULL (which is not an address because it doesn't point to an object), you leak the memory that is the old object. The OpenGroup manual states: "If the space cannot be allocated, the object shall remain unchanged." The C standard states: "If memory for the new object cannot be allocated, the old object is not deallocated and its value is unchanged."Herdsman
@Herdsman Not broken. If the failure to get memory (realloc returns NULL) is cause for program termination (as in the example by Paulo Bu), there will be no memory leak. When failure to obtain memory is a terminal problem to a program's run, this way of using realloc is fine.Cough
S
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.

Stringhalt answered 8/1, 2014 at 21:21 Comment(1)
The C11 standard draft is n1570.pdf, which you can find by googling. I find it helpful to cite using links (e.g. clicking this link will take you to the realloc section of n1570.html, which was converted from n1570.pdf).Herdsman
V
0

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.

Val answered 8/1, 2014 at 22:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.