C program to delete each character in s1 that matches any character in s2 Works fine. Not looking for any specific input.

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#define MAX_SIZE 10000


void delete_str(char *s1, char *s2){
    for(int i = 0; *(s1+i) != '\0'; ++i){
        for(int j = 0; *(s2+j) != '\0'; ++j){
            if(*(s1+i) == *(s2+j)){
                for(int n = i; *(s1+n) != '\0'; ++n){
                    *(s1+n) = *(s1+n+1);
                }
                --i;
                break;
            }
        }
    }
}

int main(void){
    char s1[MAX_SIZE];
    char s2[MAX_SIZE];

    puts("Enter s1");
    if(fgets(s1, sizeof(s1), stdin) == NULL){
        puts("Error reading s1");
        exit(EXIT_FAILURE);
    }

    puts("Enter s2");
    if(fgets(s2, sizeof(s2), stdin) == NULL){
        puts("Error reading s2");
        exit(EXIT_FAILURE);
    }

    delete_str(s1, s2);

    printf("%s", s1);

    return 0;
}
This compiles cleanly with no warnings, so a positive there.

You may find it easier to use command-line input rather than fgets() for the following reasons:


You don't need to decide on a maximum input size in advance
You can have any character (including newline) in your strings


On the flip side, you do have to be able to write the strings as single arguments; you'll have to be comfortable with your shell's quoting and character escapes.

I would re-write the main() like this:

int main(int argc, char **argv)
{
    if (argc != 3) {
        fprintf(stderr, "Usage: %s string chars-to-delete\n", argv[0]);
        return EXIT_FAILURE;
    }

    delete_str(argv[1], argv[2]);
    puts(argv[1]);
}


Two other things I changed:


Error message should go to stderr.  Your use of EXIT_FAILURE is perfectly right, and I've kept that (though I find plain return simpler than exit(), and in C++ it may be better to ensure destructors get called).
We don't necessarily have a terminating newline in the string (as we did with fgets()), so we can use puts() instead of printf("%s", s1) - but well done for avoiding the trap of printf(s1) which would be wrong and dangerous.




Turning now to delete_str itself, you might want to change s2 from char* to const char*, as we're not modifying the second argument.  That helps clarify which argument is which, too.

The first thing I note is that the array indexing *(s1+i) is usually written s1[i], and doing so will make your intent clearer to other C programmers:

void delete_str(char *s1, const char *s2)
{
    for (int i = 0;  s1[i] != '\0';  ++i) {
        for (int j = 0;  s2[j] != '\0';  ++j) {
            if (s1[i] == s2[j]) {
                for (int n = i;  s1[n] != '\0';  ++n) {
                    s1[n] = s1[n+1];
                }
                --i;
                break;
            }
        }
    }
}


One thing that will slow your program down is that each time you delete any of the matched characters, you copy the whole of the rest of s1 to close up the gap.  Instead of doing that, you can keep track of two positions: the next character to be read and the next character to be written.  These will start off at the beginning of s1, but each time a character is skipped, the read position will advance, and the write will not.  I'll call these src and dest, and I'll make them pointers (rather than indexes into s1).  We get to replace the inner (n) loop:

void delete_str(char *s1, const char *s2)
{
    char *dest = s1;
    for (const char *src = s1;  *src != '\0';  ++src) {
        *dest++ = *src;
        for (int j = 0;  s2[j] != '\0';  ++j) {
            if (*src == s2[j]) {
                --dest;
                break;
            }
        }
    }
    *dest = '\0';
}




We can also lean on the standard library.  There's strchr(const char *s, int c), which will return a pointer into s if it contains c, or a null pointer if not.  So we can test if *src is in s2 with a simple call (replacing the j loop):

#include &lt;string.h&gt;

void delete_str(char *s1, const char *s2)
{
    char *dest = s1;
    for (const char *src = s1;  *src != '\0';  ++src) {
        if (strchr(s2, *src) == NULL)
            *dest++ = *src;
    }
    *dest = '\0';
}


We can do better: the standard library also contains strspn and strcspn which tell us how many characters to skip and how many to copy.  We can use that to copy whole chunks of input, like this:

void delete_str(char *s1, const char *s2)
{
    char *dest = s1;
    for (const char *src = s1;  *src;  src += strspn(src, s2)) {
        int n = strcspn(src, s2);
        memmove(dest, src, n);
        dest += n;
        src += n;
    }
    *dest = '\0';
}


I note also that we never use s1 again, so we could just use that in place of dest.



My version

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;

void delete_str(char *s1, const char *s2)
{
    for (const char *src = s1;  *src;  src += strspn(src, s2)) {
        int n = strcspn(src, s2);
        memmove(s1, src, n);
        s1 += n;
        src += n;
    }
    *s1 = '\0';
}

int main(int argc, char **argv)
{
    if (argc != 3) {
        fprintf(stderr, "Usage: %s string chars-to-delete\n", argv[0]);
        return EXIT_FAILURE;
    }

    delete_str(argv[1], argv[2]);
    puts(argv[1]);
}




It is possible (and perhaps faster) to use a table-based approach.  Create an array of booleans (i.e. any integer type, but probably uint_fast8_t from &lt;stdint.h&gt; for speed, or char if size is most important), and toggle each position that's mentioned in s2 (remember to convert to unsigned, as plain char may or may not be a signed type).  You might want to benchmark to see which is faster, and for which inputs:

#include &lt;limits.h&gt;
#include &lt;stdint.h&gt;
#include &lt;string.h&gt;

void delete_str(char *s1, const char *s2)
{
    typedef uint_fast8_t flag_type;
    flag_type allowed[1 &lt;&lt; CHAR_BIT];
    memset(allowed, 1, sizeof allowed);
    while (*s2)
        allowed[(unsigned char)*s2++] = 0;

    for (const char *src = s1;  *src;  ++src) {
        if (allowed[(unsigned char)*src])
            *s1++ = *src;
    }
    *s1 = '\0';
}