Copying a character from one string argument to another in C - c

The following program crashes without an error message after trying to replace the first character in s with t. The purpose of the program is to test if the two strings s and t are isomorphic:
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
bool isIsomorphic(char *s, char *t);
int main()
{
isIsomorphic("egg", "add");
}
bool isIsomorphic(char *s, char *t)
{
//create two other char pointers for the characters one position before s and t.
char *preS = s;
char *preT = t;
//replace first character in s with t.
*s = *t //CRASHES HERE
//increment both pointers to their second character.
s ++;
t ++;
//run through t
while(t != NULL)
{
//if the characters in both strings are either a. both equal to their previous or b. both different to their previous:
if(((strcmp(t, preT) == 0) && (strcmp(s, preS) == 0)) || (((strcmp(t, preT) != 0) && (strcmp(s, preS) != 0))))
{
//copy t into s and shift both pointers along.
*s = *t;
s ++;
t ++;
}
else
{
printf("not isomorphic\n");
return false;
}
}
printf("isomorphic\n");
return true;
}
Why is this the case? Any help would be appreciated.

Modifying string literals is prohibited. Trying to do so invokes undefined behavior.
In this case the pointers are incremented after the assignments and the strings are not referenced after the call of isIsomorphic function, so you should remove the meaningless and harmful assignments (*s = *t;).
If you want to refer the modified string later, you should store the string to modify in a modifiable array like this:
int main(void)
{
char str[] = "egg";
isIsomorphic(str, "add");
}

You are using string literals
isIsomorphic("egg", "add");
that you are changing within the function isIsomorphic
*s = *t //CRASHES HERE
You may not change a string literal. Any attempt to change a string literal results in undefined behavior.
From the C Standard (6.4.5 String literals)
7 It is unspecified whether these arrays are distinct provided their
elements have the appropriate values. If the program attempts to
modify such an array, the behavior is undefined.
But in any case the function is incorrect.
Firstly the function shall not change passed to it strings. That is it shall be declared like
bool isIsomorphic( const char *s, const char *t);
Also these calls of the function strcmp in the if statement
if(((strcmp(t, preT) == 0) && (strcmp(s, preS) == 0)) || (((strcmp(t, preT) != 0) && (strcmp(s, preS) != 0))))
do not make a sense at least because strings pointed to by t and preT have different lengths. So this expression strcmp(t, preT) == 0 will always evaluate to logical false.
I can suggest the following function definition shown in the demonstrative program below.
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
bool isIsomorphic( const char *s, const char *t )
{
size_t n = strlen( s );
bool isomorphic = n == strlen( t );
while ( isomorphic && n-- )
{
const char *p1 = strchr( s + n + 1, s[n] );
const char *p2 = strchr( t + n + 1, t[n] );
isomorphic = ( !p1 && !p2 ) || ( p1 && p2 && p1 - s == p2 - t );
}
return isomorphic;
}
int main(void)
{
const char *s = "egg";
const char *t = "add";
printf( "\"%s\" is isomorphic with \"%s\" is %s\n",
s, t, isIsomorphic( s, t ) ? "true" : "false" );
s = "foo";
t = "bar";
printf( "\"%s\" is isomorphic with \"%s\" is %s\n",
s, t, isIsomorphic( s, t ) ? "true" : "false" );
s = "paper";
t = "title";
printf( "\"%s\" is isomorphic with \"%s\" is %s\n",
s, t, isIsomorphic( s, t ) ? "true" : "false" );
s = "0123456789";
t = "9876543210";
printf( "\"%s\" is isomorphic with \"%s\" is %s\n",
s, t, isIsomorphic( s, t ) ? "true" : "false" );
return 0;
}
The program output is
"egg" is isomorphic with "add" is true
"foo" is isomorphic with "bar" is false
"paper" is isomorphic with "title" is true
"0123456789" is isomorphic with "9876543210" is true

Related

How to count how many word in string?

I want to know how to count how many words are in a string.
I use strstr to compare and it works but only works for one time
like this
char buff = "This is a real-life, or this is just fantasy";
char op = "is";
if (strstr(buff,op)){
count++;
}
printf("%d",count);
and the output is 1 but there are two "is" in the sentence, please tell me.
For starters you have to write the declarations at least like
char buff[] = "This is a real-life, or this is just fantasy";
const char *op = "is";
Also if you need to count words you have to check whether words are separated by white spaces.
You can do the task the following way
#include <string.h>
#include <stdio.h>
#include <ctype.h>
//...
size_t n = strlen( op );
for ( const char *p = buff; ( p = strstr( p, op ) ) != NULL; p += n )
{
if ( p == buff || isblank( ( unsigned char )p[-1] ) )
{
if ( p[n] == '\0' || isblank( ( unsigned char )p[n] ) )
{
count++;
}
}
}
printf("%d",count);
Here is a demonstration program.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(void)
{
char buff[] = "This is a real-life, or this is just fantasy";
const char *op = "is";
size_t n = strlen( op );
size_t count = 0;
for ( const char *p = buff; ( p = strstr( p, op ) ) != NULL; p += n )
{
if ( p == buff || isblank( ( unsigned char )p[-1] ) )
{
if ( p[n] == '\0' || isblank( ( unsigned char )p[n] ) )
{
count++;
}
}
}
printf( "The word \"%s\" is encountered %zu time(s).\n", op, count );
return 0;
}
The program output is
The word "is" is encountered 2 time(s).
Parse the string, in a loop.
As OP has "but there are two "is" in the sentence", it is not enough just to look for "is" as that occurs 4x, twice in "This". Code needs to parse the string for the idea of a "word".
Case sensitively is also a concern.
char buff = "This is a real-life, or this is just fantasy";
char op = "is";
char *p = buff;
char *candidate;
while ((candidate = strstr(p, op)) {
// Add code to test if candidate is a stand-alone word
// Test if candidate is beginning of buff or prior character is a white-space.
// Test if candidate is end of buff or next character is a white-space/punctuation.
p += strlen(op); // advance
}
For me, I would not use strstr(), but look for "words" with isalpha().
// Concept code
size_t n = strlen(op);
while (*p) {
if (isalpha(*p)) { // Start of word
// some limited case insensitive compare
if (strnicmp(p, op, n) == 0 && !isalpha(p[n]) {
count++;
}
while (isalpha(*p)) p++; // Find end of word
} else {
p++;
}
}

How to find out if a word is inside of a line?

I am tring to make a function from exercise in book "Programming in C". The correct function should indicate if a line contain some word, if yes - return its first charcter position(of the word) in the line.
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
int substring (char a[], char b[]);
int main ()
{
char line1[15], line2[15];
printf("Print first one\n");
int i = 0;
char character;
do
{
character = getchar ();
line1[i] = character;
i++;
}
while (character != '\n');
line1[i-1] = '\0';
printf ("Print the second one\n");
scanf("%s", line2);
printf("%s, %s\n", line1, line2); \\ for checking lines
int index;
index = substring (line1, line2);
printf("The result is: %i\n", index);
}
int substring (char a[], char b[])\*function to determine if a line contains a word, if yes then return its polition, else return -3*\
{
int len1 = strlen(a), len2 = strlen(b);
int current1 = 0, current2 = 0;
bool found = false;
int result;
while( current1 < len1 )
{
if (a[current1] == b[current2])
{
if(!found)
{
result = current1+1;
}
else
found = true;
while ((a[current1] == b[current2]) && (a[current1] != '\0') && (b[current2] != '\0'))
{
current1++;
if(current2+1 == len2)
return result;
current2++;
}
current1 = result;
}
else
{
current2 = 0;
found = false;
current1++;
}
}
return -3;
}
The problem is somehow in the second function(substring), cause when i try to search for "word" in line "Here is your word", fucntion works properly, but when i try to search "word" in a line "Here is your wwwwword", function returns -3 (which is indication if something went wrong).
For starters there is the standard C string function strstr that allows to determine whether one string is contained in other string.
If you need to write such a function yourself then your function implementation looks too complicated.
For example the if-else statement
if (a[current1] == b[current2])
{
if(!found)
{
result = current1+1;
}
else
found = true;
// ...
does not make a great sense at least by two reasons. The first one is the variable result that can be returned from the function in the while loop does not contain the exact index of the found word. And the second one is that found never can be equal to true within the function. So the condition in the if statement
if( !found )
always evaluates to true.
Also you forgot to reset the variable current2 in the compound statement of the if statement after the while loop.
Apart from this the function parameters should have the qualifier const because passed strings are not being changed in the function. And the function return type shall be size_t.
The function can look for example the following way as it is shown in the demonstrative program below.
#include <stdbool.h>
#include <string.h>
size_t substring( const char *s1, const char *s2 )
{
size_t n1 = strlen( s1 );
size_t n2 = strlen( s2 );
size_t i = 0;
bool found = false;
if (!( n1 < n2 ))
{
for ( size_t n = n1 - n2; !found && i <= n; i += !found )
{
size_t j = 0;
while (s2[j] != '\0' && s2[j] == s1[i + j]) ++j;
found = s2[j] == '\0';
}
}
return found ? i : -1;
}
int main( void )
{
const char *word = "word";
const char *s1 = "Here is your word";
const char *s2 = "Here is your wwwwword";
printf( "%zu\n", substring( s1, word ) );
printf( "%zu\n", substring( s2, word ) );
}
The program output is
13
17
If a word is not found in the source string then the function returns a value of the type size_t that is equal to -1.
Pay attention to that the variable character should be declared having the type int instead of char. Otherwise in general the comparison with EOF can produce unexpected result if the type char behaves as the type unsigned char.

Can you help me find a problem with this basic C code

I'm solving some problems from a C programming book to brush up on Strings. I can't figure out why my solution is not working.
Question asks to write a function named censor that modifies a string by replacing every occurrence of foo by xxx.
My code:
int main()
{
char msg[] = "I love food, you fool.";
censor(msg);
puts(msg);
return 0;
}
void censor(char *str) {
char *c = str;
while (c+2 != '\0') {
if (*c == 'f' && *(c+1) == 'o' && *(c+2) == 'o')
*c = *(c+1) = *(c+2) = 'x';
c++;
}
}
I found that the while loop runs for like 1700 times. I'm pretty sure msg[] will include a null character automatically end of string.
You're checking the value of the pointer, not what it points to. So instead of this:
while (c+2 != '\0') {
You want this:
while (*(c+2) != '\0') {
If I have understood correctly you may not yet use standard C string functions and the function censor should be written using pointers.
For starters such a string function should return a pointer to the modified string. That is the function return type should be char * instead of void.
The condition in the while loop
while (c+2 != '\0') {
is equivalent to
while (c+2 != NULL) {
because the expression c + 2 has the pointer type char *. So the condition is incorrect.
Moreover in general if you will even change the condition like
while ( *( c+2 ) != '\0') {
the loop can have undefined behavior if the user will pass a string that has less than two characters.
The function can be defined as it is shown in the demonstrative program below.
#include <stdio.h>
char * censor( char *s )
{
const char *s1 = "foo";
const char *s2 = "xxx";
for ( char *p = s; *p; )
{
const char *t1 = s1;
while ( *t1 && *t1 == *p )
{
++t1; ++p;
}
p -= t1 - s1;
if ( *t1 == '\0' )
{
for ( const char *t2 =s2; *t2; ++t2 )
{
*p++ = *t2;
}
}
else
{
++p;
}
}
return s;
}
int main( void )
{
char msg[] = "I love food, you fool.";
puts( msg );
puts( censor( msg ) );
return 0;
}
The program output is
I love food, you fool.
I love xxxd, you xxxl
The code of the shown function does not depend on the strings "foo" and "xxx". The pointers s1 and s2 can be initialized with any other strings that have an equal length.

Finding the last occurrence of a string in a sentence in C

I am practicing and I came across an exercise. The exercise says I am to manually write a function that finds the index of the last occurrence in a string. Now, I get that this has maybe been asked before but I cannot find what is the problem with my code. It works for almost all instances, but not so when the last occurrence of the word is at the beginning of the string.
What I have tried: I used pointers to store the addresses of the ends of both the sentence and the word we are looking for. I then used a while loop to iterate through the string. If the current character matches the last character of the word we are searching for, we enter another while loop which compares the two. If the pointer that points to the beginning of the word and the one we used to iterate through the word are equal, the word is found.
Here is some code:
#include <stdio.h>
int find_last( char *str, char *word)
{
char *p, *q;
char *s, *t;
p=str; /* Pointer p now points to the last character of the sentence*/
while(*p!='\0') p++;
p--;
q = word;
while(*q!='\0') q++; /* Pointer q now points to the last character of the word*/
q--;
while(p != str) {
if(*p == *q) {
s=p; /* if a matching character is found, "s" and "t" are used to iterate through */
/* the string and the word, respectively*/
t=q;
while(*s == *t) {
s--;
t--;
}
if(t == word-1) return s-str+1; /* if pointer "t" is equal by address to pointer word-1, we have found our match. return s-str+1. */
}
p--;
}
return -1;
}
int main()
{
char arr[] = "Today is a great day!";
printf("%d", find_last(arr, "Today"));
return 0;
}
So, this code should return 0 but it returns -1.
It works in every other instance I tested! When ran in CodeBlocks, the output is as expected (0), but using any other online IDE I could find the output is still -1.
For starters the parameters of the function shall have the qualifier const and its return type should be either size_t or ptrdiff_t.
For example
ptrdiff_t find_last( const char *str, const char *word );
In any case the function shall be declared at least like
int find_last( const char *str, const char *word );
The function should emulate the behavior of the standard C function strstr. That is when the second argument is an empty string the function should return 0.
If either of the arguments is an empty string then your function has undefined behavior due to these statements
p=str; /* Pointer p now points to the last character of the sentence*/
while(*p!='\0') p++;
p--;
^^^^
q = word;
while(*q!='\0') q++; /* Pointer q now points to the last character of the word*/
q--;
^^^^
If the string pointed to by str contains only one symbol then your function returns -1 because the condition of the loop
while(p != str) {
evaluates to false independent on whether the both strings are equal each other or not.
This loop
while(*s == *t) {
s--;
t--;
}
again can invoke undefined behavior because there can be an access to memory that precedes the string word.
And this statement
if(t == word-1) return s-str+1;
also can invoke the undefined behavior by the same reason.
The function can be defined as it is shown in the demonstrative program below.
#include <stdio.h>
int find_last( const char *str, const char *word )
{
const char *p = str;
int found = !*word;
if ( !found )
{
while ( *p ) ++p;
const char *q = word;
while ( *q ) ++q;
while ( !found && !( p - str < q - word ) )
{
const char *s = p;
const char *t = q;
while ( t != word && *( s - 1 ) == *( t - 1) )
{
--s;
--t;
}
found = t == word;
if ( found ) p = s;
else --p;
}
}
return found ? p - str : -1;
}
int main(void)
{
const char *str = "";
const char *word = "";
printf( "find_last( str, word ) == %d\n", find_last( str, word ) );
word = "A";
printf( "find_last( str, word ) == %d\n", find_last( str, word ) );
str = "A";
printf( "find_last( str, word ) == %d\n", find_last( str, word ) );
str = "ABA";
printf( "find_last( str, word ) == %d\n", find_last( str, word ) );
str = "ABAB";
printf( "find_last( str, word ) == %d\n", find_last( str, word ) );
str = "ABCDEF";
printf( "find_last( str, word ) == %d\n", find_last( str, word ) );
str = "ABCDEF";
word = "BC";
printf( "find_last( str, word ) == %d\n", find_last( str, word ) );
return 0;
}
The program output is
find_last( str, word ) == 0
find_last( str, word ) == -1
find_last( str, word ) == 0
find_last( str, word ) == 2
find_last( str, word ) == 2
find_last( str, word ) == 0
find_last( str, word ) == 1

Assert is failing in a string array comparison [duplicate]

This question already has answers here:
How do I properly compare strings in C?
(10 answers)
Why isn't the size of an array parameter the same as within main?
(13 answers)
Closed 3 years ago.
In this part of my code I remove white spaces of string1 and copy the result to string2.
char * remove_blank_spaces(char * string1) {
char * string2 = malloc(sizeof(string1));
int index = 0;
for (int i = 0; string1[i] != 0; i++) {
if(string1[i] != ' ') {
//printf("i: %d\n", i);
//printf("c2: %c\n", string1[i]);
string2[index] = string1[i];
index++;
}
}
string2[index] = '\0';
printf("string2: %s\n", string2);
return string2;
}
I check the result with:
assert(remove_blank_spaces("a b") == "ab"); // Edit: here is the error!
I got an error: Assertion failed! and Expression: remove_blank_spaces("a b") == "ab"
I compared the strings in Virtual-C and they look the same.
Why the assertion is failing?
Your code has a bug: malloc allocates insufficient space, and this results in undefined behaviour when trying to access unallocated memory.
The assertion is also failing because you are comparing pointers via ==, instead of C strings via strcmp.
Furthermore, I suggest making two changes:
Don’t mix computation and output. Return the value, don’t printf it inside the function.
Use descriptive and correct names. This requires taking context into account. For instance, index can generally be a good name, but in your case it’s unclear which index you’re referring to, and this invites errors, where index is used to index into the wrong variable. As for “correct” names, what you call “blank space” is more conventionally known as “whitespace”.
To improve the second point, I suggest actually changing the implementation and, instead of having a second index variable, to iterate over the output using a pointer. There are other possibilities, but this one has the advantage that accidentally indexing using the wrong variable is impossible.
Taking this together, we get
char *remove_whitespace(const char *str) {
char *result = malloc(strlen(str) + 1);
char *out = result;
for (size_t i = 0; str[i] != '\0'; i++) {
if (str[i] != ' ') {
*out++ = str[i];
}
}
*out = '\0';
return result;
}
We could additionally do away with the i loop counter. Unfortunately the result is less readable, not more, because we would need to increment str at the end of the loop, and this would leave us with an unsightly for (; *str != '\0'; str++) loop construct.
For starters this function declaration
char * remove_blank_spaces(char * string1) {
is incorrect and only confuses users of the function. If within the function you are creating a new character array then the parameter shall have the qualifier const.
char * remove_blank_spaces( const char * string1) {
Otherwise the function should change the original string "in-place".
This call
char * string2 = malloc(sizeof(string1));
also is incorrect. I think you mean
char * string2 = malloc( strlen( string1 ) + 1 );
But even this call is not very good because the result string can be much less than the original string.
So at first you should count the numb er of characters in the result string and only then allocate the memory.
This assert is also incorrect
assert(remove_blank_spaces("a b") == "ab");
In this expression there are compared addresses of two string: the first one is the string returned by the function and the second one is the string literal.
Even if you will write an expression like this
assert( "ab" == "ab");
the value of the expression can be equal either to logical true or false depending on the compiler option that specifies whether equal string literals are stored as one string literal or occupy different extents of memory.
You should write instead
assert( strcmp( remove_blank_spaces("a b"), "ab" ) == 0 );
Take into account that it is reasonable also to consider trhe tab character '\t' in the if statement like
if(string1[i] != ' ' && string1[i] != '\t') {
Or you could use the standard function isblank.
Here is a demonstrative program
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>
char * remove_blank_spaces( const char *s )
{
size_t n = 0;
for ( size_t i = 0; s[i] != '\0'; i++ )
{
if ( !isblank( ( unsigned char )s[i] ) ) ++n;
}
char *result = malloc( n + sizeof( ( char )'\0' ) );
char *p = result;
do
{
if ( !isblank( ( unsigned char )*s ) )
{
*p++ = *s;
}
} while ( *s++ != '\0' );
return result;
}
int main(void)
{
const char *s1 = "a b";
char *s2 = remove_blank_spaces( s1 );
assert( strcmp( s1, s2 ) == 0 );
puts( s2 );
free( s2 );
return 0;
}
The program output is
ab
Pay attention to that instead of the type int as it is shown in other answers you should use the type size_t for the variables index and i because it is the type that is used with string lengths and indices and by the function malloc. The type int is not large enough to store size of strings.
If you indeed want to declare the function like
char * remove_blank_spaces( char *s )
that is when the parameter does not have the qualifier const then you shall not allocate dynamically a new character array within the function and the function itself can look much simpler.
Here is a demonstrative program.
#include <stdio.h>
#include <assert.h>
#include <string.h>
char * remove_blank_spaces( char *s )
{
char *destination = s;
char *source = s;
do
{
if ( *source != ' ' && *source != '\t' )
{
*destination++ = *source;
}
} while ( *source++ != '\0' );
return s;
}
int main(void)
{
char s[] = "a b";
remove_blank_spaces( s );
assert( strcmp( s, "ab" ) == 0 );
puts( s );
return 0;
}
Its output is
ab

Resources