Dan Brenner
Dan Brenner

Reputation: 898

C, Buffer not clearing as expected?

I'm having a bit of difficulty understanding some C code my professor has given me. The code is as follows:

#include <stdio.h>
#include <stdlib.h>

int main()
{
  char name[1000];

  printf( "What's your name? " );
  scanf( "%s", name );
  printf( "name is %s\n", name );

  scanf( "%[^\n]", name ); /* read the entire line (up to but not
                              including the '\n' at then end) */

  getchar(); /* consume the newline from the input */

  printf( "name is %s\n", name );

  return EXIT_SUCCESS;
}

The user enters a name and has it printed out twice as such:

What's your name? Dan
name is Dan
name is Dan

It confuses me how this works. The prompt is printed with printf, input is read into the buffer with scanf, and the buffer is printed with printf. However, the \n in the second printf should clear the buffer, so from where is the second scanf reading? I would think it would wait for user input (given an empty buffer) but it doesn't, it simply knows the name. How does this work?

Upvotes: 1

Views: 497

Answers (1)

jxh
jxh

Reputation: 70472

The second scanf() call jammed, and did nothing, because the next character to read was the \n. scanf() does not deal with "empty" input, and just left the name buffer unchanged. Since name was unchanged, the second printf() of the name is the same as the first printf() of the name.

If you checked the return value of scanf(), you would have noticed that the second one returned 0, meaning that it did not scan any input.

There is a danger in the way that this code uses scanf(). Since the input specifiers does not inform scanf() about how many bytes to read, the input can potentially overrun the name buffer. This can lead to undefined behavior, and in the worst case, be used in a stack smash attack. You can protect from this problem by informing scanf() not to scan too many bytes:

    scanf( "%999s", name );

    scanf( "%999[^\n]", name );

The length has to be spelled out in the format string for scanf(), there is no way to provide this information as an argument within the variable arguments. It is usually considered more reliable to use fgets() to deal with the user input, and then use sscanf() to parse it.

    /* get the name */
    char input[sizeof(name)];
    input[sizeof(input)-2] = '\n'
    if ( fgets( input, sizeof(input), stdin ) != 0 ) {
        if ( sscanf( "%s", name ) == 0 ) name[0] = '\0';
    }

    /* get the rest of the line in case it was really long */
    while ( input[sizeof(input)-2] && input[sizeof(input)-2] != '\n' ) {
        input[sizeof(input)-2] = '\n';
        if ( fgets( input, sizeof(input), stdin ) == 0 ) break;
    }

Upvotes: 4

Related Questions