'C deleting char in string

Now I'm using this code to delete some char in a string.

void eliminate(char *str, char ch){
    for(; *str != '\0';str++){
        if(*str == ch){
            strcpy(str, str+1);
            str--;
        }
    }
}

In the char *str there are some strings like

"sll $6, $5, 16"

After deleting "$", the string looks like this.

"sll 6, 5, 16"

But after deleting ",", the string became very strange.

"sll 6 5 6"

Is there any problem with the code above? And also, it only happens in Linux and online GDB. VS code in my window laptop eliminates the targeted char very well.



Solution 1:[1]

As pointed out in comments strcpy() is not safe when coping data with overlapping memory blocks. memmove(dst, src, len) is the alternative which uses an auxiliary buffer in case of src & dst memory overlaps.

You can simply skip the character to eliminate in a loop:

#include <stdio.h>

void drop_char (char *str, char ch) {
    if (!str) return;

    for(char* cp = str; 1 ; ) {
        if(*cp != ch)
            *str++ = *cp;
        if ('\0' == *cp++)
            break;
    }
}

int main () {
    char str [] = "sll     $6, $5, 16";
    printf ("Original   : [%s]", str);

    drop_char(str, '$');
    printf ("\nDropping $ : [%s]", str);

    drop_char(str, ',');
    printf ("\nDropping , : [%s]", str);

    printf ("\n");

    return 0;
}

Solution 2:[2]

Without using explicit pointer math, we can use two indices as we iterate over the input string. i is an index with increments on each iteration, and j which only increments when the target character is not found.

void drop_char(char *str, char ch) {
    size_t i = 0, j = 0;    

    for (; str[i]; ++i) {
        if (str[i] != ch) 
            str[j++] = str[i];
    }

    str[j] = '\0';
}

If we have the string char test[] = "hello" and call drop_char(test, 'l') the process looks like:

i = 0, j = 0
char: 'h'
'h' != 'l'
test[0] = 'h'

i = 1, j = 1
char: 'e'
'e' != 'l'
test[1] = 'e'

i = 2, j = 2
char: 'l'
'l' == 'l'

i = 3, j = 2
char: 'l'
'l' == 'l'

i = 4, j = 2
char: 'o'
'o' != 'l'
test[2] = 'o'

i = 5, j = 3
test[3] = '\0'

Solution 3:[3]

For starters using the function strcpy for overlapping strings as in this statement

strcpy(str, str+1);

invokes undefined behavior.

From the C Standard (7.23.2.3 The strcpy function)

2 The strcpy function copies the string pointed to by s2 (including the terminating null character) into the array pointed to by s1. If copying takes place between objects that overlap, the behavior is undefined.

This statement

str--;

also can invoke undefined behavior when the deleted character is the first character of the string.

Some remarks about the function declaration.

Such a function should follow the general convention of C standard string functions and return pointer to the result string.

Another general convention is that such functions do not check whether the passed pointer is equal to NULL. It is the responsibility of the user of the function to pass a valid pointer to a string.

Also if the user will pass '\0' as the character to be deleted then the function shall do nothing with the source string and just return it.

Here is a demonstration program that shows how the function can be declared and defined.

#include <stdio.h>

char * eliminate( char *s, char c )
{
    if ( c != '\0' )
    {
        char *src = s, *dsn = s;

        do
        {
            if ( *src != c )
            {
                if ( dsn != src ) *dsn = *src;
                ++dsn;
            }
        } while ( *src++ );
    }

    return s;
}

int main( void )
{
    char s[] = "sll     $6, $5, 16";

    printf( "\"%s\"\n", eliminate( s, '$') );
}

The program output is

"sll     6, 5, 16"

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 जलजनक
Solution 2 Chris
Solution 3 Vlad from Moscow