scanf("%[^\n]",line);
是一种有问题的读取行的方式。它比 gets()
更糟糕。
C语言中将行定义为:
文本流是由字符按顺序组成的行,每行包含零个或多个字符以及一个终止的换行符。最后一行是否需要换行符是由具体实现定义的。
scanf("%[^\n]", line)
使用了格式控制字符串"%[^\n]"
。它扫描所有与扫描集合^\n
匹配的字符,如果没有读取到任何字符,则该格式化说明符执行失败并且scanf()
返回,并且line
保持不变。如果至少读取到一个字符,则会读取和保存所有匹配的字符,最后加上一个null字符。
扫描集合
^\n
表示所有
不是(因为有一个
'^'
)
'\n'
的字符。
'\n'
未被读取
scanf("%[^\n]",....
未能读取到一个换行符'\n'
。该字符将保留在stdin
中,整个行都没有被读取。
缓冲区溢出
如果读取的字符数超过99个,则可能导致未定义行为(UB)。
char line[100];
scanf("%[^\n]",line); // buffer overflow possible
空行不执行操作
当行只包含"\n"
时,scanf("%[^\n]",line);
返回一个0
而不会设置line[]
- 没有附加任何null字符。这可能导致后续代码使用未初始化的line[]
而引起未定义的行为。 '\n'
仍然在stdin
中。
未检查返回值的失败
scanf("%[^\n]",line);
假定输入成功。更好的代码应该检查scanf()
的返回值。
建议
不要使用scanf()
,而是使用fgets()
来读取一个行的输入。
#define EXPECTED_INPUT_LENGTH_MAX 49
char line[EXPECTED_INPUT_LENGTH_MAX + 1 + 1 + 1];
if (fgets(line, sizeof line, stdin)) {
size_t len = strlen(line);
if (len > 0 && line[len-1] == '\n') {
line[--len] = '\0';
}
if (len > EXPECTED_INPUT_LENGTH_MAX) {
}
fgets()
方法也有其局限性。例如,它无法读取嵌入的null字符。
处理用户输入(可能具有敌对性)是具有挑战性的。
scanf()
的文档中,它被称为“扫描集”。 - Jonathan Leffler%[^\n]
没有任何意义。在 scanf 格式化语言(C 中使用的)中,它意味着您的代码已经打开了一个非常大的漏洞,容易受到溢出攻击。学习 scanf 格式化语言并不等于学习 C 语言。事实上,这样做会妨碍您学习 C 语言。 - William Pursell%s
标志。在你上面的代码中,你正在使用%[
。 - Jann Poppinga