试图了解我的代码的行为。我期待Ctrl-D导致程序打印数组并退出,但是需要3次按下,然后在第二次按下后进入while循环。
.box
width: 100px
height: 100px
background: gray
margin: 50px auto 0 auto
border-color: #ff0000 #0000ff
我在这里阅读了这篇文章,但我并不十分理解。 How to end scanf by entering only one EOF
阅读完之后,我希望第一个Ctrl-D清除缓冲区,然后我期待c = getchar()获取第二个Ctrl-D并跳出。相反,第二个Ctrl-D进入循环并打印p和q,然后需要第三个Ctrl-D才能退出。
由于下面的代码在第一个Ctrl-D -
上消失,这更令人困惑#include <stdio.h>
#include <stdlib.h>
void unyon(int p, int q);
int connected(int p, int q);
int main(int argc, char *argv[]) {
int c, p, q, i, size, *ptr;
scanf("%d", &size);
ptr = malloc(size * sizeof(int));
while((c = getchar()) != EOF){
scanf("%d", &p);
scanf("%d", &q);
printf("p = %d, q = %d\n", p, q);
}
for(i = 0; i < size; ++i)
printf("%d\n", *ptr + i);
free(ptr);
return 0;
}
答案 0 :(得分:1)
如果通过调试器运行它,您将获得更清晰的图像。这是一系列事件。
scanf("%d", &size);
被召唤。ENTER
。这里的关键是scanf
不会消耗\n
产生的ENTER
。getchar
被调用。这会消耗\n
。scanf("%d", &p);
被调用。这消耗了第一个ctrl-D。如果检查了返回值,那么很明显会发生错误。scanf("%d", &q);
被调用。这消耗了第二个ctrl-D。getchar
。然后第三个ctrl-D导致EOF
返回getchar
,因此循环在那时突然出现。我将把它留作练习,以解释为什么第二个程序按预期运行。
答案 1 :(得分:0)
这里有不同的东西搞乱。
首先,当您在输入终端输入Ctrl-D
时, tty 驱动程序正在处理您的输入,在缓冲区中添加每个字符并处理特殊字符。其中一个特殊字符(Ctrl-D
)表示接受最后一个字符并使它们全部可用于系统。这使得有两件事情发生:首先,从数据流中消除Ctrl-D
字符;第二,到目前为止输入的所有字符都由进程系统调用提供为read(2)
。 getchar()
是一个缓冲的库调用,可以避免每个字符读取一次,允许在缓冲区中存储以前读取的字符。
此处混乱的另一件事是系统在 posix 系统(以及所有unix系统)中发出文件结尾的信号。当您进行read(2)
系统调用时,返回值是读取的实际字符数(或-1
,如果失败,但这与EOF
无关,因为它将是很快解释)。 系统通过返回0
个字符来标记文件结束条件。因此,操作系统标记文件结束read(2)
返回0
个字节(如果您只点击返回键,则会使\n
出现在数据流中)。
这里弄乱的第三件事是来自getchar(3)
函数的返回值的类型。它不返回char
值。由于所有可能的字节值都可以为getchar(3)
返回,因此不可能为EOF
发送信号保留特殊值。该解决方案采用了很久很久以前(当设计getchar(3)
时,就是C语言的第一个版本)(参见Brian Kernighan和Denis Ritchie的 C编程语言 ,第一版。)是使用int
作为返回值,以便能够返回所有可能的字节值(0..255
)加上一个额外的值,称为EOF
。 EOF
依赖于实现,但通常定义为-1
(我认为即使标准现在指定它也必须定义为-1
,但不确定)
因此,让所有事情协同工作,EOF
是一个int
常量,定义为允许程序员编写while ((c = getchar()) != EOF)
。永远不会从终端获取-1
作为数据值。系统始终通过使read(2)
返回0
来标记文件结束条件。接收Ctrl-D
后的终端驱动程序只是将其从流中删除,并将数据提升到(但不包括Ctrl-J
或Ctrl-M
,换行和进位返回,相应地,它们也被解释并在数据流中输入为\n
所以,接下来的问题是:为什么通常需要两个(或更多)Ctrl-D
个字符来表示 eof ?
是的,正如我所说,只有Ctrl-D
(但不包括它)才能使内核可用,因此read(2)
的结果可能与0
第一次。但可以肯定的是,如果你按顺序两次输入Ctrl-D
字符,那么在第一个之后,两个字符之间就不会有更多的字符,从而确保read()
零字符。通常,程序处于循环中,执行多次读取
while ((n_read = read(fd, buffer, sizeof buffer)) > 0) {
/* NORMAL INPUT PROCESSING GOES HERE, for up to n_read bytes
* stored in buffer */
} /* while */
if (n_read < 0) {
/* ERROR PROCESSING GOES HERE */
} else {
/* EOF PROCESSING GOES HERE */
} /* if */
对于文件,行为是不同的,因为任何驱动程序都不会解释Ctrl-D
(它存储在磁盘文件中),所以你会得到Ctrl-D
作为普通字符(它是值为\004
)
当你读取一个文件时,通常这会读取大量完整的缓冲区,然后进行部分读取(小于缓冲区大小字节输入)和最终读取零字节,表示文件已经结束。
根据某些unices中tty驱动程序的配置, eof 字符可以更改并具有不同的均值。 return 字符和换行字符也会发生。有关此问题的详细文档,请参见termios(3)
手册页。