scanf的缺点 [英] Disadvantages of scanf
问题描述
我想知道scanf()
的缺点.
在许多站点中,我读到使用 scanf
可能会导致缓冲区溢出.这是什么原因?scanf
还有其他缺点吗?
In many sites, I have read that using scanf
might cause buffer overflows. What is the reason for this? Are there any other drawbacks with scanf
?
推荐答案
scanf 的问题(至少)是:
The problems with scanf are (at a minimum):
- 使用
%s
从用户处获取字符串,这导致字符串可能比您的缓冲区长,从而导致溢出. - 扫描失败的可能性,将您的文件指针留在不确定的位置.
- using
%s
to get a string from the user, which leads to the possibility that the string may be longer than your buffer, causing overflow. - the possibility of a failed scan leaving your file pointer in an indeterminate location.
我非常喜欢使用 fgets
来读取整行,这样你就可以限制读取的数据量.如果您有一个 1K 的缓冲区,并且您使用 fgets
将一行读入其中,您可以通过没有终止换行符的事实来判断该行是否太长(文件的最后一行没有尽管有换行符).
I very much prefer using fgets
to read whole lines in so that you can limit the amount of data read. If you've got a 1K buffer, and you read a line into it with fgets
you can tell if the line was too long by the fact there's no terminating newline character (last line of a file without a newline notwithstanding).
然后你可以向用户抱怨,或者为行的其余部分分配更多的空间(如果有必要,继续,直到你有足够的空间).无论哪种情况,都不存在缓冲区溢出的风险.
Then you can complain to the user, or allocate more space for the rest of the line (continuously if necessary until you have enough space). In either case, there's no risk of buffer overflow.
一旦您阅读了该行,您就知道您位于下一行,因此那里没有问题.然后,您可以sscanf
将您的字符串变成您心仪的内容,而无需保存和恢复文件指针以供重新读取.
Once you've read the line in, you know that you're positioned at the next line so there's no problem there. You can then sscanf
your string to your heart's content without having to save and restore the file pointer for re-reading.
这是我经常使用的一段代码,以确保在向用户询问信息时不会出现缓冲区溢出.
Here's a snippet of code which I frequently use to ensure no buffer overflow when asking the user for information.
如有必要,可以轻松调整以使用标准输入以外的文件,您也可以让它分配自己的缓冲区(并不断增加它直到足够大),然后再将其返回给调用者(尽管调用者会然后负责释放它,当然).
It could be easily adjusted to use a file other than standard input if necessary and you could also have it allocate its own buffer (and keep increasing it until it's big enough) before giving that back to the caller (although the caller would then be responsible for freeing it, of course).
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
#define SMALL_BUFF 3
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Size zero or one cannot store enough, so don't even
// try - we need space for at least newline and terminator.
if (sz < 2)
return SMALL_BUFF;
// Output prompt.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
// Get line with buffer overrun protection.
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// Catch possibility of ` ` in the input stream.
size_t len = strlen(buff);
if (len < 1)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[len - 1] != '
') {
extra = 0;
while (((ch = getchar()) != '
') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[len - 1] = ' ';
return OK;
}
还有,它的测试驱动程序:
And, a test driver for it:
// Test program for getLine().
int main (void) {
int rc;
char buff[10];
rc = getLine ("Enter string> ", buff, sizeof(buff));
if (rc == NO_INPUT) {
// Extra NL since my system doesn't output that on EOF.
printf ("
No input
");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long [%s]
", buff);
return 1;
}
printf ("OK [%s]
", buff);
return 0;
}
最后,测试运行以显示它的实际效果:
Finally, a test run to show it in action:
$ printf "