使用scanf()的管道的Unix C意外行为

问题描述 投票:2回答:1

自从我上次用C语言编程以来已经有一段时间了,而且我在管道工作方面遇到了麻烦。 (为了清楚起见,我在Windows 7上使用Cygwin。)特别是,我需要帮助理解以下示例的行为:

/* test.c */

#include <stdio.h>
#include <unistd.h>


int main() {

    char c;
    //scanf("%c", &c); // this is problematic

    int p[2];
    pipe(p);

    int out = dup(STDOUT_FILENO);

    // from now on, implicitly read from and write on pipe
    dup2(p[0], STDIN_FILENO);
    dup2(p[1], STDOUT_FILENO);

    printf("hello");
    fflush(stdout);

    // restore stdout
    dup2(out, STDOUT_FILENO);
    // should read from pipe and write on stdout
    putchar(getchar());
    putchar(getchar());
    putchar(getchar());
}

如果我调用:

echo abcde | ./test.exe

我得到以下输出:

hel

但是,如果我取消注释scanf电话,我得到:

bcd

哪个我无法解释。这实际上是一个更复杂的程序的非常简化版本,其中fork/exec结构开始表现非常糟糕。尽管没有周期,它却以某种方式开始在无限循环中产生无限的孩子​​。因此,在规则允许的情况下,我可能需要用更具体的使用案例来扩展问题。非常感谢。

c unix pipe cygwin scanf
1个回答
4
投票

诸如scanf之类的流I / O功能通常执行缓冲以提高性能。因此,如果您在标准输入上调用scanf,那么它可能会读取比满足请求所需的更多字符,并且额外的将等待,缓冲,以便下次读取。

交换底层文件描述符不会影响以前缓冲的数据。当您随后再次读取该文件时,您将获得第一次缓冲数据,直到这些数据耗尽,然后才能从新的基础文件中获取新数据。

如果您愿意,可以在对其执行任何I / O操作之前,通过setvbuf()函数关闭流的缓冲:

int result = setvbuf(stdin, NULL, _IONBF, 0);
if (result != 0) {
    // handle error ...
}

这实际上是一个更复杂程序的非常简化版本,其中fork / exec结构开始表现非常糟糕。尽管没有周期,它却以某种方式开始在无限循环中产生无限的孩子​​。

我不明白这种行为会与你在这里提出的问题有什么关系。

因此,在规则允许的情况下,我可能需要用更具体的使用案例来扩展问题。

这将是一个单独的问题。

© www.soinside.com 2019 - 2024. All rights reserved.