I found that some people and references like books state that if p != NULL
and p
origins from previous allocation (e.g. by malloc
), then realloc(p, 0)
is equivalent to free(p)
on GNU/Linux. To support this thesis man realloc
states exactly in that manner (emphasis mine going forward):
The realloc() function changes the size of the memory block pointed to by ptr to size bytes. The contents will be unchanged in the range from the start of the region up to the minimum of the old and new sizes. If the new size is larger than the old size, the added memory will not be initialized. If ptr is NULL, then the call is equivalent to malloc(size), for all values of size; if size is equal to zero, and ptr is not NULL, then the call is equivalent to free(ptr). Unless ptr is NULL, it must have been returned by an earlier call to malloc(), calloc() or realloc(). If the area pointed to was moved, a free(ptr) is done.
As you may find in this question, the C Standard does not define precisely what should happen and actual behavior is implementation-defined. More specifically:
The C11 §7.22.3/p1 Memory management functions says:
If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.
and C11 §7.22.3.5 The realloc function contains:
3) (...) If memory for the new object cannot be allocated, the old object is not deallocated and its value is unchanged.
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.
I wrote some basic code to find out actual behavior with help of mcheck
, memory checker, that is supplied with glibc
:
#include <mcheck.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int a = 5;
int *p, *q;
mtrace();
p = malloc(sizeof(int));
q = &a;
printf("%p\n", (void *) p);
printf("%p\n", (void *) q);
q = realloc(p, 0);
printf("%p\n", (void *) p);
printf("%p\n", (void *) q);
return 0;
}
and results are:
$ gcc -g check.c
$ export MALLOC_TRACE=report
$ ./a.out
0xfd3460
0x7ffffbc955cc
0xfd3460
(nil)
[grzegorz@centos workspace]$ mtrace a.out report
Memory not freed:
-----------------
Address Size Caller
0x0000000000fd3460 0x4 at /home/grzegorz/workspace/check.c:12
As you may see q
was set to NULL
. It seems that free()
was not really called. In fact it can't be unless my interpretation is incorrect: since realloc
has returned NULL
pointer, the new object could not have been allocated, which implies that:
the old object is not deallocated and its value is unchanged
Is this correct?
This means realloc(ptr,0) may not really free/deallocate the memory, and thus it should never be used as a replacement for free .
Once you call realloc() , you do not have to free() the memory addressed by pointer passed to realloc() - you have to free() the memory addressed by the pointer realloc() returns. (Unless realloc() returns NULL , in which case the original block of memory - passed to realloc() - has to be free() 'd.)
If realloc succeeds, it will take ownership of the incoming memory (either manipulating it or free ing it) and return a pointer that can be used ("owned") by the calling function. If realloc fails (returns NULL ), your function retains ownership of the original memory and should free it when it's done with it.
What is the difference between realloc() and free() The free subroutine frees a block of memory previously allocated by the malloc subroutine. Undefined results occur if the Pointer parameter is not a valid pointer. If the Pointer parameter is a null value, no action will occur.
Edit: Your glibc seems to be a pre-2.18, in 2.18 a bug was fixed in mtrace (see here). On a 2.20 glibc your test program reports: "No memory leaks."
free
is called in glibc. From the sources of current glibc 2.21 (here and here):
/*
REALLOC_ZERO_BYTES_FREES should be set if a call to
realloc with zero bytes should be the same as a call to free.
This is required by the C standard. Otherwise, since this malloc
returns a unique pointer for malloc(0), so does realloc(p, 0).
*/
#ifndef REALLOC_ZERO_BYTES_FREES
#define REALLOC_ZERO_BYTES_FREES 1
#endif
void *
__libc_realloc (void *oldmem, size_t bytes)
{
mstate ar_ptr;
INTERNAL_SIZE_T nb; /* padded request size */
void *newp; /* chunk to return */
void *(*hook) (void *, size_t, const void *) =
atomic_forced_read (__realloc_hook);
if (__builtin_expect (hook != NULL, 0))
return (*hook)(oldmem, bytes, RETURN_ADDRESS (0));
#if REALLOC_ZERO_BYTES_FREES
if (bytes == 0 && oldmem != NULL)
{
__libc_free (oldmem); return 0;
}
#endif
Though my intepretation of "returned NULL
" case seems to be correct (see my edit below), glibc
developers decided to keep it consistent with previous C89 Standard and rejected to conform with C99/C11:
There is no way this will be changed. This is how it has been implemented forever. C should document existing practice. Changing it would mean introducing memory leaks.
Also mcheck
indication was misleading, as other test-case has showed that memory is effectively freed by realloc
:
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *p, *q;
p = malloc(20 * sizeof(int));
malloc_stats();
putchar('\n');
q = realloc(p, 0);
malloc_stats();
return 0;
}
Here, output is:
$ gcc check.c
$ ./a.out
Arena 0:
system bytes = 135168
in use bytes = 96
Total (incl. mmap):
system bytes = 135168
in use bytes = 96
max mmap regions = 0
max mmap bytes = 0
Arena 0:
system bytes = 135168
in use bytes = 0
Total (incl. mmap):
system bytes = 135168
in use bytes = 0
max mmap regions = 0
max mmap bytes = 0
EDIT:
As hvd pointed in comment, the ISO/IEC Working Group had some discussion, materialized as Defect Report #400. The proposed changes may likely allow existing practice of glibc
in the future revision of the C Standard (or possibly as Technical Corrigendum 1 for C11).
What I really like about DR #400 is proposition to:
Add to subsection 7.31.12 a new paragraph (paragraph 2):
Invoking realloc with a size argument equal to zero is an obsolescent feature.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With