我在一段代码中用于说明输入的 fgets() 函数的功能方面遇到了困难。在继续之前,我想确保我对 I/O 和流的理解是正确的,并且我不会完全偏离基础:
C 中的输入和输出没有用于处理字符串的特定可行函数。一个专门用于处理字符串的函数是"gets()"函数,它将接受超出 char 数组限制的输入来存储输入(因此,除了向后兼容性之外,它实际上都是非法的),并创建缓冲区溢出。
这带来了流的主题,据我所知,流是解释程序中I/O的模型。溪流被认为是"流水",程序使用的数据在其上传输。查看链接:(也可用作传送带)
你能解释一的概念吗?什么是流?
在 C 语言中,标准输入和输出有 3 个预定义的 ANSII 流,如果使用 Windows 或 DOS,则有 2 个额外的流
,如下所示:- 标准输入(键盘)
- 标准输出(屏幕)
- 斯特德尔(屏幕)
- 标准处理器(打印机)
- 标准端口(串行端口)
据我了解,为了使事情易于管理,可以将它们视为操作系统中存在的河流,并且程序使用 I/O 函数将数据放入其中,从中取出数据,或更改流流的方向(例如读取或写入文件需要)。永远不要考虑流的"开始"或"结束":这是由操作系统处理的。你需要关心的是水把你的数据带到哪里,这是通过使用特定的功能(如printf()
、puts()
、gets()
、fgets()
等)来调解的。
这就是我的问题开始形成的地方。现在,我有兴趣掌握fgets()
功能以及它如何与流相关联。 fgets()
使用"stdin"流(自然地),并具有内置的故障保护(见下文),该安全不会允许用户输入超过用于存储输入的数组。以下是fgets()
函数的大纲,而不是它的原型(我不明白为什么需要声明它?
char *fgets(char *str , int n , FILE *fp);
请注意 fgets 函数采用的三个参数:
p1 是存储输入的地址(一个指针,可能只是您使用的数组的名称,例如"缓冲区")
p2 是要输入的字符的最大长度(我认为这就是我的问题所在!
P3 指定输入流,在此代码中是"stdin"(什么时候会有所不同?
现在,我在下面的代码将允许您键入字符,直到您的心满意足为止。当您点击回车键时,输入将以第二个参数的长度减去 1 (MAXLEN -1) 的行的形式打印在屏幕上。当您输入没有其他文本的返回时,程序将终止。
#include <stdio.h>
#define MAXLEN 10
int main(void)
{
char buffer[MAXLEN];
puts("Enter text a line at a time: enter a blank line to exit");
while(1)
{
fgets(buffer, MAXLEN, stdin); //Read comments below. Note 'buffer' is indeed a pointer: just to array's first element.
if(buffer[0] == 'n')
{
break;
}
puts(buffer);
}
return 0;
}
现在,这是我的问题:
1)这个程序允许我输入无限的字符吗?我没有看到使fgets()
比gets()
更安全的机制,因为我存储输入的数组大小有限(在本例中为 256)。我唯一看到的事情是我的长字符串输入被解析为 MAXLEN - 1 个切片?对于停止缓冲区溢出而 gets() 没有的fgets()
,我没有看到什么? 我在fgets()
的参数中看不到存在故障安全的地方。
2) 为什么程序以 MAXLEN-1 而不是 MAXLEN 的行打印出输入?
3)fgets()
函数的第二个参数的意义是什么?当我运行该程序时,我可以键入任意数量的字符。MAXLEN 如何防止缓冲区溢出?据我猜到,当用户输入一个大的长字符串时,一旦用户点击返回,MAXLEN 就会将字符串切成 MAXLEN 大小的位/字节(两者都在这里实际工作,哈哈)并将它们发送到数组。我确定我在这里错过了一些重要的东西。
这很拗口,但我对这个非常重要的主题缺乏掌握,这使我的代码变得薄弱。
问题 1
您实际上可以键入命令行工具允许每个输入的字符数。但是,您调用 fgets() 将只处理示例中的MAXLEN
,因为您告诉他这样做。
此外,fgets() 内部没有安全检查。您给 fgets 的第二个参数是"安全"参数。尝试将调用更改为fgets(buffer, MAXLEN + 10, stdin);
,然后键入MAXLEN
多个字符。您的程序将崩溃,因为您正在访问未分配的内存。
问题2
当你调用fgets()时,它将读取MAXLEN - 1
个字符,因为最后一个被保留给字符代码