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

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.

Related

Why my returned value of strchr is ignored?

I have to make a function, that will code my sentence like this: I want to code all words with an o, so for example I love ice cream becomes I **** ice cream.
But my function ignores the result of strchr. And I don't know why.
This is my code:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define LEN 1000
char *Shift(char *str, char *let) {
const char *limits = " ,-;+.";
char copy[LEN];
strcpy(copy, str);
char *p;
char *ptr;
ptr = strtok(copy, limits);
for (int j = 0; ptr != NULL; ptr = strtok(NULL, limits), ++j) {
int len = 0;
if (strchr(ptr, let) != NULL) {
p = strstr(str, ptr);
for (int i = 0; i < strlen(ptr); i++) {
p[i] = "*";
}
}
}
return str;
}
int main() {
char *s = Shift("I love my cocktail", "o");
puts(s);
}
Expected output is: I **** my ********
but I've got just printed the original string
For starters the function strchr is declared like
char *strchr(const char *s, int c);
that is its second parameter has the type int and the expected argument must represent a character. While you are calling the function passing an object of the type char * that results in undefined behavior
if (strchr(ptr, let) != NULL) {
It seems you mean
if (strchr(ptr, *let) != NULL) {
Also you may not change a string literal. Any attempt to change a string literal results in undefined behavior and this code snippet
p = strstr(str, ptr);
for (int i = 0; i < strlen(ptr); i++) {
p[i] = "*";
}
tries to change the string literal passed to the function
char *s = Shift("I love my cocktail", "o");
And moreover in this statement
p[i] = "*";
you are trying to assign a pointer of the type char * to a character. At least you should write
p[i] = '*';
If you want to change an original string you need to store it in an array and pass to the function the array instead of a string literal. For example
char s[] = "I love my cocktail";
puts( Shift( s, "o" ) );
Pay attention to that there is no great sense to declare the second parameter as having the type char *. Declare its type as char.
Also the function name Shift is confusing. You could name it for example like Hide or something else.
Here is a demonstration program.
#include <stdio.h>
#include <string.h>
char * Hide( char *s, char c )
{
const char *delim = " ,-;+.";
for ( char *p = s += strspn( s, delim ); *p; p += strspn( p, delim ) )
{
char *q = p;
p += strcspn( p, delim );
char *tmp = q;
while ( tmp != p && *tmp != c ) ++tmp;
if ( tmp != p )
{
for ( ; q != p; ++q ) *q = '*';
}
}
return s;
}
int main( void )
{
char s[] = "I love my cocktail";
puts(s);
puts( Hide( s, 'o' ) );
}
The program output is
I love my cocktail
I **** my ********
The for loop
for ( ; q != p; ++q ) *q = '*';
within the function can be rewritten as a call of memset
memset( q, '*', p - q );
There are multiple problems:
copying the string to a fixed length local array char copy[LEN] will cause undefined behavior if the string is longer than LEN-1. You should allocate memory from the heap instead.
you work on a copy of the string to use strtok to split the words, but you do not use the correct method to identify the parts of the original string to patch.
you should pass a character to strchr(), not a string.
patching the string with p[i] = "*" does not work: the address of the string literal "*" is converted to a char and stored into p[i]... this conversion is meaningless: you should use p[i] = '*' instead.
attempting to modify a string literal has undefined behavior anyway. You should define a modifiable array in main and pass the to Shift.
Here is a corrected version:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *Shift(char *str, char letter) {
const char *limits = " ,-;+.";
char *copy = strdup(str);
char *ptr = strtok(copy, limits);
while (ptr != NULL) {
if (strchr(ptr, letter)) {
while (*ptr != '\0') {
str[ptr - copy] = '*';
ptr++;
}
}
ptr = strtok(NULL, limits);
}
free(copy);
return str;
}
int main() {
char s[] = "I love my cocktail";
puts(Shift(s, 'o'));
return 0;
}
The above code still has undefined behavior if the memory cannot be allocated. Here is a modified version that operates in place to avoid this problem:
#include <ctype.h>
#include <stdio.h>
#include <string.h>
char *Shift(char *str, char letter) {
char *ptr = str;
while ((ptr = strchr(ptr, letter)) != NULL) {
char *p = ptr;
while (p > str && isalpha((unsigned char)p[-1]))
*--p = '*';
while (isalpha((unsigned char)*ptr)
*ptr++ = '*';
}
return str;
}
int main() {
char s[] = "I love my cocktail";
puts(Shift(s, 'o'));
return 0;
}
Note that you can also search for multiple characters at a time use strcspn():
#include <ctype.h>
#include <stdio.h>
#include <string.h>
char *Shift(char *str, const char *letters) {
char *ptr = str;
while (*(ptr += strcspn(ptr, letters)) != '\0') {
char *p = str;
while (p > str && isalpha((unsigned char)p[-1]))
*--p = '*';
while (isalpha((unsigned char)*ptr)
*ptr++ = '*';
}
return str;
}
int main() {
char s[] = "I love my Xtabentun cocktail";
puts(Shift(s, "oOxX"));
return 0;
}

How do I return a char from a char pointer function in C?

i recently made a function that finds the smallest character in a string. I am not sure how to return the smallest character in a char pointer function.
#include <stdio.h>
#include <string.h>
char * smallest(char s[])
{
char small = 'z';
int i = 0;
while (s[i] != '\0')
{
if (s[i] < small)
{
small = s[i];
}
i++;
}
return small;
}
int main(void)
{
char s[4] = "dog";
printf("%c",smallest(s));
}
The variable small has the type char according to its declaration
char small = 'z';
//...
return small;
and this variable is returned from the function while the function return type is the pointer type char *.
char * smallest(char s[])
Also if the user will pass an empty string to the function then you will try to return the character 'z' as a smallest character though this character is absent in the empty string.
I think in this case you should return a pointer to the terminating zero character '\0'.
The function can be defined the following way
char * smallest( char s[] )
{
char *small = s;
if ( *s )
{
while ( *++s )
{
if ( *s < *small ) small = s;
}
}
return small;
}
Or as in C there is no function overloading then the function should be declared and defined like
char * smallest( const char s[] )
{
const char *small = s;
if ( *s )
{
while ( *++s )
{
if ( *s < *small ) small = s;
}
}
return ( char * )small;
}
Pay attention to that this assert
assert(smallest(s[4] == 'd'));
is incorrect, It seems you mean
assert( *smallest( s ) == 'd');
Or after you updated your program you need to write
printf("%c\n",*smallest(s));
instead of
printf("%c",smallest(s));
Using this function you can not only to find the smallest character but also to determine its position in the source string.
For example
char *small = smallest( s );
printf( "The smallest character is '%c' at the position %tu\n",
*small, small - s );
or
char *small = smallest( s );
if ( *small == '\0' )
{
puts( "The source string is empty" );
}
else
{
printf( "The smallest character is '%c' at the position %tu\n",
*small, small - s );
}
There are two problems with your program.
1. Wrong parameters
The function smallest(char[] s)expects to be given an character array but what you are passing in as an argument is s[4] == 'd' which is not a character array.
This has nothing to do with the assert() itself.
What you want to do is assert(smallest(s) == 'd').
2. Wrong return type
Your function is declares that it would return *char (= a pointer to a char) but you are trying to return a char. So you should adjust the return type of your function to be char.
The correct program:
#include <stdio.h>
#include <assert.h>
#include <string.h>
char smallest(char s[]) {
char small = 'z';
int i = 0;
while(s[i] != '\0') {
if (s[i] < small) {
small = s[i];
}
i++;
}
return small;
}
int main(void) {
char s[4] = "dog";
assert(smallest(s) == 'd');
printf("Passed\n");
}

Which string is the longest

My code:
What I'm trying to do is to input two strings, then return the longest one. If they're the same length then return NULL. Now, the code is just outputting gibberish and I cannot find out why. The function returns a pointer to the first character of the largest string. Then it goes through the while loop, and I'm trying to dereference the pointer and print out its value.
Note: I'm revising for an exam and we have to use only pointers and not treat strings as arrays.
#include<stdio.h>
char* string_ln(char*, char*);
int main() {
char str1[20];
char str2[20];
char* length;
scanf("%s%s", str1, str2);
length = string_ln(str1, str2);
while (length != '\0') {
printf("%c", *length);
length++;
}
}
char* string_ln(char*p1, char*p2) {
int count1 = 0;
while (*p1 != '\0') {
count1++;
p1++;
}
int count2 = 0;
while (*p2 != '\0') {
count2++;
p2++;
}
if (count1 > count2) {
return p1;
}
else if (count2 > count1) {
return p2;
}
else {
return NULL;
}
}
In writing string_ln you iterate over both strings completely to find their lengths, and then compare those numbers. This can work, but you don't actually need to do this. You only need to know which is longer. It doesn't matter how much longer the longer string is.
char *string_ln(char *str1, char *str2) {
char *iter1, *iter2;
for (iter1 = str1, iter2 = str2;
*iter1 && *iter2;
iter1++, iter2++);
if (!(*iter1 || *iter2)) {
return NULL;
}
else if (*iter1) {
return str1;
}
else {
return str2;
}
}
We simply need to iterate over both strings, until at least one hits a NULL character. Once we get to that point, we can test to see which iterator is NULL. If it's both of them, then they're the same length. If the first iterator is not NULL, then the first string is longer. Otherwise, the second string is longer.
The benefit to this approach is that we avoid unnecessary work, and make it much quicker to compare strings of very different lengths.
There are a few problems here. First, you're modifying p1 and p2 in the function, so you won't actually return a pointer to the beginning of the largest string, but to its end. One way to avoid this is to iterate over copies of p1 and p2:
char* string_ln(char*p1, char*p2)
{
char* tmp1 = p1;
int count1 = 0;
while (*tmp1 != '\0') {
count1++;
tmp1++;
}
char* tmp2 = p2;
int count2 = 0;
while (*tmp2 != '\0') {
count2++;
tmp2++;
}
if(count1>count2){
return p1;
}
else if(count2>count1){
return p2;
}
else{
return NULL;
}
}
Second, in your main, you're using the %c format string, which works for a single char, not a whole string. Since you have a string anyway, you can avoid a format string and just print it directly. Also, note that you should explicitly check for NULLs:
int main() {
char str1[20];
char str2[20];
char* longest;
scanf("%s%s", str1, str2);
longest = string_ln(str1, str2);
if (longest) {
printf(longest);
} else {
printf("They are the same length");
}
}
I think you're missing to dereference the pointer. Instead of
while(length!='\0')
you'd need
while(*length!='\0')
That said, in the called function, you're reuring pointers after the increment, i.e., the returned pointers do not point to the start of the string anymore. You need to ensure that you return pointers which points to the beginning of the string. You can change your code to
int count1 = 0;
while (p1[count1] != '\0') {
count1++;
}
int count2 = 0;
while (p2[count2] != '\0') {
count2++;
}
so that p1 and p2 does not change.
For starters the function should be declared like
char * string_ln( const char *, const char * );
because the passed strings are not being changed within the function.
You are returning from the function the already modified pointer p1 or p2 that is being changed in one of the while loops
while (*p1 != '\0') {
count1++;
p1++;
}
while (*p2 != '\0') {
count2++;
p2++;
}
So the returned pointer points to the terminating zero '\0' of a string.
Moreover in main before this while loop
length = string_ln(str1, str2);
while(length!='\0'){
printf("%c", *length);
length++;
}
you are not checking whether the pointer length is equal to NULL. As a result the program can invoke undefined behavior.
The function itself can be defined the following way using only pointers.
char * string_ln( const char *p1, const char *p2 )
{
const char *s1 = p1;
const char *s2 = p2;
while ( *s1 != '\0' && *s2 != '\0' )
{
++s1;
++s2;
}
if ( *s1 == *s2 )
{
return NULL;
}
else if ( *s1 == '\0' )
{
return ( char * )p2;
}
else
{
return ( char * )p1;
}
}
and in main you need to write
char *length = string_ln( str1, str2 );
if ( length != NULL )
{
while ( *length )
printf( "%c", *length++ );
}
Pay attention to that the return type of the function is char * instead of const char *. It is because in C there is no function overloading and the returned pointer can point to a constant string or to a non-constant string. It is a general convention in C for declaring string functions.

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

Copying Character Pointer into another Character Pointer in C Without strcpy

I'm having some issues copying the contents of one pointer to another. I'm not able to get the contents of * s to copy into * a. If I remove the bump for * a, it copies only the last character from s. Also the use of any string library functions or any array notation isn't allowed. Sorry if the formatting is poor, this is my first post. Thanks in advance for any help.
char* copy( char *s )
{
char *a = (char *) malloc(sizeof(char)*length(s));
if (s == NULL)
{
printf("ERROR: OUT OF MEMORY\n" );
return 0;
}
while( *s != '\0' )
{
*a = *s;
s++;
a++;
}
*a = '\0';
return a;
}
Never modify the value of a pointer you have allocated. If you do that, may lose track of the address and be unable to free it.
char* copy( const char *s )
{
char *a = malloc(length(s)+1);
if (a == NULL)
{
perror( "malloc failed" );
return NULL;
}
char *c = a;
while( *s != '\0' )
{
*c = *s;
s++;
c++;
}
*c = '\0';
return a;
}
Very simple test:
int main(int argc, char *argv[])
{
const char *s = "this is a test string";
char *a;
if (NULL != (a = copy(s))) {
printf("The copy is: %s\n", a);
free(a);
}
}
Results in:
The copy is: this is a test string

Resources