如果将fgets()指向stdin,它是如何等待用户输入的呢?

5

我是C语言的新手,正在尝试理解将fgets()指向stdin时发生了什么。

基本上我的问题是这样的,如果我在fgets()中指定一个文件指针,则fgets()会从该位置开始读取,直到遇到\nEOF或指定的limit-1为止。

那么,如果我将其指向stdin,它为什么会表现不同呢?换句话说,是什么让它“等待”用户输入而不是简单地找不到可读取的内容然后返回NULL呢?

谢谢您的时间。


返回 NULL 表示已到达文件结尾。不输入任何内容与文件结束不同。 - M.M
fgets总是等待。不同之处在于,磁盘上文件的等待时间很短,您几乎感觉不到它们。 - user3386109
@user3386109,那其实是不正确的。在由fmemopen返回的FILE *上,根据实现的不同,它可能不会等待。 - Daniel Jour
@DanielJour 真的吗??? - user3386109
例如,musl libc绕过操作系统处理此类文件,因此只需通过函数指针调用函数。 - Daniel Jour
2个回答

4

fgets()函数从参数流中读取数据。如果该流被绑定到设备或管道上,则会阻塞,直到从设备/管道获得输入或检测到文件结束。

stdin通常与终端绑定。从终端读取任何挂起的输入,在fgets()情况下,会一直读取,直到键入换行符或输入足够的字符。这里有一个额外的层次需要理解:终端的内核驱动程序默认执行自己的缓冲,即使键入的字符比fgets()所期望的多,输入操作也会阻塞,直到键入换行符。这些额外的字符留在终端缓冲区中。

可以使用stty系统调用(在Posix系统上)将终端配置为原始模式(而不是默认的熟悉模式)。这样做将删除设备驱动程序中的缓冲,但缓冲仍将在FILE *流中执行。只有在执行此缓冲操作之后,fgets()才能访问来自流的字符。通常,绑定到设备的流默认为行缓冲,导致流缓冲匹配设备驱动程序缓冲。如果将设备设置为原始模式并将流设置为无缓冲,则字符将在键入时对fgets()可用,并且当其读取到换行符或已读取size-1个字符时,fgets()将停止读取。

还要注意,您可以通过在unix和OS/X系统上键入Control-D以及在Windows系统上键入Control-Z Enter来输入文件结束符。


如果我理解正确的话,我正在处理一个终端缓冲区,如果之前没有填充,它将等待换行符才会被发送给fgets()进行读取,在此期间fgets()将一直处于停止状态。 感谢您提供额外的信息,这使得事情更加清晰! - Stefan
默认情况下,终端和标准输入都执行某种形式的行缓冲。例如,如果您使用 scanf("%d", &n); 读取一个整数,则终端读取并发送到流中的其余行仍然在 stdin 流缓冲区中结束。fgets() 将读取它,并且不会从终端请求任何输入,因为它已经看到了挂起的换行符。 - chqrlie

3
如果我指定一个指向某个文件的指针来使用fgets(),那么fgets()会从该位置开始读取,直到遇到\n、EOF或指定的限制-1为止。它不是从那个位置读取,而是从那个文件中读取。
那么为什么如果我指向stdin,它会表现得不同呢?是什么导致它等待用户输入而不是返回NULL呢?
它并没有表现出任何不同。fgets()函数只接受一个指向文件的指针,并且它总是从该文件中读取。我怀疑您将fgets()与其他执行其他操作的函数混淆了。fgets()函数只是用于对文件进行阻塞读取的函数。

fgets 是如何等待的呢?如果我指定 fgets 从一个空文件中读取,它将不会等待文件被填充任何内容。谢谢回答! - Stefan
@Stefan 这与不同类型设备上定义的阻塞读取语义有关。fgets函数提供了一个阻塞读取,其含义取决于设备。文件通常被定义为“快速”设备,只等待硬件操作完成。终端、管道和套接字是“慢速”设备,可以等待事件发生。 - David Schwartz
哦,我明白了!所以基本上,如果fgets()尝试从终端读取并且默认情况下没有任何内容可读取,它会停止读取吗? - Stefan
@Stefan 是的,这就是像终端、管道和套接字这样的“慢”设备在执行阻塞读取时的行为。而像文件这样的“快”设备则不会如此。fgets函数可以让你进行阻塞读取,具体意义取决于设备。 - David Schwartz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接