C语言 C 中的 get() 函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4346598/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
gets() function in C
提问by newbie
I need help again! I thought it is pretty cool to use the gets()function because it is like the scanf()wherein I could get an input with whitespace. But I read in one of the threads (student info file handling) that it is not good to use because according to them, it is a devil's tool for creating buffer overflows (which I don't understand)
我又需要帮助了!我认为使用该gets()函数非常酷,因为它就像scanf()我可以获得带有空格的输入一样。但是我在其中一个线程(学生信息文件处理)中读到它不好使用,因为根据他们的说法,它是创建缓冲区溢出的魔鬼工具(我不明白)
If I use the gets()function, I could do this. ENTER YOUR NAME: Keanu Reeves.
如果我使用该gets()功能,我可以做到这一点。输入您的姓名:Keanu Reeves。
If I use the scanf(), I could only do this. ENTER YOUR NAME: Keanu
如果我使用scanf(),我只能这样做。输入你的名字:Keanu
So I heed their advice and replaced all my gets()code with fgets(). The problem is now some of my codes are not working anymore...are there any functions other than gets()and fgets()which could read the whole line and which ignores the whitespace.
所以我听从了他们的建议,并gets()用fgets(). 现在的问题是我的一些代码不再工作了......除了可以读取整行并且忽略空格之外gets(),还有其他函数吗?fgets()
回答by casablanca
it is a devil's tool for creating buffer overflows
它是创建缓冲区溢出的魔鬼工具
Because getsdoes not take a length parameter, it doesn't know how large your input buffer is. If you pass in a 10-character buffer and the user enters 100 characters -- well, you get the point.
因为gets不带长度参数,它不知道您的输入缓冲区有多大。如果您传入一个 10 个字符的缓冲区,而用户输入了 100 个字符——那么,您就明白了。
fgetsis a safer alternative to getsbecause it takes the buffer length as a parameter, so you can call it like this:
fgets是一种更安全的替代方法,gets因为它将缓冲区长度作为参数,因此您可以这样调用它:
fgets(str, 10, stdin);
and it will read in at most 9 characters.
它最多会读入 9 个字符。
the problem is now some of my codes are not working anymore
问题是现在我的一些代码不再工作了
This is possibly because fgetsalso stores the final newline (\n) character in your buffer -- if your code is not expecting this, you should remove it manually:
这可能是因为fgets还将最后一个换行符 ( \n) 字符存储在缓冲区中——如果您的代码不期望这一点,您应该手动删除它:
int len = strlen(str);
if (len > 0 && str[len-1] == '\n')
str[len-1] = 'main(argc, argv)
char *argv[];
{
char line[512];
...
gets(line);
';
回答by Darel
As other responses have noted, gets()doesn't check the buffer space. In addition to accidental overflow problems, this weakness can be used by malicious users to create all sorts of havoc.
正如其他回复所指出的那样,gets()不检查缓冲区空间。除了意外溢出问题之外,恶意用户还可以利用这个弱点来制造各种破坏。
One of the first widespread worms, released in 1988, used gets()to propogate itself throughout the internet. Here's an interesting excerpt from Expert C Programmingby Peter Van Der Linden which discusses how it worked:
1988 年发布的第一批广泛传播的蠕虫之一,用于gets()在整个互联网上传播自身。这是Peter Van Der Linden 的Expert C Programming的一段有趣的摘录,其中讨论了它是如何工作的:
The Early Bug gets() the Internet Worm
早期的错误得到()互联网蠕虫
The problems in C are not confined to just the language. Some routines in the standard library have unsafe semantics. This was dramatically demonstrated in November 1988 by the worm program that wriggled through thousands of machines on the Internet network. When the smoke had cleared and the investigations were complete, it was determined that one way the worm had propagated was through a weakness in the fingerdaemon, which accepts queries over the network about who is currently logged in. The fingerdaemon, in.fingerd, used the standard I/O routine gets().
C 中的问题不仅限于语言。标准库中的某些例程具有不安全的语义。1988 年 11 月,蠕虫程序在 Internet 网络上的数千台机器中蠕动着,戏剧性地证明了这一点。当烟雾消散并且调查完成后,确定蠕虫传播的一种方式是通过finger守护进程中的一个弱点,它接受网络上有关当前登录者的查询。finger守护进程in.fingerd使用标准 I /O 例程gets()。
The nominal task of gets()is to read in a string from a stream. The caller tells it where to put the incoming characters. But gets()does not check the buffer space; in fact, it can't check the buffer space. If the caller provides a pointer to the stack, and more input than buffer space, gets()will happily overwrite the stack. The fingerdaemon contained the code:
的名义任务gets()是从流中读取字符串。调用者告诉它把传入的字符放在哪里。但gets()不检查缓冲区空间;实际上,它无法检查缓冲区空间。如果调用者提供一个指向堆栈的指针,并且比缓冲区空间更多的输入,gets()将很乐意覆盖堆栈。该finger守护程序包含的代码:
gets(line);
Here, lineis a 512-byte array allocated automatically on the stack. When a user provides more input than that to the fingerdaemon, the gets()routine will keep putting it on the stack. Most architectures are vulnerable to overwriting an existing entry in the middle of the stack with something bigger, that also overwrites neighboring entries. The cost of checking each stack access for size and permission would be prohibitive in software. A knowledgeable malefactor can amend the return address in the procedure activation record on the stack by stashing the right binary patterns in the argument string. This will divert the flow of execution not back to where it came from, but to a special instruction sequence (also carefully deposited on the stack) that calls execv()to replace the running image with a shell. Voilà, you are now talking to a shell on a remote machine instead of the fingerdaemon, and you can issue commands to drag across a copy of the virus to another machine.
这里,line是一个在堆栈上自动分配的 512 字节数组。当用户提供比finger守护进程更多的输入时,gets()例程将继续将其放入堆栈。大多数架构都容易用更大的东西覆盖堆栈中间的现有条目,这也会覆盖相邻的条目。检查每个堆栈访问的大小和权限的成本在软件中是高得惊人的。一个知识渊博的罪犯可以通过在参数字符串中存储正确的二进制模式来修改堆栈上过程激活记录中的返回地址。这将把执行流程转移到一个特殊的指令序列(也小心地存放在堆栈上),它调用execv()用外壳替换正在运行的图像。瞧,您现在正在与远程机器上的 shell 而不是finger守护进程对话,您可以发出命令将病毒的副本拖到另一台机器上。
Ironically, the gets()routine is an obsolete function that provided compatibility with the very first version of the portable I/O library, and was replaced by standard I/O more than a decade ago. The manpage even strongly recommends that fgets()always be used instead. The fgets()routine sets a limit on the number of characters read, so it won't exceed the size of the buffer. The fingerdaemon was made secure with a two-line fix that replaced:
具有讽刺意味的是,该gets()例程是一个过时的函数,它提供了与便携式 I/O 库的第一个版本的兼容性,并在十多年前被标准 I/O 取代。联机帮助页甚至强烈建议fgets()始终使用它。该fgets()例程对读取的字符数设置了限制,因此它不会超过缓冲区的大小。该finger守护进程作出安全与替换两行修复:
if (fgets(line, sizeof(line), stdin) == NULL)
exit(1);
by the lines:
按行:
printf("Enter name: ");
scanf("%[^\n]s",name); //[^\n] is the trick
This swallows a limited amount of input, and thus can't be manipulated into overwriting important locations by someone running the program. However, the ANSI C Standard did not remove gets()from the language. Thus, while this particular program was made secure, the underlying defect in the C standard library was not removed.
这吞下了有限数量的输入,因此不能被运行该程序的人操纵以覆盖重要位置。但是,ANSI C 标准并未gets()从该语言中删除。因此,虽然这个特定程序是安全的,但 C 标准库中的潜在缺陷并未消除。
回答by Jonathan Leffler
You could look at this question: Safe alternative to gets(). There are a number of useful answers.
你可以看看这个问题:Safe optional togets(). 有许多有用的答案。
You should be more precise about why your code does not work with fgets(). As the answers in the other question explain, you have to deal with the newline that gets()omits.
您应该更准确地了解为什么您的代码不适用于fgets(). 正如另一个问题中的答案所解释的那样,您必须处理gets()省略的换行符。
回答by nadiah
To read all the words using scanf you can do it like this
要使用 scanf 读取所有单词,您可以这样做
Example :
例子 :
#include <stdio.h>
#define S_HELPER(X) # X
#define STRINGIZE(X) S_HELPER(X)
#define MAX_NAME_LEN 20
int flushinput(void) {
int ch;
while (((ch = getchar()) != EOF) && (ch != '\n')) /* void */;
return ch;
}
int main(void) {
char name[MAX_NAME_LEN + 1] = {0};
while (name[0] != '*') {
printf("Enter a name (* to quit): ");
fflush(stdout);
scanf("%" STRINGIZE(MAX_NAME_LEN) "[^\n]", name); /* safe gets */
if (flushinput() == EOF) break;
printf("Name: [%s]\n", name);
puts("");
}
return 0;
}
回答by pmg
You can use scanfto mimic gets. It's not pretty though.
你可以scanf用来模仿gets。不过也不是很漂亮。
scanf("%s %s\n", first_name, last_name);
You're much better off reading with fgetsand parsing (if needed) with sscanf.
您最好fgets使用sscanf.
EDITexplaining the scanf call and surrounding code.
编辑解释 scanf 调用和周围的代码。
The "%[" conversion specification of scanfaccepts a maximum field width that does not include the null terminator. So the array to hold the input must have 1 more character than read with scanf.
的“%[”转换规范scanf接受不包括空终止符的最大字段宽度。所以保存输入的数组必须比使用 scanf 读取的多 1 个字符。
To do that with only a single constant I used the STRINGIZE macro. With this macro I can use a #define'd constant both as an array size (for the variable definition) as a string (for the specifier).
为了只用一个常量来做到这一点,我使用了 STRINGIZE 宏。使用这个宏,我可以使用 #define'd 常量作为数组大小(对于变量定义)和字符串(对于说明符)。
There's one more aspect that deserves mention: the flushinput. If using gets, all data is written to memory (even when the buffer overflows) up to but not including the newline. To mimic that, the scanfreads a limited number of characters up to but not including the newline and, unlike gets, keeps the newline in the input buffer. So that newline needs to be removed and that's what flushinputdoes.
还有一个方面值得一提:flushinput. 如果使用gets,则所有数据都将写入内存(即使缓冲区溢出),直到但不包括换行符。为了模仿这一点,scanf读取有限数量的字符,最多但不包括换行符,并且与 不同的是gets,将换行符保留在输入缓冲区中。所以需要删除换行符,这就是flushinput它的作用。
The rest of the code was mainly to set up a testing environment.
剩下的代码主要是搭建测试环境。
回答by The Archetypal Paul
You can read more than one field with scanf(), so you can do:
您可以使用 阅读多个字段scanf(),因此您可以执行以下操作:
However, I think it would be better to read a string and then split it yourself since they may not have entered just a first name, or first/middle/last.
但是,我认为读取一个字符串然后自己拆分它会更好,因为他们可能没有输入名字或名字/中间/姓氏。
What are the problems you are having with fgets()?
你有什么问题fgets()?
The problem with gets()is that it returns as many characters as the user enters - you as the caller have no control over this. So you might allocate 80 characters, the user may type 100 characters and the last 20 will be written off the end of the memory you have allocated, stomping on who knows what.
问题gets()在于它返回与用户输入一样多的字符 - 作为调用者的您无法控制这一点。因此,您可能会分配 80 个字符,用户可能会键入 100 个字符,最后 20 个字符将在您分配的内存末尾被注销,踩踏谁知道什么。

