[Solved] Error when coding my own implementation of realloc()


Invalid assumption:

void *my_realloc(void *ptr, size_t size)
{
    unsigned char *old_ptr = (unsigned char *)ptr;

You’re breaking the realloc() specification here. realloc() doesn’t assume that the ptr would always be pointing to a string. It is type-agnostic.

And the cast is redundant. There’s an implicit conversion from void * to any other pointer type.


Undefined behaviour:

old_size = my_strlen_unsigned(old_ptr) + 1;

The standard, or your my_strlen_unsigned version of it, works with strings, i.e. the pointer is assumed to be pointing to an array of char terminated by a null-byte. An array of ints will not be terminated with a null-byte. Your my_realloc() thus invokes undefined behaviour.


Possible fix:

You can’t determine the old size of the block pointed to by ptr and reimplement realloc(), without at least reimplementing your own malloc() and free(), portably. But you can take the old size as the third argument. (But the standard realloc() only takes two. So would this be a conforming implementation?)

Here’s glibc’s implementation of it: malloc().c

Here’s musl C implementation of it: malloc().c

And here’s my toy example that attempts to emulate the standard realloc() to some extent:

/** 
*    @brief The my_realloc() function shall deallocate the old object pointed to
*           by ptr and return a pointer to a new object that has the size specified by new_size.
*
*    @param ptr - A pointer to a block of memory to resize.
*    @param old_size - The size of the block pointed to by ptr.
*    @param new_size - The size to resize by.
*
*    If ptr is a null pointer, my_realloc() shall be equivalent to
*    malloc() for the specified new_size.
*
*    If ptr does not match a pointer returned earlier by calloc(),
*    malloc(), or realloc() or if the space has previously been
*    deallocated by a call to free() or realloc(), the behavior is undefined.
*    
*    @return Upon successful completion, my_realloc() shall return a pointer to the moved allocated space.  
*            If size and ptr both evaluate to 0, my_realloc() shall return a 
*            NULL pointer with errno set to [EINVAL].
*            If there is not enough available memory, my_realloc() shall return a
*            NULL pointer and set errno to [ENOMEM].
*            
*            If my_realloc() returns a NULL pointer, the memory referenced by ptr shall not be changed.
*
*    @warning my_realloc() may return NULL to indicate an error. For that reason, a different pointer variable 
*             must be used to hold it's return value. Otherwise, you risk overwriting the original ptr with NULL and 
*             losing your only reference to the original block of memory.
*/ 
              
void *my_realloc (void *ptr, size_t new_size, size_t old size) 
{
    if (!ptr) {
        return malloc (new_size);
    }
    
    if (!new_size) {
        errno = EINVAL;
        return 0;
    }
    
    if (new_size <= old_size) {
        return ptr;
    }

   /* As a last resort, allocate a new chunk and copy to it. 
    */
    void *new = 0;
    if (new_size > old_size) {
        new = malloc (new_size);
        if (!new) {
            return 0;
        }
        memcpy (new, ptr, old_size);
        free (ptr);
    }
    return new;
}

You can also find a sample implementation in chapter 8 of K&R.


Side-notes:

char *new_str = malloc(1);
new_str = my_realloc(..);

You risk losing access to the original memory allocated through malloc() here. If my_realloc() returned NULL, new_str would be assigned it’s result and you would cause a memory leak in your program.

Furthermore, the memory returned by malloc() is uninitialized. And your code invokes undefined behaviour by calling my_strlen() underneath my_realloc() on a pointer that is uninitialized. Hence the warnings.


solved Error when coding my own implementation of realloc()