Search code examples
cmallocreallocvoid-pointerscalloc

Reprogramming Calloc / Realloc in C Using void pointers


I'm actually learning C programming and my school actually doesn't allow us to use calloc / realloc without reprogramming them. That's why I'm asking for help.

Here is my problem : I want to use void * to make my code reusable but I encounter the problem "dereferencing void * pointer" when I try to run through my array. I'm unable to pick up the type of the final pointer.

Here is my functions :

#include <stdlib.h>

void *my_calloc(size_t size, size_t n)              //n = number of bytes your type : sizeof(<whatever>)
{
    void *ptr = NULL;

    if (size < 1 || n < 1)
        return (NULL);
    ptr = malloc(n * (size + 1));
    if (ptr == NULL)
        return (NULL);
    for (int i = 0; i != (n * (size + 1)); i++) {
        *ptr = NULL;                                //Here is my problem
        ptr++;
    }
    return (ptr);
}

void *my_realloc(void *src, size_t size, size_t n)
{
    void *dst = NULL;
    int dst_len = 0;

    if (src == NULL || size < 0 || n < 1)
        return (NULL);
    dst_len = my_strlen(src) + size;
    if (dst_len == my_strlen(src))
        return (src);
    dst = my_calloc(dst_len, n);
    if (dst == NULL)
        return (NULL);
    for (int i = 0; src[i] != NULL;i++)
        dst[i] = src[i];                        //Here is the same problem...
    free(src);
    return (dst);
}

I just find a problem while I was writing my post, my my_strlen function can only take a char *... so I would need a function my_strlen looking like :

int my_strlen(void *str)
{
    int len = 0;

    while (str[len] != NULL) {                //same problem again...
        len++;
    }
    return (len);
}

A typical function where i call calloc / malloc would be :

int main(void)
{
    char *foo = NULL;
    int size = 0;
    int size_to_add = 0;

    size = <any size>;
    //free(foo);                                //only if foo has been malloc before
    foo = my_calloc(size, typeof(*foo));

    //something

    size_to_add = <any size>;
    foo = my_realloc(foo, size_to_add, sizeof(*foo))

    //something

    free(foo);
    return (0);
}

Thank you for trying to help me.


Solution

  • my_calloc() has various troubles:

    Attemptted pointer math on a void *

    This is undefined behavior (UB).
    Instead make ptr a character pointer.

    // void *ptr = NULL;
    unsigned char *ptr = NULL;
    ...
    ptr++;
    

    Attempt to de-reference a void *

    This is also UB.
    Instead make ptr a character pointer.

    // void *ptr = NULL;
    unsigned char *ptr = NULL;
    ...
    // *ptr = NULL;
    *ptr = '\0';
    

    my_calloc() allocates more memory than calloc()

    To do the same as calloc(), do not add one.

    // ptr = malloc(n * (size + 1));
    ptr = malloc(n * size);
    

    No overflow protection

    my_calloc() does not detect overflow with n * (size + 1). A test is

    // Note: it is known n > 0 at this point
    if (SIZE_MAX/n > size+1) return NULL;
    // or if OP drop the + 1  idea, 
    if (SIZE_MAX/n > size) return NULL;
    

    my_realloc() has various troubles:

    Different signature

    I'd expect the goal of "school actually doesn't allow us to use calloc / realloc without reprogramming them" was meant to create a realloc() substitute of which my_realloc() is not. If a different function is desired, consider a new name

    void *my_realloc(void *src, size_t size, size_t n)
    // does not match
    void *realloc(void *ptr, size_t size);
    

    Failure to handle a shrinking allocation

    The copying of data does not take into account that the new allocation may be smaller than the prior one. This leads to UB.

    Unneeded code

    size < 0 is always false

    Memory leak

    The below code does not free src before returning. Further, it does not allocate anything when n>0. This differs from calloc(pit, 0) and calloc(NULL, 42).

    // missing free, no allocation 
    if (src == NULL || size < 0 || n < 1) {
        return (NULL);
    }
    

    Assumed string

    my_strlen(src) assume src points to a valid string. calloc() does not assume that.