2

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.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
Dzious
  • 169
  • 2
  • 14
  • why don't you just memset the area pointed to by `ptr`? – bruceg Jan 11 '19 at 01:35
  • When you examine the storage representation of data, cast the void pointer to pointer to char or unsigned char. Instead of using `ptr` directly, use e.g. `char *src = ptr;` (and perhaps `char *dst;` instead of `void *new;`, for "source" and "destination", respectively). – Nominal Animal Jan 11 '19 at 01:45
  • 1
    `ptr++;`? You can't do that with a `void *`, as there's no type and therefore no "next" address to increment to. – Andrew Henle Jan 11 '19 at 01:50
  • In `my_realloc()`, what are you trying to do with `new_len = my_strlen(ptr) + size;`? You can't find the size allocated for the old block with a `strlen()` type function, and `realloc()` doesn't add the new size to the old size, it just resizes the block to new size (or allocates a new one if necessary). – Dmitri Jan 11 '19 at 01:53
  • Think about this... if you allocate 1000 bytes with `calloc()`, then call `strlen()` on the pointer, what size will it report, considering all the memory is zeroed? It's a 1000-byte block, but would `strlen()` report that? – Dmitri Jan 11 '19 at 01:58
  • @bruceg I can't memset yet due to my school... – Dzious Jan 11 '19 at 02:16
  • @AndrewHenle As i said i'm kind of new with C... and i thought it could work but no it doesn't – Dzious Jan 11 '19 at 02:17
  • @Dmitri In fact i just want to save the beginning to the first '\0' i found in src in a larger block. the size of the new block should be the size of len of the "string" i found in src + the size i give as a parameter – Dzious Jan 11 '19 at 02:23
  • @NominalAnimal thanks for the tips, i'm going to change the names right now, but i can't use a char * it wouldn't work for other types of variables like char ** – Dzious Jan 11 '19 at 02:26
  • 1
    @Dzious: No, I meant that you use `src` and `dst`, of type `char *`, internally in your functions. `size` is always in chars, because `sizeof (char) == 1` in C. Your functions still take and return `void *`. – Nominal Animal Jan 11 '19 at 02:28
  • @NominalAnimal ok, i see what you mean, i've tested it and it also works, but i still have the same problem that i have with the solution given by dbush, when i fill foo with a for loop in which i put foo[i] = 'i', when i try to prin tit thanks to printf, nothing prints (i know that printf use buffer until it find a '\n' and i put '\n') – Dzious Jan 11 '19 at 02:56
  • Why the unnecessary `+ 1` in `ptr = malloc(n * (size + 1));`? – chux - Reinstate Monica Jan 11 '19 at 03:00
  • @chux the +1 is to add a '\0' at the end of my string, it allows me to know where my string ends whitout having to bring my string_len in all my functions – Dzious Jan 11 '19 at 03:05
  • @Dzious So you want your `my_calloc()` to allocate more memory than `calloc()`? – chux - Reinstate Monica Jan 11 '19 at 03:08
  • @chux in fact yes, i never used calloc before and i always had to add a +1 with malloc so i put it here because i know i'll have to add it anyway – Dzious Jan 11 '19 at 03:21
  • When good code does then add 1 to its size request before calling `calloc()`, when that code is replaced by `my_calloc()`, it will just be less efficient. Hmmm, I think your goal would be OK if your did not call it `my_calloc()`, but maybe `my_string_calloc()` to clarify that your `***_calloc()` differs in functionality from the standard `calloc()`. – chux - Reinstate Monica Jan 11 '19 at 03:30
  • @chux ok, i did this mistake certainly because i never used calloc() yet. thank for your help – Dzious Jan 11 '19 at 03:36
  • @Dzious Do not change the details/code of your post once answers arrive. It makes for a moving target (lost of clarity) and invalidate answers. Post rolled back. In general, its OK to _append_ new information, but not to apply code from the answers. – chux - Reinstate Monica Jan 11 '19 at 03:37
  • ok, sorry, i'll undo everyhing – Dzious Jan 11 '19 at 03:51

2 Answers2

3

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.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • first thanks for your help. but can you explain more precisely the test for overflow protection in my_calloc ? An other thing i don't understand is the "Failure to handle a shrinking allocation" can you also please explain it ? Finally, i don't know yet how to get the type of my pointer, i'm still looking for it, that why my_realloc has not exactly the same proto as realloc() – Dzious Jan 11 '19 at 03:44
  • @Dzious Example assuming 32-bit `size_t`: `my_realloc(ptr, 65536, 65536)` would allocate ` 100000 * 100000` --> after overflow --> `1410065408` - less than expected. With an overflow check, code would return `NULL` on that extreme attempt allowing the calling code to identify the failure. – chux - Reinstate Monica Jan 11 '19 at 03:54
  • @Dzious With `for (int i = 0; src[i] != NULL;i++) dst[i] = src[i];`, `src[i]` is well defined for all `i`. What if `n==1`, then `dst[i]` is only good for a few iterations. Hence the _shrink_ concern. – chux - Reinstate Monica Jan 11 '19 at 04:01
  • ok, thanks for your example, i understand a bit better right now – Dzious Jan 11 '19 at 04:01
  • @Dzious "don't know yet how to get the type of my pointer" that is the tricky bit. 1st, `my_realloc()` does not need to know the type, but the size of the prior allocation. 2nd, `calloc()` knows the prior allocation size, yet that information is hidden. To mimic `calloc()`, all your allocations need to hide the size someplace - the usual place is before the pointer given to the user. This will sound a bit complex - it is, but it is the sneaky reason your class is pushing for you to re-implement code. Good luck. P.S. If you go this route, you will need your own `my_free()` too. – chux - Reinstate Monica Jan 11 '19 at 04:05
  • alright thanks a lot, i'll go forwart with what i do actually have. i'll certainly have to do it in few weeks. thanks again for your help – Dzious Jan 11 '19 at 04:12
  • sorry two lasts questions, if i let the (size + 1) in my project (my_calloc) do i need to put the first or second overflow protection ? Second one how can i find the SIZE_MAX ? – Dzious Jan 11 '19 at 04:36
  • @Dzious The check is to test `a*b > c` - without causing an overflow. Use `a > c/b` or `b > c/a` - your choice (make sure the divisor is not 0). `SIZE_MAX` is in ``. – chux - Reinstate Monica Jan 11 '19 at 05:47
0

void is an incomplete type, so you can't dereference a void *. What you can do however is cast it to a char * or unsigned char * to access individual bytes.

So my_calloc can do this:

((char *)ptr)[i] = 0;

And my_realloc can do this:

((char *)dst)[i] = ((char *)src)[i]; 
Dzious
  • 169
  • 2
  • 14
dbush
  • 205,898
  • 23
  • 218
  • 273
  • First I want to thank you, it does resolved my errors. Now i've got a new problem. In my main, when i try to fill foo with 'i', for (int i = 0; i != 3; i++) { foo[i] = 'i' } and then i print i with a printf function foo isn't filled. would you be able to help please ? (foo has enough blocks to be filled) (sorry i don't know how to add "true" code here) – Dzious Jan 11 '19 at 02:39