我用这个代码:
while ( scanf("%s", buf) == 1 ){
Run Code Online (Sandbox Code Playgroud)
什么是防止可能的缓冲区溢出的最佳方法,以便它可以传递随机长度的字符串?
我知道我可以通过调用例如限制输入字符串:
while ( scanf("%20s", buf) == 1 ){
Run Code Online (Sandbox Code Playgroud)
但我更愿意能够处理用户输入的任何内容.或者这不能使用scanf安全地完成,我应该使用fgets?
Jon*_*ler 57
在他们的" 编程实践"(非常值得一读)中,Kernighan和Pike讨论了这个问题,他们通过使用snprintf()
创建具有正确缓冲区大小的字符串来解决它,以便传递给scanf()
函数族.有效:
int scanner(const char *data, char *buffer, size_t buflen)
{
char format[32];
if (buflen == 0)
return 0;
snprintf(format, sizeof(format), "%%%ds", (int)(buflen-1));
return sscanf(data, format, buffer);
}
Run Code Online (Sandbox Code Playgroud)
注意,这仍然将输入限制为"缓冲区"提供的大小.如果您需要更多空间,则必须进行内存分配,或使用为您进行内存分配的非标准库函数.
注意的POSIX 2008(2013)版本scanf()
系列的功能支持的格式修改m
字符串输入(分配分配字符)( ,%s
,).%c
%[
而不是采取一个char *
参数,它需要一个char **
参数,它分配的读取值所需的空间:
char *buffer = 0;
if (sscanf(data, "%ms", &buffer) == 1)
{
printf("String is: <<%s>>\n", buffer);
free(buffer);
}
Run Code Online (Sandbox Code Playgroud)
如果sscanf()
函数无法满足所有转换规范,则%ms
在函数返回之前释放它为类似转换分配的所有内存.
Joh*_*ter 30
如果你正在使用gcc,你可以使用GNU扩展a
说明符让scanf()为你保存输入分配内存:
int main()
{
char *str = NULL;
scanf ("%as", &str);
if (str) {
printf("\"%s\"\n", str);
free(str);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编辑:正如Jonathan指出的那样,您应该查阅scanf
手册页,因为说明符可能不同(%m
),并且您可能需要在编译时启用某些定义.
大部分时间都是工作的组合fgets
和sscanf
工作.如果输入格式正确,另一件事就是编写自己的解析器.另请注意,您的第二个示例需要进行一些修改才能安全使用:
#define LENGTH 42
#define str(x) # x
#define xstr(x) str(x)
/* ... */
int nc = scanf("%"xstr(LENGTH)"[^\n]%*[^\n]", array);
Run Code Online (Sandbox Code Playgroud)
以上将输入流丢弃但不包括newline(\n
)字符.你需要添加一个getchar()
来消费它.还要检查您是否到达了流末尾:
if (!feof(stdin)) { ...
Run Code Online (Sandbox Code Playgroud)
这就是它.
直接使用scanf(3)
及其变体会带来许多问题。通常,用户和非交互式用例是根据输入行来定义的。如果没有找到足够的对象,则很少会出现更多行可以解决问题的情况,但这是 scanf 的默认模式。(如果用户不知道在第一行输入数字,第二行和第三行可能没有帮助。)
至少如果您fgets(3)
知道程序需要多少输入行,并且不会出现任何缓冲区溢出......