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

标签 c getchar termios

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

#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 pressed\n");
    }
    return 0;
}

在按下 q 之前,我能够在控制台中读取“Press q”

切换到阅读:

int main(void)
{
    char c;

    kb_init();
    printf("Press q ");
    read(STDIN_FILENO, &c, 1);
    if (c == 'q') {
        printf("q was pressed\n");
    }
    return 0;
}

q 被按下之前不显示“Press q”。

这是为什么?

最佳答案

正如我在 comment 中观察到的那样, 标准 I/O 包知道发生了什么并协调事情,以便在调用标准输入 (stdin) 上的读取操作之前刷新标准输出 (stdout) 的未决输出>) — 至少当输出和输入是“交互式设备”时,也就是终端。请注意,C 标准实际上并未强制要求同步,但大多数实现都提供了它。

read() 系统调用不知道也不关心标准 I/O 包发生了什么。它无权访问任何文件流,也无权访问这些流的任何私有(private)数据(例如缓冲输出)。因此,它无法确保在尝试读取输入之前刷新挂起的标准输出。

如果您要混合使用这两种模式,请确保在使用 read() 之前 fflush(stdout);fflush(0);

Does mixing the two modes have well defined behaviour?

这取决于你如何混合它们。如果您使用 stdout 进行输出并使用 STDIN_FILENO 进行输入,那么除了默认情况下缺少同步之外没有任何问题。如果您尝试将 stdout 操作与直接在 STDOUT_FILENO 上的操作混合,或者将 stdin 操作与直接在 STDIN_FILENO 上的操作混合,总的来说,那么你就会陷入一个受伤的世界。不要尝试这样做,因为您重视自己(或您的用户)的理智。在其他问题中,标准 I/O 库可以提前缓冲,文件描述符函数将无法看到已经读取了哪些标准 I/O。相反,在写入时,标准 I/O 库将进行缓冲,而文件描述符 I/O 则不会。

关于c - read 将 stdout 从无缓冲更改为在规范模式下缓冲的行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34241929/

相关文章:

c - 为什么 scanf_s 函数没有正确接受输入?

c - 从标准输入 C 读取所有数据

C termios 和 printf 问题

c++ - 在 C 中手动调用 C++ 对象的初始化程序

c - 在 getchar 之后使用 Scanf?

c - 数组索引中的后增量和赋值

c - socket(AF_INET, SOCK_RAW, IPPROTO_UDP) 会阻止所有数据报到达正确的位置吗?

java - Tesseract 3.02.02 崩溃 JRE

非规范终端 I/O 应用程序的 Linux 终端问题

c - Netlink 套接字创建返回 NULL