让我们来看看这个Hello World程序。
#include <stdio.h>
int main(int argc, char ** argv) {
printf("Hello, World!");
const char* sFile = "/dev/stdout"; // or /proc/self/fd/0
const char* sMode = "w";
FILE * output = fopen(sFile, sMode);
//fflush(stdout) /* forces `correct` order */
putc('!', output); // Use output or stdout from stdio.h
return 0;
}
使用output
文件描述符编译时,输出结果为:
!Hello, World!
使用stdio.h
提供的stdout
文件描述符进行编译,输出结果如预期:
Hello, World!!
我想当使用后者作为参数来调用putc
时,它将直接打印到stdout
中,而当使用/dev/stdout
的文件描述符时,它将打开一个管道并将内容打印到管道中。但我并不确定。这种行为更加有趣,因为它不会覆盖“Hello”单词的第一个字符,而是将自己推到已经推送字符串前面的行缓冲区的第一个位置。
从逻辑上讲,这是非常出乎意料的。
有人能解释一下这里到底发生了什么吗?
我正在使用
cc(Ubuntu 4.8.2-19ubuntu1)4.8.2
和使用gcc 4.8.2编译的3.13.0-52 Linux内核。
编辑:我对两个程序进行了
strace
,以下部分很重要:没有使用
fflush(stdout)
的情况下,output
(fopen("/dev/stdout","w"))产生:...
open("/dev/stdout", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f62f21e9000
write(3, "!", 1!) = 1
write(1, "Hello, World!", 13Hello, World!) = 13
exit_group(0) = ?
使用 fflush(stdout)
可以产生并强制执行正确的顺序:
...
open("/dev/stdout", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
write(1, "Hello, World!", 13Hello, World!) = 13
fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5ad4557000
write(3, "!", 1!) = 1
exit_group(0) = ?
stdout
(来自 stdlib.h )情况会产生:
...
write(1, "Hello, World!!", 14Hello, World!!) = 14
exit_group(0) = ?
看起来 FILE * output = fopen("/dev/stdout")
流使用的文件描述符与 stdout
不同。
同时,printf
使用 stdout
。
因此,在第三种情况下,字符串在推送到流之前被组装。
stdout
是一个FILE *
,而不是文件描述符。同样,output
也不是文件描述符。它们每个都有一个底层的文件描述符,如果你直接向其写入内容,你将看不到这种行为。(直接向文件描述符写入内容会绕过缓冲区。) - William PursellFILE*
都使用自己的缓冲区,彼此之间没有关联。 - Some programmer dude