带有scanf()的管道的Unix C意外行为

时间:2019-04-22 21:00:21

标签: c unix pipe cygwin scanf

自从我上一次用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结构的更复杂程序的非常简化的版本,开始表现很差。尽管没有周期,但它以某种方式开始以无休止的循环产生无限的孩子​​。因此,在规则允许的情况下,我可能需要使用更具体的使用案例来扩展问题。非常感谢。

1 个答案:

答案 0 :(得分:4)

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

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

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

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

这实际上是更复杂程序的非常简化的版本   带有fork / exec结构,开始表现很差。尽管没有   有了周期,它就以某种方式开始在一个   无休止的循环。

我不知道这种行为与您在这里提出的要求有什么联系。

  

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

那将是一个单独的问题。