Realloc allocates 16 Bytes of memory when pointer is NULL and size is 0

I was tracking memory leaks with Instruments and noticed that playdate->system->realloc allocates 16 Bytes of memory when ptr is NULL and size is 0.

Is it the expected behaviour ? I expected realloc to work like free when size is 0 and to do nothing when the given pointer is NULL.

1 Like

We can look into changing this behavior. However, generally it shouldn't be relied upon and is implementation specific and varies across platforms.

Passing NULL to realloc() is the same as calling malloc(), so this makes sense. malloc(0) would probably also give you a block of some minimum size on most platforms instead of returning NULL.

Yes, malloc(0) often returns a minimum size pointer instead of returning NULL. That is why I don't think of this behavior as a bug.

You may better understand my (initial) surprise if you see my original code:

void MELHashMapBucketListDeinit(MELHashMapBucketList * _Nonnull self) {
    free(self->memory);
    self->memory = NULL;
    self->count = 0;
    self->capacity = 0;
}

Using free, I overlooked the fact that it would instead be calling malloc when self->memory is NULL.

It was easy to fix the leak once I understood what was going on:

if (self->memory != NULL) {
    playdate->system->realloc(self->memory, 0);
    self->memory = NULL;
}

No need to go that far :+1: , I think a simple mention of this behavior in the documentation should be enough.

(And talking about the documentation, playdate->sprite->free is actually called playdate->sprite->freeSprite).

2 Likes

oh! You're right, our free() shim is passing the pointer directly to realloc() without checking for NULL. I'll fix that! (Also TIL that free(NULL) does nothing--I was sure that would be a crash.)

Thanks for catching this!

1 Like

Oh right, that file's in the SDK, at C_API/buildsupport/setup.c. Here's an updated version with the NULL check:
setup.c.zip (928 Bytes)

1 Like

Speaking as someone who was just caught by this...

Please document this behaviour, because right now it says:

void* playdate->system->realloc(void* ptr, size_t size)

Allocates heap space if ptr is NULL, else reallocates the given pointer. If size is zero, frees the given pointer.

I've just wasted countless hours trying to work out where my leak was coming from. I don't need that kind of extra stress in my life. I'm writing stuff for the Playdate for fun, not as a penance.

EDIT: or better yet, add a system->free().