Home > database >  malloc vs realloc - what is the best practice?
malloc vs realloc - what is the best practice?

Time:12-28

I have a variable where the size is determined in run-time.

Is it generally better to realloc it every time a new element is added like:

array_type * someArray;
int counter = 0;

//before a new element is added:
counter   ;
someArray = realloc(someArray, counter * sizeof(array_type));

Or allocate more memory than probably needed using malloc once:

array_type * someArray = malloc(ENOUG_MEMORY * sizeof(array_type));

What is best in terms of efficiency(speed), readability and memory-management? And why?

CodePudding user response:

realloc could occasionally be useful in the past, with certain allocation patterns in single-threaded code. Most modern memory managers are optimized for multi-threaded programs and to minimize fragmentation, so, when growing an allocation, a realloc will almost certainly just allocate a new block, copy the existing data, and then free the old block. So there's no real advantage to trying to use realloc.

Increasing the size one element at a time can create an O(n^2) situation. In the worst case, the existing data has to be copied each time. It would be better to increase the size in chunks (which is still O(n^2) but with a smaller constant factor) or to grow the allocation geometrically (which gives an O(n) amortized cost).

Furthermore, it's difficult to use realloc correctly.

someArray = realloc(someArray, counter * sizeof(array_type));

If the realloc fails, someArray is set to NULL. If that was your only copy of the pointer to the previously allocated memory, you've just lost it. You won't be able to access the data you had already placed, and you can't free the original allocation, so you'll have a memory leak.

CodePudding user response:

What is best in terms of efficiency(speed), readability and memory-management? And why?

There is no general best. Specific best depends on your specific application and use case and environment. You can throw wars over perfect realloc ratio and decide if you need realloc at all.

Remember about rules of optimization. You do not optimize. Then, you do not optimize, without measuring first. The best in any terms can be measured for your specific setup, your specific environment, your specific application that uses specific operating system and *alloc implementation.

what is the best practice?

Allocating a constant amount of memory (if it's small enough) is static. It's just an array. Refactor you application to just:

array_type someArray[ENOUGH_MEMORY];

If you do not want to over allocate (or ENOUGH_MEMORY is big enough), then use realloc to add one element, as presented.

If you want, "optimize" by not calling realloc that often and over allocating - it seems that ratio 1.5 is the most preferred from the linked thread above. Still, it's highly application specific - I would over allocate on Linux, I would not when working on STM32 or other bare metal.

CodePudding user response:

I would use realloc with caution. Calling realloc in general leads to:

  1. allocating completely new block
  2. copying all data from old to new location
  3. releasing (freeing) the initial block.

All combined could be questionable from performance perspective, depending on the app, volume of data, response requirements.

In addition, in case of realloc failure, the return value is NULL which means that allocation to new block is not straightforward (indirection is required). E.g.

    int *p = malloc(100 * sizeof *p);
    if (NULL == p) 
    {
        perror("malloc() failed");
        return EXIT_FAILURE;
    }
 
    do_something_with_p(p);

    /* Reallocate array to a new size
     * Using temporary pointer in case realloc() fails. */
    {
        int *temp = realloc(p, 100000 * sizeof *temp);

        if (NULL == temp)
        {
            perror("realloc() failed");
            free(p);
            return EXIT_FAILURE;
        }      

        p = temp;
    }

CodePudding user response:

malloc vs realloc - what is the best practice?

Helper functions

When writing robust code, I avoid using library *alloc() functions directly. Instead I form helper functions to handle various use cases and take care of edge cases, parameter validation, etc.

Within these helper functions, I use malloc(), realloc(), calloc() as building blocks, perhaps steered by implementation macros, to form good code per the use case.

This pushes the "what is best" to a narrower set of conditions where it can be better assessed - per function. In the growing by 2x case, realloc() is fine.

Example:

// Optimize for a growing allocation
// Return new pointer.
// Allocate per 2x *nmemb * size.
// Update *nmemb_new as needed.
// A return of NULL implies failure, old not deallocated.
void *my_alloc_grow(void *ptr, size_t *nmemb, size_t size) {
  if (nmemb == NULL) {
    return NULL;
  }
  size_t nmemb_old = *nmemb;

  if (size == 0) { // Consider array elements of size 0 as error
    return NULL;
  }
  if (nmemb_old > SIZE_MAX/2/size)) {
    return NULL;
  }

  size_t nmemb_new = nmemb_old ? (nmemb_old * 2) : 1;
  unsigned char *ptr_new = realloc(ptr, nmemb_new * size);
  if (ptr_new == NULL) {
    return NULL;
  }

  // Maybe zero fill new memory portion.
  memset(ptr_new   nmemb_old * size, 0, (nmemb_new - nmemb_old) * size);

  *nmemb = nmemb_new;
  return ptr_new;
}

Other use cases.

/ General new memory
void *my_alloc(size_t *nmemb, size_t size); // General new memory
void *my_calloc(size_t *nmemb, size_t size); // General new memory with zeroing

// General reallocation, maybe more or less.
// Act like free() on nmemb_new == 0.
void *my_alloc_resize(void *ptr, size_t *nmemb, size_t nmemb_new, size_t size);
  • Related