C,缓冲区未按预期清除?

时间:2013-10-10 19:00:47

标签: c buffer

我在理解教授给我的一些C代码时遇到了一些困难。代码如下:

#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;
}

用户输入一个名称并将其打印两次:

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

这让我感到困惑。使用printf打印提示,使用scanf将输入读入缓冲区,并使用printf打印缓冲区。但是,第二个printf中的\ n应该清除缓冲区,所以从哪里读取第二个scanf?我认为它会等待用户输入(给定一个空缓冲区)但它不会,它只是知道名称。 这是如何运作的?

1 个答案:

答案 0 :(得分:4)

第二个scanf()通话卡住了,什么也没做,因为下一个要读的字符是\nscanf()不处理“空”输入,只是保持name缓冲区不变。由于name未更改,因此名称的第二个printf()与名称的第一个printf()相同。

如果您检查了scanf()的返回值,您会注意到第二个返回0,这意味着它没有扫描任何输入。

此代码使用scanf()的方式存在危险。由于输入说明符不通知scanf()要读取的字节数,因此输入可能会超出name缓冲区。这可能导致未定义的行为,并且在最坏的情况下,可用于堆栈粉碎攻击。您可以通过通知scanf()不要扫描太多字节来防止此问题:

    scanf( "%999s", name );

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

必须在scanf()的格式字符串中拼写长度,无法将此信息作为变量参数中的参数提供。使用fgets()来处理用户输入通常被认为更可靠,然后使用sscanf()来解析它。

    /* 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;
    }