Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can realloc fail (return NULL) when trimming?

If do the next:

int* array = malloc(10 * sizeof(int));

and them I use realloc:

array = realloc(array, 5 * sizeof(int));

On the second line (and only it), can it return NULL?

like image 302
Novak Avatar asked Aug 25 '12 20:08

Novak


People also ask

Can realloc return null?

Return ValueIf size is 0, the realloc() function returns NULL. If there is not enough storage to expand the block to the given size, the original block is unchanged and the realloc() function returns NULL.

What does realloc return if failed?

realloc returns a void pointer to the reallocated (and possibly moved) memory block. If there is not enough available memory to expand the block to the given size, the original block is left unchanged, and NULL is returned.

What can cause realloc to fail?

The chances that malloc or realloc fail are negligible on most modern system. This only occurs when you run out of virtual memory. Your system will fail on accessing the memory and not on reserving it.

What happens if you realloc null?

realloc() returns a null pointer if the new block could not be allocated as requested; in this case, it does not release the old block, and your program can continue using it. If the first argument is a null pointer, realloc() allocates a new memory block, behaving similarly to malloc() .


3 Answers

Yes, it can. There are no implementation guarantees on realloc(), and it can return a different pointer even when shrinking.

For example, if a particular implementation uses different pools for different object sizes, realloc() may actually allocate a new block in the pool for smaller objects and free the block in the pool for larger objects. Thus, if the pool for smaller objects is full, it will fail and return NULL.


Or it may simply decide it's better to move the block

I just used the following program to get size of actually allocated memory with glibc:

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

int main()                                                                   
{                                                                            
    int n;                                                                   

    for (n = 0; n <= 10; ++n)                                                
    {                                                                        
        void* array = malloc(n * sizeof(int));                               
        size_t* a2 = (size_t*) array;                                        

        printf("%d -> %zu\n", n, a2[-1]);                                    
    }                                                                        
}

and for n <= 6, it allocates 32 bytes, and for 7-10 it is 48.

So, if it shrank int[10] to int[5], the allocated size would shrink from 48 to 32, effectively giving 16 free bytes. Since (as it just has been noted) it won't allocate anything less than 32 bytes, those 16 bytes are lost.

If it moved the block elsewhere, the whole 48 bytes will be freed, and something could actually be put in there. Of course, that's just a science-fiction story and not a real implementation ;).


The most relevant quote from the C99 standard (7.20.3.4 The realloc function):

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.

'May' is the key-word here. It doesn't mention any specific circumstances when that can happen, so you can't rely on any of them, even if they sound obvious at a first glance.


By the way, I think you could consider realloc() somewhat deprecated. If you'd take a look at C++, the newer memory allocation interfaces (new / delete and allocators) don't even support such a thing. They always expect you to allocate a new block. But that's just a loose comment.

like image 121
Michał Górny Avatar answered Oct 22 '22 05:10

Michał Górny


The other answers have already nailed the question, but assuming you know the realloc call is a "trimming", you can wrap it with:

void *safe_trim(void *p, size_t n) {
    void *p2 = realloc(p, n);
    return p2 ? p2 : p;
}

and the return value will always point to an object of size n.

In any case, since the implementation of realloc knows the size of the object and can therefore determine that it's "trimming", it would be pathologically bad from a quality-of-implementation standpoint not to perform the above logic internally. But since realloc is not required to do this, you should do it yourself, either with the above wrapper or with analogous inline logic when you call realloc.

like image 30
R.. GitHub STOP HELPING ICE Avatar answered Oct 22 '22 07:10

R.. GitHub STOP HELPING ICE


The language (and library) specification makes no such guarantee, just like it does not guarantee that a "trimming" realloc will preserve the pointer value.

An implementation might decide to implement realloc in the most "primitive" way: by doing an unconditional malloc for a new memory block, copying the data and free-ing the old block. Obviously, such implementation can fail in low-memory situations.

like image 3
AnT Avatar answered Oct 22 '22 07:10

AnT