跳转至

标准输入输出

当我们从 shell 启动一个新程序的时候,新的程序(新进程)会继承父进程(shell 进程)的三个文件描述符;他们分别是标准输入,标准输出,标准错误输出。 这三个文件描述符分别是 0, 1, 2 这三个整数。


原理

之所以这三个文件描述符的值是 0, 1, 2 是因为它们的值是在原代码里定义好的。

#include <iostream>
#include <unistd.h>
#include <cstring>

int main(int argc, char **argv)
{
    using namespace std;

    // 打印一下 stdout 和 stderr 对象的文件描述符
    cout << "std-in-file-no: " << STDIN_FILENO << endl;
    cout << "std-out-file-no: " << STDOUT_FILENO << endl;
    cout << "std-err-file-no:" << STDERR_FILENO << endl;
    return 0;
}
运行效果
/tmp/cpps/build/cpps
std-in-file-no: 0
std-out-file-no: 1
std-err-file-no:2
事实上 cout 背后用的就是 1 文件描述符,这个我们可以通过 strace 这个命令来确认。
strace /tmp/cpps/build/cpps

write(1, "std-in-file-no: 0\n", 18std-in-file-no: 0
)     = 18
write(1, "std-out-file-no: 1\n", 19std-out-file-no: 1
)    = 19
write(1, "std-err-file-no:2\n", 18std-err-file-no:2
)     = 18
exit_group(0)   
可以看到 cout 的背后就是 write 这个系统调用,它的第一个参数 1 就是标准输出,第二个参数是要输出的字符串,第三个参数是字符串的长度。


代码实践

以前我们要打印到标准输出都是用 cout ,现在我们知道原理了,自然就可以不再依赖于 cout 了。

#include <iostream>
#include <unistd.h>
#include <cstring>

int main(int argc, char **argv)
{
    // 打印 abcdef 这 6 个字符
    char buff[16] = "abcdef";

    // 这个时候计算出的 len 应该是 6
    int len = strlen(buff);

    // 打印到标准输出
    write(STDOUT_FILENO, buff, len);
}
运行效果
/tmp/cpps/build/cpps
abcdef