【发布时间】:2015-07-31 06:34:59
【问题描述】:
让我们看看这个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不是文件描述符。每个都有一个底层文件描述符,如果您直接写入它,您将看不到这种行为。 (直接写入文件描述符会绕过缓冲。) -
最大的不同是每个
FILE*都使用自己的缓冲区,彼此无关..