create my own memset function in c

21,510

Solution 1

how to copy the int c into a character on my void b pointer

You convert the void pointer to an unsigned char pointer:

void  *my_memset(void *b, int c, int len)
{
  int           i;
  unsigned char *p = b;
  i = 0;
  while(len > 0)
    {
      *p = c;
      p++;
      len--;
    }
  return(b);
}

what condition to use on my while to be sure it stop before a '\0' char

memset have to trust the length that is passed in. memset needs to work on a general piece of memory, not just a 0 terminated string - so there should not be such a check.

If you anyway would need to check for a 0 byte. you'd do

if (*p == 0) //or if(!*p )
     break;

Solution 2

Pointer arithmetic is based on offsetting the pointer by the size of the type it points to. Before you start incrementing that pointer, you should transform it from void* to pointer to char / unsigned char:

void* my_memset(void *s, int c, size_t len) {
    unsigned char *dst = s;
    while (len > 0) {
        *dst = (unsigned char) c;
        dst++;
        len--;
    }
    return s;
}

also note that memset returns a pointer to the memory area s, so you should return the original pointer (before you start incrementing it).

Solution 3

The reason functions often return a value is to return an error state to the calling function. In memory related functions it's usually the same pointer as where your result should be (including NULL). In your example you might not want to use the return value of your my_memset function, but usually it's because it can be included in a code evaluation (can't think of a better word for this), e.g.

if(!my_memset((void *)str, 'a', 5))
{
    printf("An error occurred in my_memset()\n");
}

or in a macro, e.g. to return pointer to the end of the memory where you copied your char:

#define INIT_MEM_PTR_END(a,x) (my_memset((void *)&(a), (x), sizeof(a)) + sizeof(a))

This is probably not a great example (plus the potential issues if a is already a pointer, etc...), but it shows that you can reuse the result without having to write another couple of lines for this to evaluate the result and so on.

You should also check your pointers before dereferencing them. If for example void *b is NULL, you'll have a segmentation fault.

Nothing wrong with passing in void *, other than the fact that the intention of the function may not be as clear as when passing pointer to a particular data type. Make sure you cast it to something valid though inside. Also this way, the function can be used to set any memory to a particular hex value (through char) or all 0's quite easily.

It would seem like in this case b should be cast to the same type as the value you're trying to copy into it, an int; however, then the len argument becomes unclear, is it size in bytes or number of times c should be copied to the b pointer?

Since in your main() you're copying a char into that memory location, then it's just better to change your c to a char, cast your b to a char* and make len the length in bytes or number of times c should be copied to *b. Avoid ambiguity.

The way you have written it, it will copy c number of times specified by len or until you meet the null character, whichever is shortest/soonest. That's fine, if that's your intention.

void *my_memset(void *b, char c, int len)
{
    char *b_char = (char *)b;

    if (b == NULL) return NULL;

    while(*b_char && len > 0)
    {
        *b_char = c;
        b_char++;
        len--;
    }

    return b; //as this pointer has not changed
}

int main()
{
    char *str;

    str = strdup("hello");
    if (!my_memset((void *)str, 'a', 5))
    {
        printf("An error occurred in my_memset()\n");
    }
    else
    {
        printf("%s\n", str);
    }
}

Solution 4

void   *my_memset(void *b, int c, int len)
{
  if (b == NULL || len <= 0)
      return b;
  unsigned char *ptr = b;
   while(*ptr != '\0' && len--)
    {
      *ptr++ = (unsigned char)c;
    }
  return(b);
}
Share:
21,510
Saxtheowl
Author by

Saxtheowl

computer enthusiast :) [email protected]

Updated on February 07, 2020

Comments

  • Saxtheowl
    Saxtheowl over 4 years

    here is the prototype:

    void *memset(void *s, int c, size_t n)
    

    first im not sure if I have to return something because when I use the memset i do for example

    memset(str, 'a', 5);
    

    instead of

    str = memset(str, 'a', 5);
    

    here is where I am with my code:

    void *my_memset(void *b, int c, int len)
    {
        int i;
    
        i = 0;
        while(b && len > 0)
        {
            b = c;
            b++;
            len--;
        }
        return(b);
    }
    
    int main()
    {
        char *str;
    
        str = strdup("hello");
        my_memset(str, 'a', 5);
        printf("%s\n", str);
    }
    

    I dont want to use array in this function, to better understand pointer and memory, so I dont get 2 things: - how to copy the int c into a character on my void b pointer - what condition to use on my while to be sure it stop before a '\0' char

    edit: i was wondering is there a way to do this function without casting ?

  • Martin R
    Martin R over 10 years
    Something is going wrong here: The first version had p = c, which should be *p = c, then @mah edited the answer and removed the assignment completely.
  • Vivek S
    Vivek S over 10 years
    Where are you setting the contents to c ?
  • mah
    mah over 10 years
    @MartinR you're correct; I realized that p = c was wrong but didn't pay enough attention to find the intention... I thought it was a stray line of code. Thanks for the heads-up!
  • LihO
    LihO over 10 years
    Argument should not have a type of char* and you should not return b after being incremented.
  • Lundin
    Lundin over 10 years
    @VivekS From the standard: The memset function copies the value of c (converted to an unsigned char) into each of the first n characters of the object pointed to by s. This answer is 100% compatible with the standard's definition of memset.
  • LihO
    LihO over 10 years
    @VivekS: The function expects the "byte" and it's perfectly fine if the implementation relies on it since 99% programmers that will use it will pass a valid value instead of trying to find out what will happen if they go against its semantics.
  • Vivek S
    Vivek S over 10 years
    @Lundin Then why take an int ? You may as well take unsigned char. Also, by the definition above, I am only supposed to pass a char or unsigned char array, because, if I pass a pointer to an integer array, you are going to set n characters to the value c which might not be what the user expected unless he passed 0 for c.
  • akluth
    akluth over 10 years
    You shouldn't just post code as your answer. Add some explaination to it.
  • TwoCode
    TwoCode over 10 years
    @LihO I've got the code changed. Did not take that seriously. Just noticed the *b=c for the first glance. Thanks for pointing out.
  • TwoCode
    TwoCode over 10 years
    @akluth The others have made quite clear specifications. Nothing special here.
  • Lundin
    Lundin over 10 years
    @VivekS The reason all character handling functions in the C language work on an int rather than char, is because a character could contain the value EOF, which is of type int and has a negative value. However, if you would pass an int containing EOF to memset, you'd likely get garbage as result. In the end, it all boils down to the C standard being generally irrational and insane, then carrying around a lot of weird backwards compatibility crap on top of the insanity.
  • Lundin
    Lundin over 10 years
    @VivekS Indeed you can't pass an integer array, it would cause the program to crash and burn.
  • TwoCode
    TwoCode over 10 years
    BTW, this code is almost the most robust and time efficient(do not need to execute the following codes if the b is null or len is negative, although that would also work without errors).(Assembly excluded)
  • Lundin
    Lundin over 10 years
    Why do you check against \0 in the target destination??? Why do you check against \0 in the first place? This code makes no sense at all.
  • TwoCode
    TwoCode over 10 years
    @Lundin Oh, the post said "what condition to use on my while to be sure it stop before a '\0' char"
  • chux - Reinstate Monica
    chux - Reinstate Monica over 10 years
    To match C's memset(s,c,n), the n parameter is size_t n and not type int.
  • Vivek S
    Vivek S over 10 years
    @Lundin, integer array works when we pass 0 which mostly is the case, but isn't there a generic memset which can sort of set char arrays, int arrays etc etc to the value we specify ? Would be good to hear about it.
  • chux - Reinstate Monica
    chux - Reinstate Monica over 10 years
    To match C's memset(s,c,n), the n parameter is size_t and not int.
  • Kbam7
    Kbam7 about 8 years
    May I ask why you are defining an unsigned char?
  • AlphaGoku
    AlphaGoku over 4 years
    What happens if *b_char is 0?