BB_bUrns
BB_bUrns

Reputation: 11

Why does realloc(NULL, size) call my malloc wrapper, but realloc(ptr, 0) does not call my free wrapper?

I have wrapped my dynamic memory functions (malloc, calloc, realloc, and free) to track memory usage in my program.

I have encountered the following:

My questions about this are:

  1. Why does realloc(NULL, size) invoke my malloc wrapper, while realloc(ptr, 0) does not explicitly invoke my free wrapper?
  2. How can I wrap/track what realloc is doing to free memory or alternatively how can I make realloc utilise free instead()?

I appreciate any help.

TLDR

I wrapped malloc, free, and realloc to track memory. I expected realloc(ptr, 0) to call my free wrapper, but it didn’t. Instead, the memory was freed silently. realloc(NULL, size) correctly went to my malloc wrapper. I’m trying to understand why this difference exists.

Upvotes: 0

Views: 106

Answers (2)

Eric Postpischil
Eric Postpischil

Reputation: 223776

  1. Why does realloc(NULL, size) invoke my malloc wrapper, while realloc(ptr, 0) does not explicitly invoke my free wrapper?

The compiler may transform realloc(NULL, size) to malloc(size) because the C standard guarantees these have the same behavior. C 2018 7.22.3.5 and C 2024 7.24.4.8 say:

… If ptr is a null pointer, the realloc function behaves like the malloc function for the specified size…

However, it is not guaranteed that realloc(validPtr, 0) and free(validPtr) have the same behavior. C 2018 7.22.3.5 says:

… If size is zero and memory for the new object is not allocated, it is implementation-defined whether the old object is deallocated…

With compilers such as GCC and Clang, the compiler and the implementation of the standard C library are separate things. The compiler may be used with different libraries, and so the compiler does not know what the behavior of the library is. If it changed realloc(validPtr, 0) to free(validPtr), it risks changing the behavior: The library might define realloc(validPtr, 0) to change the allocation to a block of memory with zero bytes (or one byte) rather than to free the memory. (A standard library implementation of realloc might also never reduce an allocation, so realloc(validPtr, 0) might do nothing.)

In C 2024, 7.24.4.8 says:

… if the size is zero, the behavior is undefined.

In this case, the transformation of realloc(validPtr, 0) to free(validPtr) is allowed by the C standard but still risks changing the behavior of programs (from the behavior that is defined by the library implementation even though it is not defined by the C standard to a different behavior), which the compiler authors may decide not to do. Additionally, compilers may not have been fully updated for C 2024.

(Under both the C 2018 and C 2024, realloc(pointer, 0) is hazardous code—unless you target only specific C implementations that define the behavior uniformly, you cannot know whether it does or does not free the memory. To write strictly conforming C code, you must not pass a size of zero to realloc. If your intention is to free the memory, you should use free(pointer) instead. If your intention is to reduce the memory allocation but retain it, you should use realloc(pointer, 1) instead.)

  1. How can I wrap/track what realloc is doing to free memory or alternatively how can I make realloc utilise free instead()?

You have already wrapped or interposed malloc and free. Simply interpose realloc as well.

Upvotes: 4

Sourav Ghosh
Sourav Ghosh

Reputation: 134376

Why does realloc(NULL, size) invoke my malloc wrapper, while realloc(ptr, 0) does not explicitly invoke my free wrapper?

Because the standards says so.

Chapter 7.22.3.5, paragraph 3:

If ptr is a null pointer, the realloc function behaves like the malloc function for the specified size.

which explains why there is call to malloc() when the first argument is NULL.

and paragraph 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.

So, it does not mandate that while passing a valid pointer, realloc must free the memory allocated to the previous address (pointer) and allocate a new block of memory at a different address, it can simply re-size (i.e., update the associated metadata which hold the allocated memory size) the memory allocation as per the latest call to realloc - so an underlying free() call is not guaranteed. It can just mark the address pointed to by the pointer as non-usable and return the same pointer, as that pointer cannot be used for any valid memory operation anymore.

In other words, copying from chapter 7.22.3,

[...] 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.

How can I wrap/track what realloc is doing to free memory or alternatively how can I make realloc utilise free instead()?

You need to write a wrapper for realloc() as well, if you want the behaviour to be deterministic. Check the source code of the libc for your platform for some more idea.

Upvotes: 3

Related Questions