我使用此代码:
while ( scanf("%s", buf) == 1 ){
什么是防止可能的缓冲区溢出的最佳方法,以便它可以传递随机长度的字符串?
我知道我可以通过调用例如:
来限制输入字符串while ( scanf("%20s", buf) == 1 ){
但我更愿意处理用户输入的内容。 或者这不能使用scanf安全地完成,我应该使用fgets?
答案 0 :(得分:56)
在他们的书The Practice of Programming(非常值得一读)中,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);
}
注意,这仍然将输入限制为“缓冲区”提供的大小。如果你需要更多空间,那么你必须进行内存分配,或者使用为你做内存分配的非标准库函数。
请注意,scanf()
系列函数的POSIX 2008(2013)版本支持字符串输入的格式修饰符m
(赋值分配字符)%s
,{{ 1}},%c
)。它不是取%[
参数,而是采用char *
参数,并为它读取的值分配必要的空间:
char **
如果char *buffer = 0;
if (sscanf(data, "%ms", &buffer) == 1)
{
printf("String is: <<%s>>\n", buffer);
free(buffer);
}
函数无法满足所有转换规范,则在函数返回之前释放为sscanf()
分配的所有内存(如转换)。
答案 1 :(得分:29)
如果你正在使用gcc,你可以使用GNU-extension a
说明符让scanf()为你保存输入分配内存:
int main()
{
char *str = NULL;
scanf ("%as", &str);
if (str) {
printf("\"%s\"\n", str);
free(str);
}
return 0;
}
编辑:正如Jonathan指出的那样,您应该查阅scanf
手册页,因为说明符可能不同(%m
),您可能需要启用某些定义编译。
答案 2 :(得分:9)
大多数情况下,fgets
和sscanf
的组合可以胜任这项工作。如果输入格式正确,另一件事就是编写自己的解析器。另请注意,您的第二个示例需要进行一些修改才能安全使用:
#define LENGTH 42
#define str(x) # x
#define xstr(x) str(x)
/* ... */
int nc = scanf("%"xstr(LENGTH)"[^\n]%*[^\n]", array);
上面将输入流丢弃但不包括换行符(\n
)。您需要添加getchar()
才能使用此功能。还要检查您是否到达了流末尾:
if (!feof(stdin)) { ...
就是这样。
答案 3 :(得分:4)
直接使用scanf(3)
及其变体会带来许多问题。通常,用户和非交互式用例是根据输入行定义的。很少见到如果没有找到足够的对象,更多行将解决问题,但这是scanf的默认模式。 (如果用户不知道在第一行输入数字,则第二行和第三行可能无效。)
至少如果你fgets(3)
知道你的程序需要多少输入行,你就不会有任何缓冲区溢出......
答案 4 :(得分:1)
限制输入的长度肯定更容易。您可以通过使用循环接受任意长的输入,一次读取一下,根据需要为字符串重新分配空间......
但这是很多工作,所以大多数C程序员只是以任意长度切断输入。我想你已经知道了,但是使用fgets()不会允许你接受任意数量的文本 - 你仍然需要设置一个限制。
答案 5 :(得分:1)
创建一个为字符串分配所需内存的函数并不是那么多工作。 这是我前段时间写的一个小函数,我总是用它来读字符串。
它将返回读取字符串或者如果发生内存错误,则返回NULL。 但请注意,您必须释放()您的字符串,并始终检查它的返回值。
#define BUFFER 32
char *readString()
{
char *str = malloc(sizeof(char) * BUFFER), *err;
int pos;
for(pos = 0; str != NULL && (str[pos] = getchar()) != '\n'; pos++)
{
if(pos % BUFFER == BUFFER - 1)
{
if((err = realloc(str, sizeof(char) * (BUFFER + pos + 1))) == NULL)
free(str);
str = err;
}
}
if(str != NULL)
str[pos] = '\0';
return str;
}