Search code examples
cscanf

When I input multiple words with scanf("%s%s%s", word0, word1, word2), the first character of every word except the last word is missing


Here's my code:

#include <stdio.h>

int main() {
    char word0[4], word1[4], word2[4];
    scanf("%s%s%s", word0, word1, word2);
    printf("word0: ");
    for(int i = 0; i < 4; ++i) printf("%c", word0[i]);
    printf("\nword1: ");
    for(int i = 0; i < 4; ++i) printf("%c", word1[i]);
    printf("\nword2: ");
    for(int i = 0; i < 4; ++i) printf("%c", word2[i]);
}

Input:

zxcv abcd 1234

And it outputs:

word0: xcv
word1: bcd
word2: 1234

I changed the code from

scanf("%s%s%s", word0, word1, word2);

to

scanf("%s", word0);
scanf("%s", word1);
scanf("%s", word2);

and then I debug the code:
(For this case I was input with zxcv qwer 0235)
Screenshot Screenshot
I am totally unexpected with those result.
How can scanf() modify the variable after the line has executed?


Solution

  • You have the classic -- too-short-by-one byte issue with your arrays. Continuing from my comments. In C, a string is defined as a sequence of characters terminated by the '\0' (nul-character). This is how all functions that take a string as a parameter know where the string ends.

    To prevent your problem, you must always specify the field-width modifier when using the "%s" or "%[..]" conversion specifiers to fill an array. You must manually provide the field-width modifier as it cannot be specified with a named constant or provided as a parameter to scanf(). The field-width modifier is set to size-of-array - 1 to limit the number of characters scanf() will read to one less than the size of the array -- ensuring 1-byte remains for the nul-terminating character.

    For your arrays that would be "%3s" -- which shows the off-by-one error you have. What happens is you write beyond the end of your array overwriting the address for the first character of the next array, and so on.

    To use scanf() correctly, you must ALWAYS check the return, and if filling an array ALWAYS use the field-width modifier.

    Putting a short example together from your code, and showing a couple of ways to output, you can do:

    #include <stdio.h>
    
    #define MAXC 64   /* if you need a constant, define one (or more) */
    
    int main() {
        /* use a constant to set array size (not Magic-Numbers) */
        char word0[MAXC], word1[MAXC], word2[MAXC];
        
        /* prompt for input */
        puts ("enter 3 space separated words");
        
        /* ALWAYS use the field-width modifier filling arrays and
         * ALWAYS check the scanf return.
         */
        if (scanf("%63s%63s%63s", word0, word1, word2) != 3) {
            fputs ("error: reading words.\n", stderr);
            return 1;
        }
        putchar ('\n');                     /* tidy up with newline */
        
        fputs ("word0: ", stdout);
        for (int i = 0; word0[i]; i++) {    /* if you want to loop chars */
            putchar (word0[i]);
        }
        putchar ('\n');                     /* tidy up with newline */
        
        fputs ("word1: ", stdout);
        puts (word1);                       /* otherwise just output the word */
        
        fputs ("word2: ", stdout);
        puts (word2);
    }
    

    (Note: puts() will always append a '\n' to the string being output. If you need end-of-line control, then use fputs() which doesn't. The point being, you don't need to call the variadic printf() function when there are no conversion in your output string.)

    Example Use/Output

    Using your example:

    $./bin/3words
    enter 3 space separated words
    xzcv abcd 1234
    
    word0: xzcv
    word1: abcd
    word2: 1234
    

    Or three different sized words:

    $ ./bin/3words
    enter 3 space separated words
    My Elephant Sings
    
    word0: My
    word1: Elephant
    word2: Sings
    

    Let me know if you have further questions -- and consider reading all input with fgets() to read a line at a time and avoid the scanf() pitfalls...