c语言 - fgets 在这个程序中是如何工作的,它如何与'stream'概念联系起来?



我在一段代码中用于说明输入的 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个字符,因为最后一个被保留给字符代码这通常意味着字符串的结尾

fgets() 的第二个参数不是要存储的字符数,而是缓冲区的最大容量。而且您始终必须考虑字符串终止字符

问题3

如果你之前没有回答过这两个答案,你就可以自己回答这个答案了。尝试使用此值。并使用与用于缓冲区大小的值不同的值。

另外,你说

P3 指定输入流,在此代码中是"stdin"(什么时候会有所不同?

您可以使用 fget 读取存储在计算机上的文件。这是一个例子:

char buffer[20];
FILE *stream = fopen("myfile.txt", "r"); //Open the file "myfile.txt" in readonly mode
fgets(buffer, 20, stream); //Read the 19 first characters of the file "myfile.txt"
puts(buffer);

当你调用fgets()时,它可以让你在stdin中输入尽可能多的内容,所以一切都保留在stdin中。 似乎 fgets() 取前 9 个字符,附加一个空字符,并将其分配给buffer 。 然后 puts() 显示buffer然后创建一个换行符。

关键是它在 while 循环中 - 代码再次循环,然后获取 stdin 中剩余的内容并将其输入 fgets(),后者需要接下来的 9 个字符并重复。 Stdin只是仍然有东西"排队"。

C 中的输入和输出没有用于处理字符串的特定可行函数。

有几个用于输出字符串的函数,例如 printfputs

字符串可以用fgetsscanf输入;但是没有同时输入和分配内存的标准函数。您需要预先分配一些内存,然后将一些字符读入该内存。


你把溪流比作河流并不好。无论你是否从中取出物品,河流都会流淌,但溪流不会。一个更好的类比可能是体育场门口的一排人。

C也有"行"的概念,行的末尾有一个'n'字符来标记。在我的类比中,假设换行符由一个矮个子表示。

当你这样做fgets(buf, 20, stdin)就像"让接下来的19个人进来,但如果你在此期间遇到一个矮个子,让他通过,而不是其他人"。 然后,fgets 函数通过把字符串末尾的标记放在末尾,从这些0创建一个字符串来19字符;并且该字符串放置在buf 中。

请注意,要fgets的第二个参数是缓冲区大小,而不是要读取的字符数。

当您输入字符时,就像更多的人加入队列一样。

如果少于19人,没有矮人,那么fgets等待更多的人到来。在标准 C 中,没有办法检查人们是否在等待而不阻止等待他们,如果他们没有

默认情况下,C 流是行缓冲的。在我的类比中,这就像在正门更早的地方有一个"预检"门,所有到达的人都会进入一个收容所,直到一个矮个子的人到达;然后,握笔的所有人加上那个矮个子都被送到了正门。 这可以使用 setvbuf 关闭。

永远不要考虑流的"开始"或"结束":这是由操作系统处理的。

这是您必须担心的事情。 stdin等在您输入之前已经开始 main() ,但其他流(例如,如果您想从硬盘驱动器上的文件中读取),您必须开始它们。

流可能会结束。当流结束时,fgets将返回NULL 。您的程序必须处理此问题。在我的类比中,大门是关闭的。

相关内容

  • 没有找到相关文章

最新更新