Should I enforce realloc check if the new block size is smaller than the initial?
Asked Answered
N

6

9

Can realloc fail in this case?

int *a = NULL;

a = calloc(100, sizeof(*a));
printf("1.ptr: %d\n", a);

a = realloc(a, 50 * sizeof(*a));
printf("2.ptr: %d\n", a);

if(a == NULL){
    printf("Is it possible?\n");
}

return (0);

}

The output in my case is:

1.ptr: 4072560
2.ptr: 4072560

So 'a' points to the same adress. So should I enforce realloc check?

Later edit:

  • Using MinGW compiler under Windows XP.
  • Is the behaviour similar with gcc on Linux ?

Later edit 2: Is it OK to check this way ?

int *a = NULL, *b = NULL;

a = calloc(100, sizeof(*a));
b = realloc(a, 50 * sizeof(*a));

if(b == NULL){
    return a;
}
a = b;
return a;
Nathan answered 29/3, 2010 at 20:54 Comment(1)
This is a duplicate of #1736933 but is a better question and answers in my opinion.Puree
A
7

Yes, you should always enforce a check on realloc, or any other memory allocation for that matter.

The current behavior of re-using the same address is an implementation detail that should not be relied upon. Doing so is just opening yourself up for bugs when either the library switches it's implementation or you move to a new platform.

Is it likely this will ever fail? Probably not, I'd be astounded if you could find a case that it does. However that doesn't mean it won't. Wrapping realloc in a function which automatically does the check for every operation is simple enough that there's no reason to not do it.

void* xrealloc(void* ptr, size_t size) {
  ptr = realloc(ptr, size);
  if ( !ptr ) {
    exit(EXIT_FAILURE);
  }
  return ptr;
}
Andesine answered 29/3, 2010 at 20:57 Comment(7)
Can you give an example when the reallocation of a memory patch that is smaller could fail?Secret
@hanno, I can't and frankly I'd be surprised if it ever could. But using that as proof that it won't fail is making an argument from igonorance. I'd prefer to go the route of using a wrapper function which checks the return in all cases. It's simply safer to do so.Andesine
I also think is very unlikely to ever happen. Thanks for the xrealloc 'trick'. Can you please check to see if my check is valid.Nathan
@nomemory your check is valid and looks OK to me assuming it's OK to return a with a larger size.Andesine
@Andesine but if realloc fails, i want to hold a reference to a. Am I wrong ? I may misunderstood the whole free issue.Nathan
@nomemory if realloc fails it should not alter the contents of the original pointer. So a will still point to the return value of callocAndesine
@hanno: if your malloc implementation uses a bibop allocator and the smaller size needs to go on a new page as a result, but the page allocator fails to allocate a page, the realloc could fail. Of course, a smart allocator might choose to not free the old block and just return it, but its conceivable it could return NULLMcguigan
E
6

It would be surprising if realloc failed when passed a size smaller than the original allocation, but nothing in the C standard (7.20.3.4) guarantees that it will always succeed:

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.

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 the calloc, malloc, or realloc 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

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.

A very simple conforming implementation of realloc would be this:

void *realloc(void *ptr, size_t size)
{
    void *new_ptr= malloc(size);
    if (new_ptr && ptr)
    {
        size_t original_size= _get_malloc_original_size(ptr);
        memcpy(new_ptr, ptr, min(original_size, size));
        free(ptr);
    }

    return new_ptr;
}

Under low memory conditions (or any conditions under which malloc would return NULL), this would return NULL.

It would also be a very simple optimization to return the same pointer if the size of the original allocation is greater than or equal to the requested size. But nothing in the C Standard dictates that.

Ephemeron answered 29/3, 2010 at 21:22 Comment(5)
Umm...where does it say a shrinking realloc will always succeed? I quote the same text and come to the inverse conclusion. I fear one of us is wrong.Antung
@Jonathan Leffler: Umm...yes, where does it say that? I think you're both in the same boat here.Andreas
@Jonathan, I said "It would be surprising if realloc failed when passed a size smaller than the original allocation", not that shrinking realloc would always succeed. :) I suppose the wording is subtle enough to confuse.Ephemeron
Could a standards-compliant implementation ever fail (leaving the old memory allocated) if asked to realloc a memory block to size zero? Since a null return would be legitimate in case of success, would there be any other means by which one could determine that the memory block still needed to be freed?Cytaster
@supercat, yes, in theory it could. It would really depend on if malloc(0) returns a null pointer. If it does, then you cannot determine if realloc(ptr, 0) succeeded or not. If it does not, then you can: success= (realloc(ptr, 0)!=0).Ephemeron
D
2

It is good practice to check the return value of realloc in any case (the specification doesn't say you are safer if you shrink your memory block than if you expand it). But you should be careful NOT to lose the initial pointer (which you do, in your case), as you'd then be completely unable to release it.

Dextrogyrate answered 29/3, 2010 at 20:59 Comment(0)
A
2

The C99 standard §7.20.3.4 (realloc) says:

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.

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 the calloc, malloc, or realloc 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

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.

Note that the old object is deallocated; the new object may happen to point to the same location as the old. And there could be problems. It is pretty unlikely, but it is far simpler to go with a rule 'always' than to have odd exceptions.

The normal counter-argument is "if this cannot fail, it means I have an error path that I cannot test". Up to a point, that is true. However, it could be that there has been some trampling of the memory so that the allocation cannot succeed - because the control information has been corrupted. More likely you'll just get a core dump, but maybe the code is robust enough to be able to avoid that. (I assume the hard coded 100 and 50 are for purposes of asking the question; real life code would not over-allocate when it knows how much it really needs.)

Where the two calls to 'realloc()' are adjacent, as here, there is very little room for anything to go wrong. However, real life working code would have some operations in between the two - and that code could cause the second 'realloc()' to fail.

Regarding your 'Edit 2'...

The code might be better written as:

if (b != NULL)
    a = b;
return a;

But the basic concept is OK. Note that the standard explicitly says that the original allocation is safe if the new one cannot be created.

Antung answered 29/3, 2010 at 21:21 Comment(1)
Thanks for your answer. The code is 'dummy', just to understand the concept(s).Nathan
W
1

The time it takes to do the check is so small compared to the time spent in realloc() that I can't even see why it'd be a problem. Or do you want to reduce the number of lines of code?

Winthrop answered 29/3, 2010 at 21:28 Comment(0)
Z
1

realloc() can return NULL easily enough on size reduction.

void *ptr = malloc(10);
ptr = realloc(ptr, 0);
if (ptr == NULL) {
  puts("Failure because return value is NULL? - not really");
}

realloc(any_pointer, 0) could return NULL or maybe some not-NULL pointer, it is implementation defined.

That is why realloc()/malloc() failure should not be a simple test of if (ptr == NULL) but

void *ptr = malloc(newsize); // or realloc(..., newsize)
if (ptr == NULL && newsize > 0) {
  exit(0); // Handle OOM;
}

Because of this ambiguity, should code want to make a realloc() wrapper, recommend something like:

void *xrealloc(void *ptr, size_t newsize, bool *falure) {
  *failure = 0;
  if (newsize > 0) {
    void *tmp = realloc(ptr, newsize);
    if (tmp == NULL) {
      *failure = 1;
      return ptr;  // old value
    }
    return tmp;  // new value
  } 
  free(ptr);
  return NULL; // new value
  }

Getting NULL on a realloc() with reduced size therefore is not really a failure and so this answer only tangentially applies, but OP's question was "... enforce realloc check if the new block size is smaller than the initial?" and then used the less trustworthy if (ptr == NULL) paradigm.

Zahara answered 13/9, 2014 at 3:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.