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
?
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
?
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.
realloc
deprecated just because C++ doesn't have an analogue in the new/delete regime. C++ is a very different language from C, and in particular, support for moving objects in C++ would require some way for the implementation to notify an object that it's being relocated and allow it to update its own internal references. C on the other hand doesn't automate or encapsulate any of this, so it's up to the caller (and thus perfectly fine) to be responsible for whether the object's contents need to be changed after realloc
. –
Condition 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
.
realloc()
just failed, there will be soon a random failing malloc()
… –
Noyade malloc
is going to fail somewhere else, that will (in a robust program, at least) be at a point where the program can handle the failure case, back out any partial work, and report the error. –
Condition realloc
would be utterly useless in robust programs. This is actually an extremely common form of memory leak (i.e. p=realloc(p,newsize);
which loses the old memory if realloc
fails). –
Condition realloc(3)
. I've just fixed a leak in my code where I didn't free the old memory when realloc fails (although it's not extremely important since I call exit
right after). –
Restharrow realloc()
that shrinks a block, the block should be left with a size close to what's requested. If the block isn't shrunk very much, the application shouldn't care about whether the block was actually shrunk. –
Brazell NULL
does not always mean failure. If n==0
, it is possible that the old memory is freed and no longer valid. –
Malcolmmalcom realloc
with size 0. –
Condition 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.
Don't count on it. The standard makes no such provision; it merely states "or a null pointer if the new object could not be allocated".
You'd be hard-pressed to find such an implementation, but according to the standard it would still be compliant.
I suspect there may be a theoretical possibility for failure in the scenario you describe.
Depending on the heap implementation, there may be no such a thing as trimming an existing allocation block. Instead a smaller block is allocated first, then the data is copied from the old one, and then it's freed.
For instance this may be the case with bucket-heap strategy (used by some popular heaps, such as tcmalloc).
realloc
never fails. –
Condition tcmalloc.cc
function do_realloc()
which is used in tc_realloc()
, (github.com/gperftools/gperftools/blob/master/src/…) –
Malcolmmalcom A bit late, but there is at least one popular implementation which realloc()
with a smaler size can fail: TCMalloc. (At least as far as i understand the code)
If you read the file tcmalloc.cc
, in the function do_realloc_with_callback()
, you will see that if you shrink enough (50% of alloced memory, otherwise it will be ignored), TCMalloc will alloc the new memory first (and possible fail) and then copy it and remove the old memory.
I do not copy the source code, because i am not sure if the copyrights (of TCMalloc and Stackoverflow) will allow that, but here is a link to the source (revision as at May 17, 2019).
realloc
will not fails in shrinking the existing memory, so it will not return NULL
. It can return NULL
only if fails during expansion.
But shrinking can fail in some architecture, where realloc
can be implemented in a different manner like allocating a smaller size memory separately and freeing the old memory to avoid fragmentation. In that case shrinking memory can return NULL. But its very rare implementation.
But its better to be in a safer side, to keep NULL
checks after shrinking the memory also.
© 2022 - 2024 — McMap. All rights reserved.