Understanding code of function strlen in C Programming - c

I was given a solution to the problem of recreating the function strlen in C Programming and it was the following, I need help understanding what the code is actually doing.
void *ft_memset(void *b, int c, size_t len)
{
size_t i;
i = 0;
while (i < len)
{
((unsigned char *)b)[i] = c;
i++;
}
return (b);
}
My biggest doubts are the meaning of ((unsigned char *)b)[i] = c, specially because I don't understand the ( *)b, and the need for a return b in a void function.
Thank you very much for your help, sorry if I did something wrong its my first post in stack overflow.

Let's break it down, albeit my C skills are somewhat rusty. Edits are welcome :)
void *ft_memset(void *b, int c, size_t len)
{
size_t i;
i = 0;
while (i < len)
{
((unsigned char *)b)[i] = c;
i++;
}
return (b);
}
First, you have declared a function called ft_memset which returns void *.
void * means a pointer to any object. It's just a number, pointing to a memory address. But we do not know what's in it because it's not properly annotated.
Your function takes three arguments:
b which is a pointer to anything (or void pointer if you will)
c which is an int 32-bit signed integer number.
len which is a size_t which is usually an alias to an unsigned integer. Read more about size_t over here
Your function iterates through the first len bytes and sets the c's value to those.
We're basically overwriting b's contents for the first len bytes.
Now, what does ((unsigned char*)b) mean. This statement is casting your void * to an unsigned char* (byte pointer), this is just
telling the compiler that we're gonna deal with the contents pointed by b as if they were just a unsigned char.
Later we're just indexing on the i-th position and setting the value c in it.

Understanding code of function strlen
void *ft_memset(void *b, int c, size_t len) is like void *memset(void *s, int c, size_t n). They both assign values to the memory pointed to by b. These functions are quite different form strlen().
size_t strlen(const char *s) does not alter the memory pointed to by s. It iterates through that memory looking for a null character.
size_t strlen(const char *s) {
const char *end = s;
while (*end != '\0') {
end++;
}
return (size_t)(end - s);
}
// or
size_t strlen(const char *s) {
size_t i = 0;
while (s[i] != '\0') {
i++;
}
return i;
}

Related

C arrays memcmp trouble

I need to write a code for my UNI, in which I have to receive 2 arrays of some data (it may be int array or char array or even float array) and compare n characters from pointers in both of the arrays, so far I am stuck with this
*note in other words - I need to write my own memcmp :
int byte_compare(const void *b1,const void *b2,size_t len)
{
char *q1 = (char *)p1;
char *q2 = (char *)p2;
int i;
for (i=0; i<=len*sizeof(*q1) ;i++)
{
if(*q1++ != *q2++)
return 1;
}
return 0;
}
int main()
{
char str1[] = "abcheya";
char str2[] = "gtyheyb";
printf((byte_compare(str1[3],str2[3],3))?"not equal\n":"equal\n");
/*the output is equal*/
int arr1[]={1,2,3,4};
int arr2[]={1,2,7,59};
printf((byte_compare(arr1[0],arr2[0],3))?"not equal\n":"equal\n");
/*the output is also equal although it is not supposed to be*/
return 0;
}
when I compare strings it works just fine, but when I try to compare int or float it wont work properly, please help me.
You should calculate the comparing byte length while passing the len parameter. So, you can use templates for this.
template<typename T>
int byte_compare(const T *b1, const T *b2, size_t len)
{
return memcmp(b1, b2, len * sizeof(T));
}
Edit note : The question is edited and the answer has no more meaning. And it uses C++, not applicable for C lessons. I'm remaining the answer for whose have no restriction to use C++.
Edit: to write own memcmp function
int byte_compare(const void *b1,const void *b2,size_t len)
{
if (len != 0) {
register const unsigned char *p1 = b1, *p2 = b2;
do {
if (*p1++ != *p2++)
return (*--p1 - *--p2);
} while (--len != 0);
}
return (0);
}
You need to pass the size of the objects, not their length:
int byte_compare(const void *b1, const void *b2, size_t size)
{
return memcmp(b1, b2, size);
}
Your instructor probably does not want you to use a library call. So write a byte by byte comparison (aka memcmp).
Get a copy of K&R C (or another book on C), and you will find examples of how to process char(acter) values in a loop.
int byte_compare(const void *b1,const void *b2,size_t len)
{
if( !b1 || !b2 ) return 0; //0 represents false
char* p1=(char*)b1; char* p2=(char*)p2;
for( ; len --> 0; ) {
if( *p1++ != *p2++ ) return 0;
}
return 1;
}

Compiler showing "Expression must have a constant value" in VS2015 (C Programming)

So i wanted to create a swap function that will allocation size of character array dynamically. Is it possible? Is it recommended?
My code that shows errors.
void swap(void * vp1, void * vp2, int size)
{
char buffer[size]; //size must have a constant value is the error i am getting.
memcpy(buffer, vp1, size);
memcpy(vp1, vp2, size);
memcpy(vp2, buffer, size);
}
char buffer[size];
An array whose size is not known at compile time is known as a variable length array. These are C-only features. Since this is giving an error, you are using a C++ compiler. You must change size so that it will be known at compile time.
The following are valid C++ array definitions
#define NUM 8
int a[16];
int a[NUM];
This is ill-formed:
int n;
printf("What is your age?\n");
scanf("%d", &n);
int a[n];
because n depends on what the user types.
The solution is to cast the void pointer arguments into pointers to unsigned char, and then manipulate the unsigned char *
void swap(void *a, void *b, size_t n)
{
unsigned char *p = (unsigned char *) a,
*q = (unsigned char *) b,
tmp;
for (size_t i = 0; i < n; ++i) {
tmp = p[i];
p[i] = q[i];
q[i] = tmp;
}
}
But since you're using C++, it is a better idea to use templates.
Unfortunately MSVC is the only major C compiler that doesn't support your code. To support all compilers you can write:
void swap(void * vp1, void * vp2, int size)
{
char *buffer = malloc(size);
if ( buffer )
{
memcpy(buffer, vp1, size);
memcpy(vp1, vp2, size);
memcpy(vp2, buffer, size);
free(buffer);
}
}
Note: This way is simple in code but there may be more efficient options, especially if you are typically only swapping small buffers.

Use pointer math instead of array indexing

I'm trying to solve a problem found on my C programming book.
#include <stdio.h>
char *f (char s[], char t[], char out[]);
int main(void)
{
char s[] = "ISBN-978-8884981431";
char t[] = "ISBN-978-8863720181";
char out[10];
printf ("%s\n", f(s,t,out));
return 0;
}
char *f (char s[], char t[], char out[]) {
int i;
for (i=0; s[i] == t[i]; i++)
out[i] = s[i];
out[i] = '\0';
return &out[0];
}
As you can see from the code, this code uses as return value &out[0]: does this means that the complete array is used as return value?
char *f (char s[], char t[], char out[]);
int main(void)
{
char s[] = "ISBN-978-8884981431";
char t[] = "ISBN-978-8863720181";
char out[10];
printf ("%s\n", f(s,t,out));
return 0;
}
char *f (char s[], char t[], char out[]) {
for (; *(s+=1) == *(t+=1);)
*(out+=1) = *s;
*(out+1) = '\0';
return out;
}
This is my proposed solution, but while the proposed code returns "ISBN-978-88", mine only returns "8".
The array is smaller than the lenght of the string, how the proposed code can work without any kind of overflow?
Thanks for your responses.
Your code is too aggressive on side effects: the += 1 operation (which is more commonly denoted simply as ++) should be applied after the copy to the output has been made, not after the comparison.
In addition, you need to save the value of the out buffer before incrementing the pointer, so that you could return a pointer to the beginning of the copied string.
char *orig = out;
for ( ; *s == *t ; s++, t++)
*out++ = *s;
*out = '\0';
return orig;
Demo on ideone.
Your code is returning a pointer to the end of the out array. Not the start. You need to stash the initial value of out and return that.
As an aside, the fact that you can do assignments inside a comparison doesn't mean it's a good idea. That code is going to be very hard to maintain.
&out[0] is equivalent to out. Since arrays in C are passed by reference, in a sense, yes it does return the entire array.
Your solution only prints "8" because you're returning a pointer into the middle of the array. When it tries to print the string, it has no way of knowing that it's in the middle of the array/string, thus you only get a substring printed.

create my own memset function in c

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 ?
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;
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).
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);
}
}
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);
}
You could use a duff device to have even better performance.
#define DUFF_DEVICE_8(aCount, aAction) \
do { \
int count_ = (aCount); \
int times_ = (count_ + 7) >> 3; \
switch (count_ & 7){ \
case 0: do { aAction; \
case 7: aAction; \
case 6: aAction; \
case 5: aAction; \
case 4: aAction; \
case 3: aAction; \
case 2: aAction; \
case 1: aAction; \
} while (--times_ > 0); \
} \
} while (0)
I tried one implementation like this:
void memset(void *b, int c, int len)
{
char *s = b;
while(len--)
*s++ = c;
}

what is wrong with this code snippet

Folks, here is an implementation of memset(), however I have been told that there is one logical mistake in the code. Could you help me find it.
I feel that a double pointer for the target string should be passed to this function, which will be like passing the address of the pointer variable and not the pointer itself.
I am getting an "access violation" when I execute the code in MS VC++ IDE.
The definition of the ā€˜Cā€™ Library function memset is
void *memset(char *s, char c, size_t n)
Copy c to the first n characters of s. Return s.
void *memset(char *s, char c, size_t n)
{
size_t i;
for (i = 0; i < n; i++, s++)
{
*s = c;
}
return s;
}
This has several problems.
void *memset(char *s, char c, size_t n)
{
size_t i;
for (i = 0; i < n; i++, s++) /* incrementing s means you cannot return */
{ /* the original value */
*s = c; /* consider using s[i] = c after not incr. s*/
}
return s; /* this should probably be a cast back to void */
}
You shouldn't be changing the pointer that is returned.
Check the return value of the function. What does it return? What is it documented to return?
I have a feeling that your size_t n may be off by one.
Also s points at the end of the string instead of the original s by the end of the function.
You modify the value of s and then return it. This means you will be returning a pointer to the end of the memset region, not the start (which is probably what you want)
You make the statement that you are getting a "access violation". That indicates that you are calling the function with a non-null value for 's', however, either 's' was not properly initialized
// bad - s will have some arbitrary value as allocated on the stack (all bets are off)
char *s;
memset(s,0,100);
// good
char s[100];
memset(s,0,100);
// bad - the memset will generate an access violation on byte 101
char s[100];
memset(s,0,101);
// good
char *s = malloc(100);
memset(s,0,100);
** one note not related to the access violation... returning 's' the way you do is not the same behavior as the traditional memset() in string.h. In that library, the return value is supposed to be the value of 's' as input. In your code you are returning a pointer to the byte after the last byte which would generate an access violation. for example:
// good
char *s = malloc(100);
char *d = memset(s,0,100);
printf("%s\n",d); // generates an access violation
in the memset() doc, d and s should have the same value. In your code, d = s[101];
Hum...
Try this:
void *memset (char* s, char c, size_t n){
char* begin = s;
char* end = begin + n;
whilw (begin != end) *begin++ = c;
return s;
}

Resources