提问人:Kode1000 提问时间:4/29/2022 最后编辑:chqrlieKode1000 更新时间:4/30/2022 访问量:195
getchar() 即使在后续调用后仍会继续返回 EOF,但 read() 系统调用似乎“清除”了 stdin。这背后的原因是什么?
getchar() keeps returning EOF even after subsequent calls but read() system calls seem to "clear" the stdin. What are the reasons behind this?
问:
char buff[1];
int main() {
int c;
c = getchar();
printf("%d\n", c); //output -1
c = getchar();
printf("%d\n", c); // output -1
int res;
//here I get a prompt for input. What happened to EOF ?
while ((res = read(0, buff, 1)) > 0) {
printf("Hello\n");
}
while ((res = read(0, buff, 1)) > 0) {
printf("Hello\n");
}
return 0;
}
代码中带有注释行的最终输出是简单地键入(在 macOS 上)的结果。Ctrl-DEOF
我对 的行为有点困惑,尤其是与 相比。getchar()
read
循环内的系统调用不应该也返回吗?他们为什么提示用户?有没有发生某种清除?
read
while
EOF
stdin
考虑到在后台使用系统调用,为什么它们的行为会有所不同?难道不应该是“唯一”的,条件是共享的吗?
getchar()
read
stdin
EOF
为什么在下面的代码中,当给定输入时,两个系统调用都返回?
read
EOF
Ctrl-D
int res;
while ((res = read(0, buff, 1)) > 0) {
printf("Hello\n");
}
while ((res = read(0, buff, 1)) > 0) {
printf("Hello\n");
}
我试图找到这一切背后的逻辑。希望有人能弄清楚它到底是什么,它的真实行为方式。EOF
P.S 我正在使用 Mac OS 机器
答:
一旦文件结束指示器设置为 ,就不尝试读取。stdin
getchar()
清除文件结束指示符(例如 或其他)重新尝试阅读。clearerr()
该函数等价于 with 参数 。
getchar
getc
stdin
该函数等价于...
getc
fgetc
如果未设置流指向的输入流的文件结束指示符,并且存在下一个字符,则该函数将获取该字符作为转换为 an 的字符,并推进流的关联文件位置指示器(如果已定义)。
fgetc
unsigned char
int
read()
每次仍然尝试阅读。
注意:如果设置了文件结束指示器,则通过 、like 、 读取不会尝试读取。然而,即使设置了错误指示器,仍会发生读取尝试。FILE *
stdin
评论
char
getchar()
getchar()
unsigned char
unsigned char
unsigned char
fgetc
函数获取该字符的短语仍然有些模棱两可,因为字符不能正确描述控制字节,也不会返回多字节字符编码的单个字节。在这方面,你的措辞比路易斯的回答更精确,问题更少,他确实指定了一个范围 0..255,这是 C 标准规定的最小值,但不是唯一的可能性,他应该在许多地方使用而不是。getchar()
unsigned char
char
MacOs 是 BSD unix 系统的衍生产品。它的 stdio 实现不是来自 GNU 软件,因此它是一个不同的实现。在 EOF 上,当发出系统调用并接收 0 作为读取返回的字符数时,文件描述符被标记为错误,因此,在重置错误条件之前,它不会再次显示错误,这将产生您观察到的行为。在发出下一个调用之前在描述符上使用,一切都会好起来的。你也可以用 glib 来做到这一点,然后,你的程序将在 stdio 的任一实现中运行相同的内容(glib 与 bsd)read(2)
read(2)
clearerr(stream);
FILE *
getchar(3)
我试图找到这一切背后的逻辑。希望有人能弄清楚EOF到底是什么,以及它的真实行为方式。
EOF
只是一个常量(通常它的值为 -1),它与返回的任何可能值不同 ( 返回 0..255 区间的 an,而不是用于此目的的 char,以扩展 os 可能的字符范围,再增加一个来表示 EOF 条件,但 EOF 不是字符) 文件结束条件由 getchar 函数系列 (getchar, fgetc 等),因为文件条件的结束由返回值 0(返回的字符数为零)发出信号,该值不会映射为某个字符。因此,可能的字符数将扩展到整数,并定义一个新值,以便在达到文件结束条件时返回。这与具有 Ctrl-D 字符(ASCII EOT 或 Cntrl-D,十进制值 4)且不表示 END OF FILE 条件的文件兼容(当您从文件中读取 ASCII EOT 时,它显示为十进制值 4 的普通字符)char
getchar(3)
getchar()
int
read(2)
EOF
另一方面,unix tty 实现允许在线输入模式使用特殊字符(Ctrl-D、ASCII EOT/END OF TRANSMISSION、十进制值 4)来指示和结束流到驱动程序。这是一个特殊字符,如 ASCII CR 或 ASCII DEL(在将其提供给程序之前在输入中生成行编辑),在这种情况下,终端只是准备所有输入字符并允许应用程序读取它们(如果没有,则不读取,并且您得到了文件的末尾) 所以认为 Cntrl-D 仅在 unix tty 驱动程序中是特殊的,并且仅在它在规范中工作时才有效模式(线路输入模式)。因此,最后,只有两种方法可以在行模式下将数据输入到程序中:
- 按下 RETURN 键(这由终端映射到 ASCII CR,终端将其转换为著名字符 ASCII LF)并将 ASCII LF 字符输入到程序中
'\n'
- 按 Ctrl-D 键。这使得终端可以抓取到此刻之前输入的所有内容并将其发送到程序(不添加 Ctrl-D 本身),并且不会向输入缓冲区添加任何字符,这意味着,如果输入缓冲区为空,则不会向程序发送任何内容,并且调用有效地从缓冲区中读取零个字符。
read(2)
为了统一,在每种情况下,系统调用通常会阻塞到内核中,直到一个或多个字符可用。只有在文件末尾,它才会取消阻止并向程序返回零个字符。这应该是文件结束指示。许多程序在发出真正的 END OF FILE 信号之前会读取不完整的缓冲区(少于作为参数传递的字符数),因此,几乎每个程序都会进行另一次读取以检查这是否是不完整的读取,或者确实是文件结束指示。read(2)
最后,如果我想将 Cntrl-D 字符作为其本身输入到文件中怎么办?TTY 实现中还有另一个特殊字符,允许您对前面的特殊字符进行转义。在今天的系统中,该字符默认为 Ctrl-V,因此如果您想输入一个特殊字符(甚至 ?Ctrl-V) 您必须在它前面加上 Ctrl-V,因此在文件中输入 Ctrl-D 必须输入 Ctrl-V + Ctrl-D。
评论
EOF
EOF
范围内的 int 值,但它不会强制它为负数......在大多数实现中,它与任何系统调用的错误返回值兼容。但恕我直言,ANSI C 并没有指定为否定。这确实是一个小问题,并且定义为正值(例如 256)有点奇怪,并且可能会破坏传统软件。EOF
EOF
EOF
int
评论
FILE
clearerr(stdin)
read()
getchar()
read()
clearerr()