Search code examples
cpointersmemory-managementdynamic-memory-allocationrealloc

Does realloc mutate its arguments


  1. Does realloc mutate its first argument?
  2. Is mutating the first argument dependent on the implementation?
  3. Is there a reason it should not be const? As a counter example memcpy makes its src argument const.

ISO C standard, section 7.20.3 Memory management functions, does not specify. The Linux man page for realloc does not specify.

#include <stdio.h>
#include <stdlib.h>

int main() {
  int* list = NULL;
  void* mem;
  mem = realloc(list, 64);
  printf("Address of `list`: %p\n", list);
  list = mem;
  printf("Address of `list`: %p\n", list);
  mem = realloc(list, 0);
  printf("Address of `list`: %p\n", list);
  // free(list);  // Double free
  list = mem;
  printf("Address of `list`: %p\n", list);
}

When I run the above code on my Debian laptop:

  • The first printf is null.
  • The second printf has an address.
  • The third printf has the same address as the second.
  • In accordance with the spec, trying to free the address results in a double free error.
  • The forth printf is null.

Solution

  • The function does not change the original pointer because it deals with a copy of the pointer. That is the pointer is not passed by reference.

    Consider the following program

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void) 
    {
        int *p = malloc( sizeof( int ) );
        *p = 10;
    
        printf( "Before p = %p\n", ( void * )p );
    
        char *q = realloc( p, 2 * sizeof( int ) );
    
        printf( "After  p = %p\n", ( void * )p );
    
        free( q );
    
        return 0;
    }
    

    Its output is

    Before p = 0x5644bcfde260
    After  p = 0x5644bcfde260
    

    As you see the pointer p was not changed.

    However the new pointer q can have the same value as pointer p had before the call of realloc.

    From the C Standard (7.22.3.5 The realloc function)

    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.

    Of course if you will write

        p = realloc( p, 2 * sizeof( int ) );
    

    instead of

        char *q = realloc( p, 2 * sizeof( int ) );
    

    then it is evident that in general the new value of pointer p can differ from the old value of p (though can be the same according to the quote). For example if the function was unable to reallocate memory. In this case a memory leak will occur provided that initial value of the pointer p was not equal to NULL. Because in this case (when the initial value of the pointer was not equal to NULL) the address of the early allocated memory will be lost.

    The old memory is not deallocated if a new memory extent can not be allocated because the function needs to copy the old content to the new extent of memory.

    From the C Standard (7.22.3.5 The realloc function)

    If memory for the new object cannot be allocated, the old object is not deallocated and its value is unchanged.

    Pay attention to that this call

    mem = realloc(list, 0);
    

    does not necessary return NULL.

    From the C Standard (7.22.3 Memory management functions)

    If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.