1

I want to use realloc to free memory from the end of a chunk of memory. I understand that the standard does not require that realloc succeed, even if the memory requested is lower than the original malloc/calloc call. Can I just realloc, and then if it fails returns the original?

// Create and fill thing1
custom_type *thing1 = calloc(big_number, sizeof(custom_type));
// ...

// Now only the beginning of thing1 is needed
assert(big_number > small_number);
custom_type *thing2 = realloc(thing1, sizeof(custom_type)*small_number);

// If all is right and just in the world, thing1 was resized in-place
// If not, but it could be copied elsewhere, this still works
if (thing2) return thing2;

// If thing2 could not be resized in-place and also we're out of memory,
// return the original object with extra garbage at the end.
return thing1;

This is not a minor optimization; the part I want to save might be as little as 5% of the length of the original, which could be several gigabytes.


Note: Using realloc to shrink the allocated memory and Should I enforce realloc check if the new block size is smaller than the initial? are similar but do not address my particular question.

Community
  • 1
  • 1
Charles
  • 11,269
  • 13
  • 67
  • 105
  • 4
    It is [clearly mentioned](http://en.cppreference.com/w/c/memory/realloc) that if `realloc` fails then original pointer remains as it is . – ameyCU Mar 17 '16 at 16:25
  • If you know how big `thing1` is you are better served by `thing2 = malloc (actualsizeof thing1); memcpy (thing2, thing1, actualsizeof thing1);` – David C. Rankin Mar 17 '16 at 16:27
  • The `realloc` function will not touch the original memory unless it succeeds in changing the allocation, This is why it's so important to not reassign to the pointer you use to call `realloc` (which you thankfully don't). – Some programmer dude Mar 17 '16 at 16:28
  • 1
    @DavidC.Explicitly allocating and copying and not calling `realloc` will not handle the case where `realloc` can actually shrink the already allocated memory. – Some programmer dude Mar 17 '16 at 16:30
  • 1
    @DavidC.Rankin I was hoping to avoid the `memcpy` entirely, but with a fallback in case it doesn't work. As it happens I don't know the size of `thing2` (the initial segment of `thing1`) in advance. And ideally I'd like this to work when there's enough memory for `thing1` but not both `thing1` and `thing2`. – Charles Mar 17 '16 at 16:34
  • @DavidC.Rankin: What is the difference to `realloc` doing the copy? It is well possible it can use a better way to copy , e.g. just partially copying. – too honest for this site Mar 17 '16 at 16:45
  • @DavidC.Rankin absolutely negative. On say glibc, malloc of large areas is implemented on top of `mmap`; shrinking `realloc` there would just unmap pages. Your solution can run out of memory while realloc would continue work. – Antti Haapala -- Слава Україні Mar 17 '16 at 16:49
  • I think you guys are all missing the point of my comment. It is doing the exact same thing a `realloc`. The only difference is that I allocate a second block of memory, copy the memory to the new block, and free the original. That implies checking that the second allocation succeeds before freeing the original. There is *no difference* between doing it separately or doing it with `realloc`. I would also use `realloc`, it was just intended as an explanation of what `realloc` would accomplish. – David C. Rankin Mar 17 '16 at 17:07
  • @ David C. Rankin: No, it doesn't. *realloc()* could, in case your allocation is the last before brk (the ideal case), actually see you try to shrink, release the end of the allocated block, and even *lower* brk after that activity. Your version doesn't have that chance, the system would always have to *raise* brk and end up with a "hole" that cannot be handed back to the OS in all cases. ( am not saying that would be guaranteed to happen) – tofro Mar 17 '16 at 17:50

1 Answers1

7

Yes, you can. If realloc() is not successful, the original memory region is left untouched. I usually use code like this:

/* shrink buf to size if possible */
void *newbuf = realloc(buf, size);
if (newbuf != NULL)
    buf = newbuf;

Make sure that size is not zero. The behaviour of realloc() with a zero-length array depends on the implementation and can be a source of trouble. See this question for details.

Community
  • 1
  • 1
fuz
  • 88,405
  • 25
  • 200
  • 352
  • A shrink may shrink to 0, in which case returning `NULL` is not an error and `buf` should be considered free'd. IMO, more code needed to properly handle `size == 0 || buf == NULL` – chux - Reinstate Monica Mar 17 '16 at 19:23
  • 1
    @chux Indeed, this is a well-known issue, let me add that this assumes `size > 0`. – fuz Mar 17 '16 at 19:24