If `malloc(0)` returns a non-null pointer, can I pass that to `free`?

43

2

I've been reading over discussions of how malloc behaves when you request a zero sized block.

I understand that the behavior of malloc(0) is implementation-defined, and it is supposed to either return a null pointer, or a non-null pointer that I am nonetheless not supposed to access. (Which makes sense, since there's no guarantee that it points to any usable memory.)

However, if a get such a nonaccessable non-null pointer, am I allowed to pass it to free in the usual way? Or is that illegal, since the pointer I get from malloc(0) may not point to an actual allocated block of memory?

Concretely, does the following code have well-defined behavior:

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

int main() {
    int* x = (int*) malloc(0);
    if (x == NULL) {
        printf("Got NULL\n");
        return 0;
    } else {
        printf("Got nonnull %p\n", x);
    }
    free(x); // is calling `free` here okay?
}

pnkfelix

Posted 2017-06-02T14:24:43.647

Reputation: 2 753

Answers

42

The C99 standard (actually WG14/N1124. Committee Draft -- May 6, 2005. ISO/IEC 9899:TC2) says about malloc():

The pointer returned points to the start (lowest byte address) of the allocated space. If the space cannot be allocated, a null pointer is returned. 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 about free():

Otherwise, if the argument 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 free or realloc, the behavior is undefined.

IEEE Std 1003.1-2008 (POSIX), 2016 Edition says about free():

The free() function shall cause the space pointed to by ptr to be deallocated; that is, made available for further allocation. If ptr is a null pointer, no action shall occur. Otherwise, if the argument does not match a pointer earlier returned by a function in POSIX.1-2008 that allocates memory as if by malloc(), or if the space has been deallocated by a call to free() or realloc(), the behavior is undefined.

So, whatever *alloc() returns, you can pass to free().

As for the current implementations of malloc():

FreeBSD uses the contributed jemalloc which does

void *
je_malloc(size_t size)
{
    void *ret;
    size_t usize JEMALLOC_CC_SILENCE_INIT(0);

    if (size == 0)
        size = 1;
    [...]

Whereas Apple's libmalloc does

void *
szone_memalign(szone_t *szone, size_t alignment, size_t size)
{
    if (size == 0) {
        size = 1; // Ensures we'll return an aligned free()-able pointer
    [...]

The GLIBC also changes the requested size; it is using a call to this macro with the requested size in bytes as the parameter to align the size to a certain boundary or simply the minimum allocation size:

#define request2size(req)                                       \
    (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE)  ?         \
    MINSIZE :                                                   \
    ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)

apriori

Posted 2017-06-02T14:24:43.647

Reputation: 941

25

Yes, in fact you must do so to avoid a probable memory leak.

The malloc system typically returns a hidden control block in the space immediately before the pointer, with information such as allocation size. If allocation size is zero, this block still exists and occupies memory, if malloc returns non-null.

Malcolm McLean

Posted 2017-06-02T14:24:43.647

Reputation: 5 427