What is wrong with my version of strchr? - c

My assignment is to write my own version of strchr, yet it doesn't seem to work. Any advice would be much appreciated.
Here it is:
char *strchr (const char *s, int c) //we are looking for c on the string s
{
int dog; //This is the index on the string, initialized as 0
dog = 0;
int point; //this is the pointer to the location given by the index
point = &s[dog];
while ((s[dog] != c) && (s[dog] != '\0')) { //it keeps adding to dog until it stumbles upon either c or '\0'
dog++;
}
if (s[dog]==c) {
return point; //at this point, if this value is equal to c it returns the pointer to that location
}
else {
return NULL; //if not, this means that c is not on the string
}
}

You are trying to store an address into point but it's an int-variable. You should do something like this:
char *strchr(char *s, char c) {
int pos = 0;
while (s[pos] != c && s[pos] != '\0')
pos++;
if (s[pos] == c)
return &s[pos];
else
return NULL;
}
By the way: s should be char * not const char * because your returning an pointer to achar, and that wouldn't be a good style ;) (or return const char *)

You return "point" which was originally initialized to beginning of string and not moved since then. You don't need that variable at all, but could simply return &s[dog] (although I would prefer something more descriptive than dog as a variable name).
In fact you would survive with something as simple as this:
while (*s != c && *s)
++s;
return (*s == c) ? s : NULL;

int point;
This is not the declaration of a pointer, here is how to declare a pointer to an int:
int *bla;
In your case &s[dog] is a pointer to a const char, so you want to declare point this way:
const char *point;
And as others pointed out, you are actually ignoring this pointer in your function afterwards.

Here in your code
int point; //this is the pointer to the location given by the index
point = &s[dog];
you are trying to convert a pointer to char to an int, when
char* point = &s[dog];
is what you want. You should have seen this from the return type of your function. You want to return a char* but you return an int (your variable point) or NULL. As you never actually change point, you are actually returning the address of the first character in your array, so your code is not working as you would like anyway.
If you insist on this you would be better using
char* point = &s[dog];
while ((*point != c) && (*point != '\0')) {
++point;
}
return (*point == c) ? point : NULL;
But here it looks like you still have a conceptual problem as you want to compare a char with an int. You should work out if you need an int array or a char array. If you want a char array, change your input argument c to be of type char.

Related

String doesn't "rewind" itself when accessing via pointer

char *ft_between(char *str, size_t from, size_t to)
{
char *between;
between = malloc(16);
while ((from >= 0) && (from < to) && (to < ft_strlen(str)))
{
*(between++) = str[from++];
}
*between = '\0';
printf("%s\n", between); // print nothing
printf("%s\n", between - 16); // print between but never had to do this before...
return (between);// even on calling function the pointer still at end of string
}
I think it's because I changed the address of between using ++ but I usually do that and never had this behavior... is that because of malloc ???
Is there someting I missed ?
Is thear a way to "rewind" the string lol
If I do it via a counter ie. between[counter++] = str[from++]; it works but I wanted to do via pointers as it's faster... from what I've red !
In this example str is iterate with ++ until the end to add char
but when return in calling function a printf will print all str
void ft_nbr2str(char *str, size_t nbr, char *base, size_t base_len)
{
if (nbr >= base_len)
{
ft_nbr2str(str, (nbr / base_len), base, base_len);
while (*str != '\0')
str++;
*str = base[nbr % base_len];
}
else
*str = base[nbr];
}
I think it's because I changed the address of between using ++
It's because you modified the value of between via the ++ operator. That value is the address of something else. The address of between or any other object cannot be modified.
but I usually do that and never had this behavior.
The behavior you describe is absolutely normal, so either no, you don't usually do that, or yes you did have that behavior. In your code, you will observe the same effect on from. I really don't fathom why immediately after you execute *between = '\0'; you would expect printf("%s\n", between) to print a non-empty string. malloc has nothing in particular to do with it.
I speculate that in other cases you may have instead modified a copy of your pointer, which, naturally, does not modify the original pointer. Possibly you did this by passing your pointer (by value) to another function. Example:
void strcpy_range(char *dest, char *src, size_t from, size_t to) {
while ((from >=0) && (from < to) && (src[from] != '\0'))
{
*(dest++) = src[from++]; // dest is modified
}
*dest = '\0';
}
char *ft_between(char *str, size_t from, size_t to)
{
char *between = malloc(16);
strcpy_range(between, str, from, to);
printf("%s\n", between); // prints the extracted substring
return between; // returns a pointer to the extracted substring
}
If you want to rescue your original version without introducing a new function, then use a temporary variable to track the current location in the substring. For example,
char *ft_between(char *str, size_t from, size_t to)
{
char *between = malloc(16);
char *temp = between;
while ((from >=0) && (from < to) && (to < ft_strlen(str)))
{
*(temp++) = str[from++];
}
*temp = '\0';
printf("%s\n", between); // prints the extracted substring
return between; // returns the extracted substring
}
Addendum
The alternative example added to the question demonstrates exactly the form I speculated you might have used. The (non-)effect on the caller's copy of the pointer in that case is not analogous to or even related to the modification of the function parameter observed during execution of the first function presented in the question.
After you've incremented the pointer, it now points to a different region of memory. Since the pointer is of type char, summing one unit is the same as summing sizeof(char) units, which turns out to still be 1; to 'rewind' it, as you say, you'd just have to subtract 16 * sizeof(char) = 16 (notice you're dereferencing the pointer summed by 16, so it makes perfect sense to subtract 16 to get it back to its position, or subtract however many times you want so that it points to the location you expect it to)
After this statement
*between = '\0';
the pointer between points to an empty string. So this call of printf:
printf("%s\n", between); // print nothing
indeed will output nothing.
And this return statement
return (between);// even on calling function the pointer still at end of string
returns this pointer to an empty string.
Pay attention to that this condition
(from >=0)
does not make sense because objects of the unsigned type size_t can not be negative.
Also it is unclear why there is used the magic number 16
between = malloc(16);
and
printf("%s\n", between - 16); // print between but never had to do this before...
And the function should not output any message. It is the caller of the function that will decide whether to output something.
The function can be declared and defined the following way
char * ft_between( const char *str, size_t from, size_t to )
{
char *between;
size_t n = ft_strlen( str );
if ( n < to ) to = n;
if ( to <= from )
{
between = calloc( 1, sizeof( char ) );
}
else
{
between = malloc( to - from + 1 );
if ( between != NULL )
{
char *p = between;
while ( from != to ) *p++ = str[from++];
*p = '\0';
}
}
return between;
}

assign a char array to the other which is also a argument of function in c

I run this code and get console shutdown every time.
could you help me? thank you
int lower(int a)
{
if ((a >= 0x41) && (a <= 0x5A))
a |= 0x20;
return a;
}
void get_lower_case(char** str)
{
int str_len = strlen(*str);
char *temp = (char *)malloc(str_len);
while (**str)
{
*temp =lower(**str);
temp++;
*str++;
}
strncpy( *str, temp, str_len);
free(temp);
}
int main()
{
char searchChars[] = "findstring";
get_lower_case(&searchChars);
return (0);
}
or there is any other way that I can change char array as argument?
Continuing from the comments, there is no need to pass a pointer to pointer to char to get_lower_case. You have a modifiable string in searchChars, so simply pass the string and operate on the characters within it. You will greatly simply your conversion to lower case.
Also, don't use magic numbers in your code, e.g. (a >= 0x41) && (a <= 0x5A), (a >= 'A') && (a <= 'Z') is much easier to read. You can keep 0x20 as 32.
Putting the pieces together, you could do something like the following:
#include <stdio.h>
int lower(int a)
{
if ((a >= 'A') && (a <= 'Z'))
a |= 0x20;
return a;
}
void get_lower_case (char *str)
{
while (*str) {
*str = lower(*str);
str++;
}
}
int main (void)
{
char searchChars[] = "FINDstring";
get_lower_case(searchChars);
printf ("%s\n", searchChars);
return (0);
}
Example Use/Output
$ ./bin/tolower
findstring
The issue is in the while loop and the logic that is written to copy the string from source to destination.
while (**str)
{
*temp =lower(**str);
temp++; //pointer is moved to next location of memory
*str++; //pointer is moved to next location of memory
}
strncpy( *str, temp, str_len); //at this point both pointers are pointing to the end of the string
free(temp); //pointer is pointing to the end of the memory
In the while loop both the pointers are incremented to next location of memory and at the end of that loop both will point to the end of the string, the next statement is strncpy() and free() which is obviously going to fail as strncpy will copy garbage value to str and temp has moved to end so call to free will also fail.
The best solution is the above one that #David C. Rankin has suggested or an alternative is, you can take two more pointers to point to temp and str at the beginning of the function get_lower_case() and use those two pointers to copy the string.

Creating a simplified version of strchr() [duplicate]

This question already has answers here:
Why does strchr take an int for the char to be found?
(4 answers)
Closed 6 years ago.
Trying to create a simple function that would look for a single char in a string "like strchr() would", i did the following:
char* findchar(char* str, char c)
{
char* position = NULL;
int i = 0;
for(i = 0; str[i]!='\0';i++)
{
if(str[i] == c)
{
position = &str[i];
break;
}
}
return position;
}
So far it works. However, when i looked at the prototype of strchr():
char *strchr(const char *str, int c);
The second parameter is an int? I'm curious to know.. Why not a char? Does this mean that we can use int for storing characters just like we use a char?
Which brings me to the second question, i tried to change my function to accept an int as a second parameter... but i'm not sure if it's correct and safe to do the following:
char* findchar(char* str, int c)
{
char* position = NULL;
int i = 0;
for(i = 0; str[i]!='\0';i++)
{
if(str[i] == c) //Specifically, is this line correct? Can we test an int against a char?
{
position = &str[i];
break;
}
}
return position;
}
Before ANSI C89, functions were declared without prototypes. The declaration for strchr looked like this back then:
char *strchr();
That's it. No parameters are declared at all. Instead, there were these simple rules:
all pointers are passed as parameters as-is
all integer values of a smaller range than int are converted to int
all floating point values are converted to double
So when you called strchr, what really happened was:
strchr(str, (int)chr);
When ANSI C89 was introduced, it had to maintain backwards compatibility. Therefore it defined the prototype of strchr as:
char *strchr(const char *str, int chr);
This preserves the exact behavior of the above sample call, including the conversion to int. This is important since an implementation may define that passing a char argument works differently than passing an int argument, which makes sense on 8 bit platforms.
Consider the return value of fgetc(), values in the range of unsigned char and EOF, some negative value. This is the kind of value to pass to strchr().
#Roland Illig presents a very good explanation of the history that led to retaining use of int ch with strchr().
OP's code fails/has trouble as follows.
1) char* str is treated like unsigned char *str per §7.23.1.1 3
For all functions in this subclause, each character shall be interpreted as if it had the type unsigned char
2) i should be type size_t, to handle the entire range of the character array.
3) For the purpose of strchr(), the null character is considered part of the search.
The terminating null character is considered to be part of the string.
4) Better to use const as str is not changed.
char* findchar(const char* str, int c) {
const char* position = NULL;
size_t i = 0;
for(i = 0; ;i++) {
if((unsigned char) str[i] == c) {
position = &str[i];
break;
}
if (str[i]=='\0') break;
}
return (char *) position;
}
Further detail
The strchr function locates the first occurrence of c (converted to a char) in the string pointed to by s. C11dr §7.23.5.2 2
So int c is treat like a char. This could imply
if((unsigned char) str[i] == (char) c) {
Yet what I think this is meant:
if((unsigned char) str[i] == (unsigned char)(char) c) {
or simply
if((unsigned char) str[i] == (unsigned char)c) {

How can I make a function to remove double letters in C?

I am trying to make a function that removes double letters from a string. The function is only supposed to remove double letters next to each other, not in the whole string. e.g 'aabbaa' would become 'aba' (not 'ab'). Im a fairly new to c programming and dont fully understand pointers etc. and need some help. Below is what I have so far. It does not work at all, and I have no idea what to return since when I try and return string[] it has an error:
char doubleletter( char *string[] ) {
char surname[25];
int i;
for((i = 1) ; string[i] != '\0' ; i++) {
if (string[i] == string[(i-1)]) { //Supposed to compare the ith letter in array with one before
string[i] = '\0' ; //Supposed to swap duplicate chars with null
}
}
surname[25] = string;
return surname ;
Try the following. It is a clear simple and professionally-looked code.:)
#include <stdio.h>
char * unique( char *s )
{
for ( char *p = s, *q = s; *q++; )
{
if ( *p != *q ) *++p = *q;
}
return s;
}
int main(void)
{
char s[] = "aabbaa";
puts( unique( s ) );
return 0;
}
The output is
aba
Also the function can be rewritten the following way that to escape unnecassary copying.
char * unique( char *s )
{
for ( char *p = s, *q = s; *q++; )
{
if ( *p != *q )
{
( void )( ( ++p != q ) && ( *p = *q ) );
}
}
return s;
}
Or
char * unique( char *s )
{
for ( char *p = s, *q = s; *q++; )
{
if ( *p != *q && ++p != q ) *p = *q;
}
return s;
}
It seems that the last realization is the best.:)
First of all delete those parenthenses aroung i = 1 in for loop (why you put them there in the first place ?
Secondly if you put \0 in the middle of the string, the string will just get shorter.
\0 terminates array (string) in C so if you have:
ababaabababa
and you replace second 'a' in pair with \0:
ababa\0baba
effectively for compiler it will be like you just cut this string to:
ababa
Third error here is probably that you are passing two-dimensional array to function here:
char *string[]
This is equivalent to passing char **string and essentialy you are passing array of strings while you wanna only to pass a string (which means a pointer, which means an array: char *string or ofc char string[])
Next thing: you are making internal assumption that passed string will have less than 24 chars (+ \0) but you don't check it anywhere.
I guess easiest way (though maybe not the most clever) to remove duplicated chars is to copy in this for loop passed string to another one, omitting repeated characters.
One example, It does not modify input string and returns a new dynamically allocated string. Pretty self explanatory I think:
char *new_string_without_dups(const char *input_str, size_t len)
{
int i = 1;
int j = 0;
char tmpstr[len+1] = {0};
for (; i < len; i++) {
if (input_str[i] == input_str[i-1]) {
continue;
}
tmpstr[j] = input_str[i];
j++;
}
return strdup(tmpstr);
}
Don't forget to free the returned string after usage.
Note that there are several ways to adapt/improve this. One thing now is that it requires C99 std due to array size not being known at compile time. Other things like you can get rid of the len argument if you guarantee a \0 terminated string as input. I'll leave that as excercises.
Your idea behind the code is right, but you are making two fundamental mistakes:
You return a char [] from a function that has char as return type. char [], char * and char are three different types, even though in this case char [] and char * would behave identically. However you would have to return char * from your function to be able to return a string.
You return automatically allocated memory. In other languages where memory is reference counted this is OK. In C this causes undefined behavior. You cannot use automatic memory from within a function outside this very function. The memory is considered empty after the function exits and will be reused, i.e. your value will be overwritten. You have to either pass a buffer in, to hold the result, or do a dynamic allocation within the function with malloc(). Which one you do is a matter of style. You could also reuse the input buffer, but writing the function like that is undesirable in any case where you need to preserve the input, and it will make it impossible for you to pass const char* into the function i.e. you would not be able to do do something like this:
const char *str = "abbc";
... doubleletter(str,...);
If I had to write the function I would probably call it something like this:
int doubleletter (const char *in, size_t inlen, char *out, size_t outlen){
int i;
int j = 0;
if (!inlen) return 0;
if (!outlen) return -1;
out [j++] = in[0];
for (i = 1; i < inlen; ++i){
if (in[i - 1] != in[i]){
if (j > outlen - 1) return -1;
out[j++] = in[i];
}
}
out[j] = '\0';
return j - 1;
}
int main(void) {
const char *str1 = "aabbaa";
char out[25];
int ret = doubleletter(str1, strlen(str1), out, sizeof(out)/sizeof(out[0]));
printf("Result: %s", out);
return 0;
}
I would recommend using 2 indices to modify the string in-place:
void remove_doubles(char *str)
{
// if string is 1 or 0 length do nothing.
if(strlen(str)<=1)return;
int i=0; //index (new string)
int j=1; //index (original string)
// loop until end of string
while(str[j]!=0)
{
// as soon as we find a different letter,
// copy it to our new string and increase the index.
if(str[i]!=str[j])
{
i++;
str[i]=str[j];
}
// increase index on original/old string
j++;
}
// mark new end of string
str[i+1]='\0';
}

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