Reputation: 882
I have been writing code where data are allocated in many loops, and found using realloc()
quite convenient for the purpose because it handles initial and mid-loop data consistently without adding conditions for freeing and allocating new pointers.
I wrote this little test program:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
char *x = NULL;
x = realloc (x, 0);
printf ("Address of realloc (NULL, 0): %p\n", x);
assert (x != NULL);
printf ("Address of realloc (x, 8): %p\n", x);
x = realloc (x, 8);
assert (x != NULL);
x = realloc (x, 0);
assert (x == NULL);
printf ("Address of realloc (x, 0): %p\n", x);
x = realloc (x, 21);
printf ("Address of realloc (x, 21): %p\n", x);
assert (x != NULL);
x = realloc (x, 0); // free.
assert (x == NULL);
printf ("Address of realloc (x, 0): %p\n", x);
return 0;
}
And doing a memory check:
$ gcc -Wall -g test_realloc.c -o test_realloc.o
$ valgrind ./test_realloc.o
==1192613== Memcheck, a memory error detector
==1192613== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==1192613== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
==1192613== Command: ./test_realloc.o
==1192613==
Address of realloc (NULL, 0): 0x4a3a040
Address of realloc (x, 8): 0x4a3a040
Address of realloc (x, 0): (nil)
Address of realloc (x, 21): 0x4a3a510
Address of realloc (x, 0): (nil)
==1192613==
==1192613== HEAP SUMMARY:
==1192613== in use at exit: 0 bytes in 0 blocks
==1192613== total heap usage: 4 allocs, 4 frees, 1,053 bytes allocated
==1192613==
==1192613== All heap blocks were freed -- no leaks are possible
==1192613==
==1192613== For lists of detected and suppressed errors, rerun with: -s
==1192613== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
So far, great. The only caveat is that the initial value must be initialized (even to NULL
).
Now I am wondering how portable this is across platforms. man 3 realloc
on Linux x86_64 is pretty explicit about this behavior except for realloc (NULL, 0)
which in this case, unexpectedly yields a non-NULL address.
How far can I rely on this behavior?
EDIT: Precisely, my question is whether I can rely on my sample program to be free of UB or memory leaks on any platform.
Thanks.
Upvotes: 1
Views: 189
Reputation: 223795
In asking about “this behavior,” your question is not specific about which behavior you are interested in. Presumably, you are interested in whether realloc
may or may not return a null pointer for realloc(x, 0)
and whether it will free the previously allocated space pointed to by x
.
The answer to both of those questions is either is allowed but the C implementation must document which it does.
C 2018 7.22.3 1 says “… If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned to indicate an error, 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.” Implementation-defined means the implementation must document what it does, so this should be stated in the compiler and/or standard library documentation.
Regarding using realloc
to free space, C 2018 7.22.3.5 3 says “… If size
is zero and memory for the new object is not allocated, it is implementation-defined whether the old object is deallocated…”
To write portable code, do not use realloc
to free all the space of an allocation. Use free
.
To allocate “zero” space, either tolerate any return value from malloc (null or not) or always allocate at least one byte even though you do not need it.
Upvotes: 2
Reputation: 1
TL;DR: No. Don't use realloc(somePointer,0)
.
It looks like realloc(somePointer,0)
changed between different C standards. From C89 [1]:
... If ptr is a null pointer, the realloc function behaves like the malloc function for the specified size. ... If the space cannot be allocated, the object pointed to by ptr is unchanged. If size is zero and ptr is not a null pointer, the object it points to is freed.
But this not the case anymore in the newer C standards.
From the POSIX man page [2]:
The description of realloc() has been modified from previous versions of this standard to align with the ISO/IEC 9899:1999 standard. Previous versions explicitly permitted a call to realloc(p, 0) to free the space pointed to by p and return a null pointer. While this behavior could be interpreted as permitted by this version of the standard, the C language committee have indicated that this interpretation is incorrect. Applications should assume that if realloc() returns a null pointer, the space pointed to by p has not been freed. Since this could lead to double-frees, implementations should also set errno if a null pointer actually indicates a failure, and applications should only free the space if errno was changed.
[1] http://port70.net/~nsz/c/c89/c89-draft.html#4.10.3.4
[2] https://pubs.opengroup.org/onlinepubs/9699919799/functions/realloc.html
Upvotes: 1
Reputation: 76433
realloc(ptr, 0)
is not equivalent to free(ptr)
To quote the standard (C18, §7.22.3.5 - IIRC, C11 hasn't changed):
The realloc function Synopsis 1 #include <stdlib.h> void *realloc(void *ptr, size_t size); Description 2 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. 3 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 a memory management function, or if the space has been deallocated by a call to the free or realloc function, the behavior is undefined. If size is nonzero and memory for the new object is not allocated, the old object is not deallocated. If size is zero and memory for the new object is not allocated, it is implementation-defined whether the old object is deallocated. If the old object is not deallocated, its value shall be 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 has not been allocated.
As you can see, it doesn't specify a special case for realloc calls where the size is 0. It explicitly states that what happens if you call realloc(ptr, 0)
, the behaviour is implementation defined.
That pretty much answers your question: using realloc
instead of free is not something you can rely on.
Furthermore: assert(x != NULL)
is test code. Whenever allocating memory, you really ought to check:
if (x == NULL) {}
The behaviour you see happens to be how GCC chose to implement the spec, but it cannot be relied upon.
Upvotes: 1
Reputation: 31409
This is what the C11 standard (to be more precise, it's a draft but it's the closest thing to the standard that's publicly available) says:
Synopsis
#include <stdlib.h> void *realloc(void *ptr, size_t size);
Description
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 a memory management 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.
https://port70.net/~nsz/c/c11/n1570.html#7.22.3.4
So realloc (NULL, 0)
is the same as malloc(0)
which may return a non-null pointer that may not be dereferenced.
Upvotes: 2