我在理解教授给我的一些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?我认为它会等待用户输入(给定一个空缓冲区)但它不会,它只是知道名称。 这是如何运作的?
答案 0 :(得分:4)
第二个scanf()
通话卡住了,什么也没做,因为下一个要读的字符是\n
。 scanf()
不处理“空”输入,只是保持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;
}