当我在规范模式下使用这段代码时:
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
static struct termios newt;
static struct termios oldt;
static void kb_fini(void)
{
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
}
void kb_init(void)
{
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= (tcflag_t)~(ICANON | ECHO | ISIG);
newt.c_cc[VMIN] = 1;
newt.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
atexit(kb_fini);
}
int main(void)
{
int c;
kb_init();
printf("Press q ");
c = getchar();
if (c == 'q') {
printf("q was pressedn");
}
return 0;
}
在按下q
之前,我可以阅读控制台中的"按下q"
切换到read
:
int main(void)
{
char c;
kb_init();
printf("Press q ");
read(STDIN_FILENO, &c, 1);
if (c == 'q') {
printf("q was pressedn");
}
return 0;
}
在按下q
之前,不会显示"Press q"(按下q)。
为什么会这样?
正如我在评论中所观察到的,标准I/O包知道发生了什么,并协调事情,以便在调用对标准输入(stdin
)的读取操作之前,将挂起的输出刷新为标准输出(stdout
)——至少当输出和输入是"交互式设备"(也称为终端)时是这样。请注意,同步实际上并不是由C标准强制要求的,但大多数实现都提供了它
read()
系统调用不知道或不关心标准I/O包发生了什么。它无法访问任何文件流,也无法访问这些流专用的任何数据(如缓冲输出)。因此,它无法确保在尝试读取输入之前刷新挂起的标准输出。
如果要混合使用这两种模式,请确保在使用read()
之前使用fflush(stdout);
或fflush(0);
。
混合这两种模式是否具有明确的行为?
这取决于你如何混合它们。如果使用stdout
作为输出,使用STDIN_FILENO
作为输入,那么除了默认情况下缺少同步之外,没有任何问题。如果你试图将stdout
操作与直接在STDOUT_FILENO
上的操作混合,或者将stdin
操作与直接对STDIN_FILENO
的操作混合在一起,那么你通常会受到伤害。不要这样做,因为你看重自己(或用户)的理智。在其他问题中,标准I/O库可以提前缓冲,文件描述符函数将无法查看已经读取的标准I/O。相反,在写入时,标准I/O库将缓冲,而文件描述符I/O不会。