create my own memset function in c
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);
}
Comments
-
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 over 10 yearsSomething 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 over 10 yearsWhere are you setting the contents to c ?
-
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 over 10 yearsArgument should not have a type of
char*
and you should not returnb
after being incremented. -
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 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 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 over 10 yearsYou shouldn't just post code as your answer. Add some explaination to it.
-
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 over 10 years@akluth The others have made quite clear specifications. Nothing special here.
-
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 over 10 years@VivekS Indeed you can't pass an integer array, it would cause the program to crash and burn.
-
TwoCode over 10 yearsBTW, 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 over 10 yearsWhy 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 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 over 10 yearsTo match C's
memset(s,c,n)
, then
parameter issize_t n
and not typeint
. -
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 over 10 yearsTo match C's
memset(s,c,n)
, then
parameter issize_t
and notint
. -
Kbam7 about 8 yearsMay I ask why you are defining an unsigned char?
-
AlphaGoku over 4 yearsWhat happens if *b_char is 0?