c-read在规范模式下将stdout从无缓冲更改为行缓冲



当我在规范模式下使用这段代码时:

#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不会。

最新更新