0

我在一段代码的功能上遇到了困难,该功能旨在说明用于输入的 fgets() 函数。在继续之前,我想确保我对 I/O 和流的理解是正确的,并且我并没有完全偏离基础:

C 中的输入和输出没有用于处理字符串的特定可行函数。专用于处理字符串的一个函数是“gets()”函数,它将接受超出 char 数组限制的输入来存储输入(因此对于除向后兼容性之外的所有内容都有效地非法),并创建缓冲区溢出。

这就引出了流的话题,据我所知,它是一个解释程序中 I/O 的模型。溪流被认为是“流动的水”,程序使用的数据在其上传送。查看链接:(也可作为传送带)

你能解释一下流的概念吗? 什么是流?

在 C 语言中,标准输入和输出有 3 个预定义的 ANSII 流,如果使用 windows 或 DOS,则有 2 个附加流,如下所示:

  • 标准输入(键盘)
  • 标准输出(屏幕)
  • 标准错误(屏幕)
  • stdprn(打印机)
  • 标准辅助(串行端口)

据我了解,为了使事情易于管理,可以将它们视为操作系统中存在的河流,并且程序使用 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) 这个程序是否允许我输入 UNLIMITED 个字符?我看不到fgets()比 更安全的机制gets(),因为我存储输入的数组大小有限(在这种情况下为 256)。我看到发生的唯一一件事是我的长输入字符串被解析为 MAXLEN - 1 个切片?我没有看到什么fgets()停止了gets()没有的缓冲区溢出?我在参数中看不到fgets()故障安全存在的位置。

2) 为什么程序在 MAXLEN-1 行而不是 MAXLEN 行中打印输入?

3)函数的第二个参数有什么意义fgets()?当我运行程序时,我可以输入任意数量的字符。MAXLEN 在做什么来防止缓冲区溢出?据我猜测,当用户输入一个大的长字符串时,一旦用户点击返回,MAXLEN 会将字符串切成 MAXLEN 大小的位/字节(两者实际上都在这里工作,哈哈)并将它们发送到数组。我确定我在这里遗漏了一些重要的东西。

那是满嘴的,但是我对这个非常重要的主题缺乏掌握使我的代码变弱了。

4

3 回答 3

2

问题 1

实际上,您可以键入尽可能多的字符,因为您的命令行工具允许您每次输入。但是,您调用 fgets() 只会MAXLEN在您的示例中处理,因为您告诉他这样做。

此外,fgets() 内部没有安全检查。您给 fgets 的第二个参数是“安全”参数。尝试改变你对 fgets 的调用fgets(buffer, MAXLEN + 10, stdin);,然后输入多个MAXLEN字符。您的程序将崩溃,因为您正在访问未分配的内存。

问题2

当你调用 fgets() 时,它会读取MAXLEN - 1字符,因为最后一个是保留给字符代码的\0,这通常意味着字符串的结尾

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

问题 3

如果您之前没有理解 2 个答案,您将能够自己回答这个问题。尝试使用这个值。并使用与用于缓冲区大小的值不同的值。

还有,你说

p3 指定输入流,在这段代码中是“stdin”(什么时候会有所不同?)

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

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);
于 2013-10-26T17:56:44.677 回答
1

当您调用 fgets() 时,它可以让您在stdin中输入任意数量的内容,因此所有内容都保留在 stdin 中。似乎 fgets() 采用前 9 个字符,附加一个空字符,并将其分配给buffer. 然后 puts() 显示buffer然后创建一个换行符。

关键是它在一个while循环中——代码再次循环,然后获取stdin中剩余的内容并将其输入fgets(),fgets()获取接下来的9个字符并重复。标准输入仍然有“排队”的东西。

于 2014-06-06T04:59:21.663 回答
0

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

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

可以使用fgets或输入字符串scanf;但是,没有标准函数既可以输入也可以分配内存。您需要预先分配一些内存,然后将一些字符读入该内存。


您将溪流比喻为河流并不是很好。无论您是否从中取出物品,河流都会流动,但溪流不会。一个更好的类比可能是体育场门口的一排人。

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

当你这样做fgets(buf, 20, stdin)时,就像“让接下来的 19 个人进来,但如果你在此期间遇到一个矮个子,让他通过,但不要让其他任何人通过”。然后该函数通过将字符串结束标记放在末尾来fgets创建一个字符串。并且该字符串被放置在.019buf

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

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

如果少于 19 人且没有矮人,则fgets等待更多人到达。在标准 C 中,没有办法检查人们是否在等待而不阻塞等待他们,如果他们不是。

默认情况下,C 流是行缓冲的。以我的比喻,这就像在大门之前有一个“预先检查”的大门,所有到达的人都进入一个围栏,直到一个矮个子到达;然后把持笔的人,加上那个矮个子,都被送到了正门。这可以使用关闭setvbuf

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

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

流可能会结束。当一个流结束时,fgets将返回NULL. 你的程序必须处理这个问题。在我的比喻中,大门是关闭的。

于 2014-06-06T05:42:01.747 回答